Blob Blame Raw


#include <tools/assistants/guidelineellipse.h>

// TnzCore includes
#include "tgl.h"


//*****************************************************************************************
//    TGuidelineEllipse implementation
//*****************************************************************************************

TGuidelineEllipse::TGuidelineEllipse(
  bool enabled,
  double magnetism,
  TAffine matrix
):
  TGuideline(enabled, magnetism),
  matrix(matrix),
  matrixInv(matrix.inv()) { }


TGuidelineEllipse::TGuidelineEllipse(
  bool enabled,
  double magnetism,
  TAffine matrix,
  TAffine matrixInv
):
  TGuideline(enabled, magnetism),
  matrix(matrix),
  matrixInv(matrixInv) { }


TTrackPoint
TGuidelineEllipse::transformPoint(const TTrackPoint &point) const
{
  TTrackPoint p = point;
  TPointD pp = matrixInv*p.position;
  double l2 = norm2(pp);
  if (l2 > TConsts::epsilon*TConsts::epsilon)
    p.position = matrix*(pp*(1.0/sqrt(l2)));
  return p;
}


bool
TGuidelineEllipse::truncateEllipse(
  TAngleRangeSet &ranges,
  const TAffine &ellipseMatrixInv,
  const TRectD &bounds )
{
  if (ranges.isEmpty()) return false;
  if (bounds.isEmpty()) { ranges.clear(); return false; }

  TPointD o  = ellipseMatrixInv*bounds.getP00();
  TPointD dx = ellipseMatrixInv.transformDirection(TPointD(bounds.getLx(), 0.0));
  TPointD dy = ellipseMatrixInv.transformDirection(TPointD(0.0, bounds.getLy()));
  double lx2 = norm2(dx);
  double ly2 = norm2(dy);
  if ( lx2 < TConsts::epsilon*TConsts::epsilon
    || ly2 < TConsts::epsilon*TConsts::epsilon )
      { ranges.clear(); return false; }
  TPointD nx = rotate90(dx)*(1.0/sqrt(lx2));
  TPointD ny = rotate90(dy)*(1.0/sqrt(ly2));

  TAngleI ax = TAngleRangeSet::fromDouble(atan(dx));
  TAngleI ay = TAngleRangeSet::fromDouble(atan(dy));

  double sign = nx*dy;
  if (fabs(sign) <= TConsts::epsilon)
    { ranges.clear(); return false; }

  if (sign < 0.0) {
    nx = -nx; ny = -ny;
    ax ^= TAngleRangeSet::half;
    ay ^= TAngleRangeSet::half;
  }

  TAngleI angles[] = {
    ax,
    ax^TAngleRangeSet::half,
    ay,
    ay^TAngleRangeSet::half };

  double heights[] = {
    o*nx,
    -((o+dx+dy)*nx),
    (o+dx)*ny,
    -((o+dy)*ny) };

  for(int i = 0; i < 4; ++i) {
    double h = heights[i];
    if (heights[i] <= TConsts::epsilon - 1.0)
      continue;
    if (h >= 1.0 - TConsts::epsilon)
      { ranges.clear(); return false; }
    TAngleI a = TAngleRangeSet::fromDouble(asin(h));
    TAngleI da = angles[i];
    ranges.subtract(da - a, (da + a)^TAngleRangeSet::half );
    if (ranges.isEmpty()) return false;
  }

  return true;
}


int
TGuidelineEllipse::calcSegmentsCount(const TAffine &ellipseMatrix, double pixelSize) {
  const TAffine &em = ellipseMatrix;
  const int min = 4, max = 1000;
  double r = sqrt(0.5*(norm2(TPointD(em.a11, em.a21)) + norm2(TPointD(em.a12, em.a22))));
  double h = 0.5*pixelSize/r;
  if (h <= TConsts::epsilon) return max;
  if (h >= 1.0 - TConsts::epsilon) return min;
  double segments = round(M_2PI/acos(1.0 - h));
  return segments <= (double)min ? min
       : segments >= (double)max ? max
       : (int)segments;
}


void
TGuidelineEllipse::draw(bool active, bool enabled) const {
  TAffine4 modelview, projection;
  glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a);
  glGetDoublev(GL_PROJECTION_MATRIX, projection.a);
  TAffine screenMatrix = (projection*modelview).get2d();
  TAffine screenMatrixInv = screenMatrix.inv();
  double pixelSize = sqrt(tglGetPixelSize2());

  const TRectD oneBox(-1.0, -1.0, 1.0, 1.0);
  TAngleRangeSet ranges(true);
  if (!truncateEllipse(ranges, matrixInv*screenMatrixInv, oneBox))
      return;

  int segments = calcSegmentsCount(matrix, pixelSize);
  double da = M_2PI/segments;
  double s = sin(da);
  double c = cos(da);

  for(TAngleRangeSet::Iterator i(ranges); i; ++i) {
    double a0 = i.d0();
    double a1 = i.d1greater();
    TPointD r(cos(a0), sin(a0));
    TPointD p0 = matrix*r;
    int cnt = (int)floor((a1 - a0)/da);
    for(int j = 0; j < cnt; ++j) {
      r = TPointD(r.x*c - r.y*s, r.y*c + r.x*s);
      TPointD p1 = matrix*r;
      drawSegment(p0, p1, pixelSize, active, enabled);
      p0 = p1;
    }
    drawSegment(p0, matrix*TPointD(cos(a1), sin(a1)), pixelSize, active, enabled);
  }
}