9a49d4
9a49d4
d5090c
#include <tools assistants="" guidelineellipse.h=""></tools>
9a49d4
9a49d4
// TnzCore includes
f278a5
#include <tgl.h></tgl.h>
f278a5
#include <tmathutil.h></tmathutil.h>
f278a5
f278a5
f278a5
f278a5
// returns a point nearest to the ellipse with center in zero
f278a5
static TPointD findNearestPoint(const TPointD &p, double Rx, double Ry) {
f278a5
  Rx = fabs(Rx);
f278a5
  Ry = fabs(Ry);
f278a5
  TPointD ep;
f278a5
  
f278a5
  if (isAlmostZero(Rx)) {
f278a5
    ep.y = p.y < -Ry ? -Ry
f278a5
         : p.y >  Ry ?  Ry : p.y;
f278a5
    return ep;
f278a5
  }
f278a5
f278a5
  if (isAlmostZero(Ry)) {
f278a5
    ep.x = p.x < -Rx ? -Rx
f278a5
         : p.x >  Rx ?  Rx : p.x;
f278a5
    return ep;
f278a5
  }
f278a5
f278a5
  double k = 1/Rx;
f278a5
  double x0 = p.x*k;
f278a5
  double y0 = p.y*k;
f278a5
  k *= Ry;
f278a5
  k *= k; // k = (Ry/Rx)^2
f278a5
  double l = k - 1;
f278a5
f278a5
  double a = l*l;
f278a5
  double b = 2*l*x0;
f278a5
  double c = x0*x0 + y0*y0*k - l*l;
f278a5
  double d = -b;
f278a5
  double e = -x0*x0;
f278a5
f278a5
  double dist = INFINITY;
f278a5
  std::complex<double> roots[4];</double>
f278a5
  int cnt = solveEquation4(roots, a, b, c, d, e);
f278a5
  for(int i = 0; i < cnt; ++i) {
f278a5
    if (!isAlmostZero(roots[i].imag()))
f278a5
      continue;
f278a5
    
f278a5
    double x = roots[i].real();
f278a5
    double y;
f278a5
    if (isAlmostZero(fabs(x) - 1)) {
f278a5
      y = 0;
f278a5
    } else
f278a5
    if (fabs(x) < 1) {
f278a5
      y = sqrt(k*(1 - x*x));
f278a5
      if (y0 < 0) y = -y;
f278a5
    } else {
f278a5
      continue;
f278a5
    }
f278a5
f278a5
    double dd = (x0-x)*(x0-x) + (y0-y)*(y0-y);
f278a5
    if (dd < dist)
f278a5
      { ep.x = x*Rx; ep.y = y*Rx; dist = dd; }
f278a5
  }
f278a5
  return ep;
f278a5
}
f278a5
9a49d4
9a49d4
9a49d4
//*****************************************************************************************
9a49d4
//    TGuidelineEllipse implementation
9a49d4
//*****************************************************************************************
9a49d4
f278a5
9a49d4
TGuidelineEllipse::TGuidelineEllipse(
9a49d4
  bool enabled,
9a49d4
  double magnetism,
f278a5
  const TAffine &matrix,
f278a5
  const TAffine &matrixInv,
f278a5
  double Rx,
f278a5
  double Ry
9a49d4
):
9a49d4
  TGuideline(enabled, magnetism),
9a49d4
  matrix(matrix),
f278a5
  matrixInv(matrixInv),
f278a5
  Rx(Rx),
f278a5
  Ry(Ry)
f278a5
  { }
9a49d4
9a49d4
9a49d4
TGuidelineEllipse::TGuidelineEllipse(
9a49d4
  bool enabled,
9a49d4
  double magnetism,
f278a5
  const TAffine &matrix,
f278a5
  const TAffine &matrixInv
9a49d4
):
f278a5
  TGuidelineEllipse(
f278a5
    enabled, magnetism, matrix, matrixInv,
f278a5
    sqrt(matrix.a11*matrix.a11 + matrix.a21*matrix.a21),
f278a5
    sqrt(matrix.a12*matrix.a12 + matrix.a22*matrix.a22) )
f278a5
  { }
f278a5
f278a5
f278a5
TGuidelineEllipse::TGuidelineEllipse(
f278a5
  bool enabled,
f278a5
  double magnetism,
f278a5
  const TAffine &matrix
f278a5
):
f278a5
  TGuidelineEllipse(enabled, magnetism, matrix, matrix.inv())
f278a5
  { }
f278a5
9a49d4
9a49d4
9a49d4
TTrackPoint
9a49d4
TGuidelineEllipse::transformPoint(const TTrackPoint &point) const
9a49d4
{
9a49d4
  TTrackPoint p = point;
9a49d4
  TPointD pp = matrixInv*p.position;
9a49d4
  double l2 = norm2(pp);
9a49d4
  if (l2 > TConsts::epsilon*TConsts::epsilon)
9a49d4
    p.position = matrix*(pp*(1.0/sqrt(l2)));
9a49d4
  return p;
9a49d4
}
9a49d4
9a49d4
f278a5
TPointD
f278a5
TGuidelineEllipse::nearestPoint(const TPointD &point) const
f278a5
{
f278a5
  TPointD p = matrixInv*point;
f278a5
  p = findNearestPoint(TPointD(p.x*Rx, p.y*Ry), Rx, Ry);
f278a5
  if (!isAlmostZero(Rx)) p.x /= Rx;
f278a5
  if (!isAlmostZero(Ry)) p.y /= Ry;
f278a5
  return matrix*p;
f278a5
}
f278a5
f278a5
f278a5
9a49d4
bool
9a49d4
TGuidelineEllipse::truncateEllipse(
9a49d4
  TAngleRangeSet &ranges,
9a49d4
  const TAffine &ellipseMatrixInv,
9a49d4
  const TRectD &bounds )
9a49d4
{
9a49d4
  if (ranges.isEmpty()) return false;
9a49d4
  if (bounds.isEmpty()) { ranges.clear(); return false; }
9a49d4
9a49d4
  TPointD o  = ellipseMatrixInv*bounds.getP00();
9a49d4
  TPointD dx = ellipseMatrixInv.transformDirection(TPointD(bounds.getLx(), 0.0));
9a49d4
  TPointD dy = ellipseMatrixInv.transformDirection(TPointD(0.0, bounds.getLy()));
9a49d4
  double lx2 = norm2(dx);
9a49d4
  double ly2 = norm2(dy);
9a49d4
  if ( lx2 < TConsts::epsilon*TConsts::epsilon
9a49d4
    || ly2 < TConsts::epsilon*TConsts::epsilon )
9a49d4
      { ranges.clear(); return false; }
9a49d4
  TPointD nx = rotate90(dx)*(1.0/sqrt(lx2));
9a49d4
  TPointD ny = rotate90(dy)*(1.0/sqrt(ly2));
9a49d4
9a49d4
  TAngleI ax = TAngleRangeSet::fromDouble(atan(dx));
9a49d4
  TAngleI ay = TAngleRangeSet::fromDouble(atan(dy));
9a49d4
9a49d4
  double sign = nx*dy;
9a49d4
  if (fabs(sign) <= TConsts::epsilon)
9a49d4
    { ranges.clear(); return false; }
9a49d4
9a49d4
  if (sign < 0.0) {
9a49d4
    nx = -nx; ny = -ny;
9a49d4
    ax ^= TAngleRangeSet::half;
9a49d4
    ay ^= TAngleRangeSet::half;
9a49d4
  }
9a49d4
9a49d4
  TAngleI angles[] = {
9a49d4
    ax,
9a49d4
    ax^TAngleRangeSet::half,
9a49d4
    ay,
9a49d4
    ay^TAngleRangeSet::half };
9a49d4
9a49d4
  double heights[] = {
9a49d4
    o*nx,
9a49d4
    -((o+dx+dy)*nx),
9a49d4
    (o+dx)*ny,
9a49d4
    -((o+dy)*ny) };
9a49d4
9a49d4
  for(int i = 0; i < 4; ++i) {
9a49d4
    double h = heights[i];
9a49d4
    if (heights[i] <= TConsts::epsilon - 1.0)
9a49d4
      continue;
9a49d4
    if (h >= 1.0 - TConsts::epsilon)
9a49d4
      { ranges.clear(); return false; }
9a49d4
    TAngleI a = TAngleRangeSet::fromDouble(asin(h));
9a49d4
    TAngleI da = angles[i];
9a49d4
    ranges.subtract(da - a, (da + a)^TAngleRangeSet::half );
9a49d4
    if (ranges.isEmpty()) return false;
9a49d4
  }
9a49d4
9a49d4
  return true;
9a49d4
}
9a49d4
9a49d4
9a49d4
int
9a49d4
TGuidelineEllipse::calcSegmentsCount(const TAffine &ellipseMatrix, double pixelSize) {
9a49d4
  const TAffine &em = ellipseMatrix;
9a49d4
  const int min = 4, max = 1000;
9a49d4
  double r = sqrt(0.5*(norm2(TPointD(em.a11, em.a21)) + norm2(TPointD(em.a12, em.a22))));
9a49d4
  double h = 0.5*pixelSize/r;
9a49d4
  if (h <= TConsts::epsilon) return max;
9a49d4
  if (h >= 1.0 - TConsts::epsilon) return min;
9a49d4
  double segments = round(M_2PI/acos(1.0 - h));
9a49d4
  return segments <= (double)min ? min
9a49d4
       : segments >= (double)max ? max
9a49d4
       : (int)segments;
9a49d4
}
9a49d4
9a49d4
9a49d4
void
9a49d4
TGuidelineEllipse::draw(bool active, bool enabled) const {
9a49d4
  TAffine4 modelview, projection;
9a49d4
  glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a);
9a49d4
  glGetDoublev(GL_PROJECTION_MATRIX, projection.a);
9a49d4
  TAffine screenMatrix = (projection*modelview).get2d();
9a49d4
  TAffine screenMatrixInv = screenMatrix.inv();
9a49d4
  double pixelSize = sqrt(tglGetPixelSize2());
9a49d4
9a49d4
  const TRectD oneBox(-1.0, -1.0, 1.0, 1.0);
9a49d4
  TAngleRangeSet ranges(true);
9a49d4
  if (!truncateEllipse(ranges, matrixInv*screenMatrixInv, oneBox))
9a49d4
      return;
9a49d4
9a49d4
  int segments = calcSegmentsCount(matrix, pixelSize);
9a49d4
  double da = M_2PI/segments;
9a49d4
  double s = sin(da);
9a49d4
  double c = cos(da);
9a49d4
9a49d4
  for(TAngleRangeSet::Iterator i(ranges); i; ++i) {
9a49d4
    double a0 = i.d0();
9a49d4
    double a1 = i.d1greater();
9a49d4
    TPointD r(cos(a0), sin(a0));
9a49d4
    TPointD p0 = matrix*r;
9a49d4
    int cnt = (int)floor((a1 - a0)/da);
9a49d4
    for(int j = 0; j < cnt; ++j) {
9a49d4
      r = TPointD(r.x*c - r.y*s, r.y*c + r.x*s);
9a49d4
      TPointD p1 = matrix*r;
9a49d4
      drawSegment(p0, p1, pixelSize, active, enabled);
9a49d4
      p0 = p1;
9a49d4
    }
9a49d4
    drawSegment(p0, matrix*TPointD(cos(a1), sin(a1)), pixelSize, active, enabled);
9a49d4
  }
9a49d4
}
9a49d4