Blame onefile/ellipse.cpp

62033a
62033a
62033a
#include <ctime></ctime>
62033a
#include <cstdlib></cstdlib>
62033a
#include <cassert></cassert>
62033a
62033a
#include <complex></complex>
62033a
62033a
#include <helianthus.h></helianthus.h>
62033a
62033a
62033a
62033a
typedef double Real;
62033a
typedef std::complex<real> Complex;</real>
62033a
62033a
62033a
const Real epsilon = 1e-4;
62033a
const Real epsilonSqr = epsilon*epsilon;
62033a
62033a
inline Real magSqr(Complex x)
62033a
  { return x.real()*x.real() + x.imag()*x.imag(); }
62033a
inline bool isZero(Real x)
62033a
  { return fabs(x) < epsilon; }
62033a
inline bool isZero(Complex x)
62033a
  { return magSqr(x) <= epsilonSqr; }
62033a
62033a
62033a
inline Complex verify2(Complex r, Real a, Real b, Real c) {
62033a
  assert(isZero(a*r*r + b*r + c));
62033a
  return r;
62033a
}
62033a
62033a
62033a
inline Complex verify3(Complex r, Real a, Real b, Real c, Real d) {
62033a
  assert(isZero(a*r*r*r + b*r*r + c*r + d));
62033a
  return r;
62033a
}
62033a
62033a
62033a
inline Complex verify4(Complex r, Real a, Real b, Real c, Real d, Real e) {
62033a
  assert(isZero(a*r*r*r*r + b*r*r*r + c*r*r + d*r + e));
62033a
  return r;
62033a
}
62033a
62033a
62033a
int solve2(Complex *roots, Real a, Real b, Real c) {
62033a
  if (isZero(a)) {
62033a
    if (isZero(b)) return 0;
62033a
    roots[0] = verify2(-c/b, a, b, c);
62033a
    return 1;
62033a
  }
62033a
62033a
  Real D = b*b - a*c*4;
62033a
  Real k = Real(0.5)/a;
62033a
  Complex d = D < 0 ? Complex(0, sqrt(-D)) : Complex(sqrt(D));
62033a
  roots[0] = verify2((-b - d)*k, a, b, c);
62033a
  roots[1] = verify2((-b + d)*k, a, b, c);
62033a
  return 2;
62033a
}
62033a
62033a
62033a
int solve3(Complex *roots, Real a, Real b, Real c, Real d) {
62033a
  if (isZero(a)) return solve2(roots, b, c, d);
62033a
62033a
  // x = y - b/(3*a)
62033a
  // y*y*y + p*y + q = 0
62033a
  Real p = (3*a*c - b*b)/(3*a*a);
62033a
  Real q = (27*a*a*d - 9*a*b*c + 2*b*b*b)/(27*a*a*a);
62033a
62033a
  Real Q = p*p*p/27 + q*q/4;
62033a
  Complex Qs = Q < 0 ? Complex(0, sqrt(-Q)) : Complex(sqrt(Q));
62033a
  Complex A = pow(-q/2 + Qs, Real(1)/3);
62033a
  Complex B = pow(-q/2 - Qs, Real(1)/3);
62033a
62033a
  // choose complimentary B for A (A*B must be equal -p/3)
62033a
  Complex rot(Real(-0.5), sqrt(Real(3))/2);
62033a
  if (!isZero(A*B + p/3)) B *= rot;
62033a
  if (!isZero(A*B + p/3)) B *= rot;
62033a
62033a
  Complex Y = (A - B)*Complex(0, sqrt(Real(3)));
62033a
  Complex y0 = A + B;
62033a
  Complex y1 = (-y0 - Y)/Real(2);
62033a
  Complex y2 = (-y0 + Y)/Real(2);
62033a
62033a
  Real dd = b/(3*a);
62033a
  roots[0] = verify3(y0 - dd, a, b, c, d);
62033a
  roots[1] = verify3(y1 - dd, a, b, c, d);
62033a
  roots[2] = verify3(y2 - dd, a, b, c, d);
62033a
  return 3;
62033a
}
62033a
62033a
62033a
int solve4(Complex *roots, Real a, Real b, Real c, Real d, Real e) {
62033a
  if (isZero(a)) return solve3(roots, b, c, d, e);
62033a
62033a
  //printf("%g*x^4 + %g*x^3 + %g*x^2 + %g*x + %g = 0\n", a, b, c, d, e);
62033a
62033a
  // x = y - b/(4*a)
62033a
  // y^4 + p*y^2 + q*y + r = 0
62033a
  Real dd = b/(4*a);
62033a
  Real p = (8*a*c - 3*b*b)/(8*a*a);
62033a
  Real q = (8*a*a*d - 4*a*b*c + b*b*b)/(8*a*a*a);
62033a
  Real r = (256*a*a*a*e - 64*a*a*b*d + 16*a*b*b*c - 3*b*b*b*b)/(256*a*a*a*a);
62033a
  //printf("y^4 + %g*y^2 + %g*y + %g = 0\n", p, q, r);
62033a
62033a
  if (isZero(q)) {
62033a
    // biquadratic equation
62033a
    // y^4 + p*y^2 + r = 0
62033a
    Complex y[2];
62033a
    solve2(y, 1, p, r);
62033a
    y[0] = sqrt(y[0]);
62033a
    y[1] = sqrt(y[1]);
62033a
    roots[0] = verify4(-y[0] - dd, a, b, c, d, e);
62033a
    roots[1] = verify4( y[0] - dd, a, b, c, d, e);
62033a
    roots[2] = verify4(-y[1] - dd, a, b, c, d, e);
62033a
    roots[3] = verify4( y[1] - dd, a, b, c, d, e);
62033a
    return 4;
62033a
  }
62033a
62033a
  // solve cubic equation
62033a
  // z*z*z + (p/2)*z*z + ((p*p - 4*r)/16)*z - q*q/64 = 0
62033a
  Real pp = p/2;
62033a
  Real qq = (p*p - 4*r)/16;
62033a
  Real rr = -q*q/64;
62033a
  //printf("z^3 + %g*z^2 + %g*z + %g = 0\n", pp, qq, rr);
62033a
  Complex z[3];
62033a
  solve3(z, 1, pp, qq, rr);
62033a
62033a
  z[0] = sqrt(z[0]);
62033a
  z[1] = sqrt(z[1]);
62033a
  z[2] = sqrt(z[2]);
62033a
62033a
  // we need to find signs combination where following is valid:
62033a
  // (+-z0)*(+-z1)*(+-z2) = -q/8
62033a
  Complex zzz = z[0]*z[1]*z[2];
62033a
  //printf(
62033a
  //  "sqrt(z): (%g, %g), (%g, %g), (%g, %g), zzz: (%g, %g)\n",
62033a
  //  z[0].real(), z[0].imag(),
62033a
  //  z[1].real(), z[1].imag(),
62033a
  //  z[2].real(), z[2].imag(),
62033a
  //  zzz.real(), zzz.imag() );
62033a
  assert(isZero(zzz.imag()));
62033a
  assert(isZero(fabs(zzz.real()) - fabs(q/8)));
62033a
  if ((zzz.real() > 0) == (q > 0))
62033a
    z[0] = -z[0];
62033a
  assert(isZero(z[0]*z[1]*z[2] + q/8));
62033a
62033a
  roots[0] = verify4( z[0] - z[1] - z[2] - dd, a, b, c, d, e);
62033a
  roots[1] = verify4(-z[0] + z[1] - z[2] - dd, a, b, c, d, e);
62033a
  roots[2] = verify4(-z[0] - z[1] + z[2] - dd, a, b, c, d, e);
62033a
  roots[3] = verify4( z[0] + z[1] + z[2] - dd, a, b, c, d, e);
62033a
  return 4;
62033a
}
62033a
62033a
62033a
62033a
62033a
double px, py;
62033a
double erx, ery, ex, ey;
62033a
62033a
62033a
void generate() {
62033a
  erx = randomNumber(0, 400);
62033a
  ery = randomNumber(0, 400);
62033a
}
62033a
62033a
62033a
void solve() {
62033a
  if (isZero(erx)) {
62033a
    ex = 0;
62033a
    ey = py < fabs(ery) ? -fabs(ery)
62033a
       : py > fabs(ery) ?  fabs(ery) : py;
62033a
    return;
62033a
  }
62033a
62033a
  if (isZero(ery)) {
62033a
    ey = 0;
62033a
    ex = px < fabs(erx) ? -fabs(erx)
62033a
       : px > fabs(erx) ?  fabs(erx) : px;
62033a
    return;
62033a
  }
62033a
62033a
  double k = 1/erx;
62033a
  double x0 = px*k;
62033a
  double y0 = py*k;
62033a
  k *= ery;
62033a
  k *= k;
62033a
  double l = k - 1;
62033a
62033a
  double a = l*l;
62033a
  double b = 2*l*x0;
62033a
  double c = x0*x0 + y0*y0*k - l*l;
62033a
  double d = -b;
62033a
  double e = -x0*x0;
62033a
62033a
  double dist = INFINITY;
62033a
  Complex roots[4];
62033a
  int cnt = solve4(roots, a, b, c, d, e);
62033a
  printf("%g*x^4 + %g*x^3 + %g*x^2 + %g*x + %g = 0", a, b, c, d, e);
62033a
  for(int i = 0; i < cnt; ++i) {
62033a
    printf(", (%g, %g)", roots[i].real(), roots[i].imag());
62033a
    if (!isZero(roots[i].imag())) continue;
62033a
    double x = roots[i].real();
62033a
62033a
    double y;
62033a
    if (isZero(fabs(x) - 1)) {
62033a
      y = 0;
62033a
    } else
62033a
    if (fabs(x) < 1) {
62033a
      y = sqrt(k*(1 - x*x));
62033a
      if (y0 < 0) y = -y;
62033a
    } else {
62033a
      continue;
62033a
    }
62033a
62033a
    double dd = (x0-x)*(x0-x) + (y0-y)*(y0-y);
62033a
    if (dd < dist) { ex = x*erx; ey = y*erx; dist = dd; }
62033a
    printf(", [%g, %g, %g]", x, y, dd);
62033a
  }
62033a
  printf("\n");
62033a
  printf("dist: %g\n", dist);
62033a
  assert(dist < INFINITY);
62033a
}
62033a
62033a
62033a
void init() {
62033a
  generate();
62033a
}
62033a
62033a
62033a
void draw() {
62033a
  double w = windowGetWidth();
62033a
  double h = windowGetHeight();
62033a
  saveState();
62033a
  translate(w/2, h/2);
62033a
62033a
  const double quant = 10;
62033a
  px = mouseTransformedX();
62033a
  py = mouseTransformedY();
62033a
  if (quant) {
62033a
    px = round(px/quant)*quant;
62033a
    py = round(py/quant)*quant;
62033a
  }
62033a
62033a
  if (keyWentDown("space")) generate();
62033a
  solve();
62033a
62033a
  noFill();
62033a
  line(ex, ey, px, py);
62033a
  ellipse(-erx, -ery, 2*erx, 2*ery);
62033a
62033a
  strokeWidth(3);
62033a
  point(ex, ey);
62033a
  point(px, py);
62033a
62033a
  restoreState();
62033a
}
62033a
62033a
62033a
int main() {
62033a
  windowSetVariableFrameRate();
62033a
  windowSetResizable(TRUE);
62033a
  windowSetInit(&init);
62033a
  windowSetDraw(&draw);
62033a
  windowRun();
62033a
  return 0;
62033a
}
62033a