Blame onefile/ellipse2.c

eabf4d
eabf4d
eabf4d
#include <math.h></math.h>
eabf4d
#include <stdio.h></stdio.h>
eabf4d
#include <assert.h></assert.h>
eabf4d
#include <string.h></string.h>
eabf4d
eabf4d
#include <helianthus.h></helianthus.h>
eabf4d
eabf4d
eabf4d
#define MAX_POINTS  100
eabf4d
#define POINTS_FILE "output/ellipse-points.txt"
eabf4d
#define POINTS_SIZE 10
eabf4d
#define SCALE       100
eabf4d
eabf4d
eabf4d
typedef struct {
eabf4d
  double x;
eabf4d
  double y;
eabf4d
} Point;
eabf4d
eabf4d
typedef struct {
eabf4d
  union {
eabf4d
    struct { Point p; };
eabf4d
    struct { double x, y; };
eabf4d
  };
eabf4d
  double w;
eabf4d
} Point3;
eabf4d
eabf4d
typedef struct {
eabf4d
  union {
eabf4d
    struct { double m[9]; };
eabf4d
    struct { Point3 r[3]; };
eabf4d
    struct { Point3 r0, r1, r2; };
eabf4d
    struct { Point3 dx, dy, dw; };
eabf4d
    struct { double m00, m01, m02,
eabf4d
                    m10, m11, m12,
eabf4d
                    m20, m21, m22; };
eabf4d
  };
eabf4d
} Matrix;
eabf4d
eabf4d
eabf4d
eabf4d
Point *points[MAX_POINTS];
eabf4d
int pointsCnt;
eabf4d
eabf4d
Point *current;
eabf4d
double cmx, cmy;
eabf4d
eabf4d
Point e00, e01, e10, e11;
eabf4d
Matrix em;
eabf4d
Matrix em2;
eabf4d
Matrix em3;
eabf4d
eabf4d
eabf4d
Point* vecZero(Point *v)
eabf4d
  { memset(v, 0, sizeof(*v)); return v; }
eabf4d
Point* vecSet(Point *v, double x, double y)
eabf4d
  { v->x = x; v->y = y; return v; }
eabf4d
Point* vecCopy(Point *v, const Point *a)
eabf4d
  { memcpy(v, a, sizeof(*v)); return v; }
eabf4d
eabf4d
Point3* vec3Zero(Point3 *v)
eabf4d
  { memset(v, 0, sizeof(*v)); return v; }
eabf4d
Point3* vec3Set(Point3 *v, double x, double y, double w)
eabf4d
  { v->x = x; v->y = y; v->w = w; return v; }
eabf4d
Point3* vec3Copy(Point3 *v, const Point3 *a)
eabf4d
  { memcpy(v, a, sizeof(*v)); return v; }
eabf4d
eabf4d
Matrix* matZero(Matrix *m)
eabf4d
  { memset(m, 0, sizeof(*m)); return m; }
eabf4d
Matrix* matIdentity(Matrix *m)
eabf4d
  { matZero(m); m->dx.x = m->dy.y = m->dw.w = 1; return m; }
eabf4d
Matrix* matCopy(Matrix *m, const Matrix *a)
eabf4d
  { memcpy(m, a, sizeof(*m)); return m; }
eabf4d
eabf4d
eabf4d
Point* matMulVec(Point *v, const Matrix *a, const Point *b) {
eabf4d
  Point bb; if (v == b) b = vecCopy(&bb, b);
eabf4d
  double w = a->dx.w * b->x + a->dy.w * b->y + a->dw.w;
eabf4d
  w = fabs(w) > 1e-8 ? 1/w : 0;
eabf4d
  v->x = (a->dx.x * b->x + a->dy.x * b->y + a->dw.x)*w;
eabf4d
  v->y = (a->dx.y * b->x + a->dy.y * b->y + a->dw.y)*w;
eabf4d
  return v;
eabf4d
}
eabf4d
eabf4d
Point3* matMulVec3(Point3 *v, const Matrix *a, const Point3 *b) {
eabf4d
  Point3 bb; if (v == b) b = vec3Copy(&bb, b);
eabf4d
  v->x = a->dx.x * b->x + a->dy.x * b->y + a->dw.x * b->w;
eabf4d
  v->y = a->dx.y * b->x + a->dy.y * b->y + a->dw.y * b->w;
eabf4d
  v->w = a->dx.w * b->x + a->dy.w * b->y + a->dw.w * b->w;
eabf4d
  return v;
eabf4d
}
eabf4d
eabf4d
Matrix* matMul(Matrix *m, const Matrix *a, const Matrix *b) {
eabf4d
  Matrix aa; if (m == a) a = matCopy(&aa, a);
eabf4d
  Matrix bb; if (m == b) b = matCopy(&bb, b);
eabf4d
  matMulVec3(&m->dx, a, &b->dx);
eabf4d
  matMulVec3(&m->dy, a, &b->dy);
eabf4d
  matMulVec3(&m->dw, a, &b->dw);
eabf4d
  return m;
eabf4d
}
eabf4d
eabf4d
Matrix* matInv(Matrix *m, const Matrix *a) {
eabf4d
  Matrix aa; if (m == a) a = matCopy(&aa, a);
eabf4d
  m->m00 = a->m11*a->m22 - a->m12*a->m21;
eabf4d
  m->m10 = a->m12*a->m20 - a->m10*a->m22;
eabf4d
  m->m20 = a->m10*a->m21 - a->m11*a->m20;
eabf4d
  double d = m->m00*a->m00 + m->m10*a->m01 + m->m20*a->m02;
eabf4d
  if (!(fabs(d) > 1e-8)) return matZero(m);
eabf4d
  d = 1/d;
eabf4d
  m->m00 *= d; m->m10 *= d; m->m20 *= d;
eabf4d
  m->m01 = (a->m02*a->m21 - a->m01*a->m22)*d;
eabf4d
  m->m02 = (a->m01*a->m12 - a->m02*a->m11)*d;
eabf4d
  m->m11 = (a->m00*a->m22 - a->m02*a->m20)*d;
eabf4d
  m->m12 = (a->m02*a->m10 - a->m00*a->m12)*d;
eabf4d
  m->m21 = (a->m01*a->m20 - a->m00*a->m21)*d;
eabf4d
  m->m22 = (a->m00*a->m11 - a->m01*a->m10)*d;
eabf4d
  return m;
eabf4d
}
eabf4d
eabf4d
eabf4d
int equal(double a, double b)
eabf4d
  { return fabs(a-b) < 1e-4; }
eabf4d
eabf4d
eabf4d
void fixEllipseMatrix() {
eabf4d
  matZero(&em2);
eabf4d
eabf4d
  Matrix m;
eabf4d
  matInv(&m, &em);
eabf4d
eabf4d
  double A = m.r0.x*m.r0.x + m.r0.y*m.r0.y - m.r0.w*m.r0.w;
eabf4d
  double B = m.r1.x*m.r1.x + m.r1.y*m.r1.y - m.r1.w*m.r1.w;
eabf4d
  double C = m.r2.x*m.r2.x + m.r2.y*m.r2.y - m.r2.w*m.r2.w;
eabf4d
eabf4d
  double D = m.r0.x*m.r1.x + m.r0.y*m.r1.y - m.r0.w*m.r1.w;
eabf4d
  double E = m.r0.x*m.r2.x + m.r0.y*m.r2.y - m.r0.w*m.r2.w;
eabf4d
  double F = m.r1.x*m.r2.x + m.r1.y*m.r2.y - m.r1.w*m.r2.w;
eabf4d
eabf4d
  //printf( "A: %f, B: %f, C: %f, D: %f, E: %f, F: %f\n",
eabf4d
  //         A    , B    , C    , D    , E    , F        );
eabf4d
eabf4d
  matZero(&m);
eabf4d
  if (!(fabs(A) > 1e-8))
eabf4d
    return;
eabf4d
eabf4d
  double X0 = A;
eabf4d
  double X1 = D*D/X0;
eabf4d
  double X2 = E*E/X0;
eabf4d
  double Y1 = (B - X1);
eabf4d
  double Y2 = (C - X2);
eabf4d
eabf4d
  double k = sqrt(X1*X2);
eabf4d
  if (D*E < 0) k = -k;
eabf4d
  k = F - k;
eabf4d
  k = k*k - Y1*Y2;
eabf4d
eabf4d
  if (!(fabs(k) > 1e-8))
eabf4d
    return;
eabf4d
  k = Y1/k;
eabf4d
eabf4d
  m.r0.x = sqrt(X0*k);
eabf4d
  m.r1.x = sqrt(X1*k);
eabf4d
  m.r1.y = sqrt(Y1*k);
eabf4d
  m.r2.x = sqrt(X2*k);
eabf4d
  m.r2.y = sqrt(Y2*k + 1);
eabf4d
  m.r2.w = 1;
eabf4d
eabf4d
  if (D*k < 0) m.r1.x = -m.r1.x;
eabf4d
  if (E*k < 0) m.r2.x = -m.r2.x;
eabf4d
  if (F*k < m.r1.x * m.r2.x) m.r2.y = -m.r2.y;
eabf4d
eabf4d
  //assert(equal( A*k , m.r0.x*m.r0.x + m.r0.y*m.r0.y - m.r0.w*m.r0.w ));
eabf4d
  //assert(equal( B*k , m.r1.x*m.r1.x + m.r1.y*m.r1.y - m.r1.w*m.r1.w ));
eabf4d
  //assert(equal( C*k , m.r2.x*m.r2.x + m.r2.y*m.r2.y - m.r2.w*m.r2.w ));
eabf4d
  //assert(equal( D*k , m.r0.x*m.r1.x + m.r0.y*m.r1.y - m.r0.w*m.r1.w ));
eabf4d
  //assert(equal( E*k , m.r0.x*m.r2.x + m.r0.y*m.r2.y - m.r0.w*m.r2.w ));
eabf4d
  //assert(equal( F*k , m.r1.x*m.r2.x + m.r1.y*m.r2.y - m.r1.w*m.r2.w ));
eabf4d
eabf4d
  matInv(&em2, &m);
eabf4d
}
eabf4d
eabf4d
void fixEllipseMatrix2() {
eabf4d
  matZero(&em3);
eabf4d
eabf4d
  double a = em2.m00;
eabf4d
  double b = em2.m01;
eabf4d
  double c = em2.m10;
eabf4d
  double d = em2.m11;
eabf4d
  //printf("a: %f, b: %f, c: %f, d: %f\n", a, b, c, d);
eabf4d
eabf4d
  double g = a*c + b*d;
eabf4d
  if (!(fabs(g) > 1e-8))
eabf4d
    { em3 = em2; return; }
eabf4d
  g = (a*a + b*b - c*c - d*d)/g;
eabf4d
  double G = g*g + 4;
eabf4d
  //printf("g: %f, G: %f\n", g, G);
eabf4d
eabf4d
  double D = G*G - 4*G;
eabf4d
  assert(D >= 0);
eabf4d
  D = sqrt(D);
eabf4d
  assert(G > D);
eabf4d
eabf4d
eabf4d
  double e = (G + D)/(2*G);
eabf4d
  double f = 1 - e;
eabf4d
  assert(0 <= e && e <= 1);
eabf4d
  assert(0 <= f && f <= 1);
eabf4d
eabf4d
  e = sqrt(e);
eabf4d
  f = sqrt(f);
eabf4d
  if (g < 0) f = -f;
eabf4d
eabf4d
  //printf("e: %f, f: %f\n", e, f);
eabf4d
eabf4d
  Matrix m = {};
eabf4d
  m.m00 =  e;
eabf4d
  m.m01 =  f;
eabf4d
  m.m10 = -f;
eabf4d
  m.m11 =  e;
eabf4d
  m.m22 =  1;
eabf4d
eabf4d
  matMul(&em3, &em2, &m);
eabf4d
}
eabf4d
eabf4d
eabf4d
void updateEllipseMatrix() {
eabf4d
  double A = e11.x - e01.x;
eabf4d
  double B = e11.y - e01.y;
eabf4d
  double C = e11.x - e10.x;
eabf4d
  double D = e11.y - e10.y;
eabf4d
  double E = e01.x + e10.x - e00.x - e11.x;
eabf4d
  double F = e01.y + e10.y - e00.y - e11.y;
eabf4d
  double k = A*D - B*C;
eabf4d
  if (fabs(k) < 1e-6) return;
eabf4d
  k = 1/k;
eabf4d
eabf4d
  em.dx.w = (D*E - C*F)*k;
eabf4d
  em.dy.w = (A*F - B*E)*k;
eabf4d
  em.dw.w = 1;
eabf4d
eabf4d
  em.dx.x = e01.x*(em.dx.w + 1) - e00.x;
eabf4d
  em.dx.y = e01.y*(em.dx.w + 1) - e00.y;
eabf4d
eabf4d
  em.dy.x = e10.x*(em.dy.w + 1) - e00.x;
eabf4d
  em.dy.y = e10.y*(em.dy.w + 1) - e00.y;
eabf4d
eabf4d
  em.dw.x = e00.x;
eabf4d
  em.dw.y = e00.y;
eabf4d
eabf4d
  Matrix m = {};
eabf4d
  m.dx.x = m.dy.y = 0.5;
eabf4d
  m.dw.x = m.dw.y = 0.5;
eabf4d
  m.dw.w = 1;
eabf4d
  matMul(&em, &em, &m);
eabf4d
eabf4d
  fixEllipseMatrix();
eabf4d
  fixEllipseMatrix2();
eabf4d
}
eabf4d
eabf4d
eabf4d
void drawEllipse(Matrix *m) {
eabf4d
  for(double a = 0; a < 2*PI; a += 0.05) {
eabf4d
    Point p = { cos(a), sin(a) };
eabf4d
    matMulVec(&p, m, &p);
eabf4d
    lineTo(p.x, p.y);
eabf4d
  }
eabf4d
  closePath();
eabf4d
eabf4d
  Point p[] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
eabf4d
  for(int i = 0; i < 4; ++i)
eabf4d
    matMulVec(&p[i], m, &p[i]);
eabf4d
  line(p[0].x, p[0].y, p[1].x, p[1].y);
eabf4d
  line(p[2].x, p[2].y, p[3].x, p[3].y);
eabf4d
}
eabf4d
eabf4d
eabf4d
void drawEllipseBounds() {
eabf4d
  stroke(colorByRGBA(0, 0, 0, 0.5));
eabf4d
  moveTo(e00.x, e00.y);
eabf4d
  lineTo(e01.x, e01.y);
eabf4d
  lineTo(e11.x, e11.y);
eabf4d
  lineTo(e10.x, e10.y);
eabf4d
  closePath();
eabf4d
}
eabf4d
eabf4d
eabf4d
eabf4d
void savePoints() {
eabf4d
  FILE *f = fopen(POINTS_FILE, "w");
eabf4d
  for(int i = 0; i < pointsCnt; ++i)
eabf4d
    fprintf(f, "%f %f\n", points[i]->x, points[i]->y);
eabf4d
}
eabf4d
eabf4d
void loadPoints() {
eabf4d
  FILE *f = fopen(POINTS_FILE, "r");
eabf4d
  for(int i = 0; i < pointsCnt; ++i)
eabf4d
    fscanf(f, "%lf %lf", &points[i]->x, &points[i]->y);
eabf4d
}
eabf4d
eabf4d
void init() {
eabf4d
  points[pointsCnt++] = &e00;
eabf4d
  points[pointsCnt++] = &e01;
eabf4d
  points[pointsCnt++] = &e10;
eabf4d
  points[pointsCnt++] = &e11;
eabf4d
  loadPoints();
eabf4d
}
eabf4d
eabf4d
void deinit() {
eabf4d
  savePoints();
eabf4d
}
eabf4d
eabf4d
void draw() {
eabf4d
  double w = windowGetWidth();
eabf4d
  double h = windowGetHeight();
eabf4d
  saveState();
eabf4d
  translate(w/2, h/2);
eabf4d
eabf4d
  zoom(SCALE);
eabf4d
  strokeWidth(1.0/SCALE);
eabf4d
eabf4d
  double pointRadius = POINTS_SIZE/2.0/SCALE;
eabf4d
  double pointRadius2 = pointRadius*pointRadius;
eabf4d
  double mx = mouseTransformedX();
eabf4d
  double my = mouseTransformedY();
eabf4d
eabf4d
  if (mouseWentDown("left")) {
eabf4d
    current = NULL;
eabf4d
    for(int i = pointsCnt-1; i >= 0; --i) {
eabf4d
      cmx = points[i]->x - mx;
eabf4d
      cmy = points[i]->y - my;
eabf4d
      if (cmx*cmx + cmy*cmy <= pointRadius2) {
eabf4d
        current = points[i];
eabf4d
        break;
eabf4d
      }
eabf4d
    }
eabf4d
  } else
eabf4d
  if (current && mouseDown("left")) {
eabf4d
    current->x = mx + cmx;
eabf4d
    current->y = my + cmy;
eabf4d
  } else {
eabf4d
    current = NULL;
eabf4d
  }
eabf4d
eabf4d
  noFill();
eabf4d
eabf4d
  updateEllipseMatrix();
eabf4d
eabf4d
  stroke(colorByRGBA(1, 0, 0, 0.5));
eabf4d
  drawEllipse(&em);
eabf4d
  stroke(colorByRGBA(0, 1, 0, 0.5));
eabf4d
  drawEllipse(&em2);
eabf4d
  stroke(colorByRGBA(0, 0, 1, 0.5));
eabf4d
  drawEllipse(&em3);
eabf4d
eabf4d
  stroke(colorByRGBA(0, 0, 0, 0.5));
eabf4d
  drawEllipseBounds();
eabf4d
eabf4d
  fill(colorByRGBA(0, 0, 1, 0.5));
eabf4d
  for(int i = 0; i < pointsCnt; ++i)
eabf4d
    circle(points[i]->x, points[i]->y, pointRadius);
eabf4d
eabf4d
  restoreState();
eabf4d
}
eabf4d
eabf4d
eabf4d
int main() {
eabf4d
  windowSetVariableFrameRate();
eabf4d
  windowSetResizable(TRUE);
eabf4d
  windowSetInit(&init);
eabf4d
  windowSetDeinit(&deinit);
eabf4d
  windowSetDraw(&draw);
eabf4d
  windowRun();
eabf4d
  return 0;
eabf4d
}
eabf4d