|
|
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 |
|