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