9a49d4
9a49d4
9a49d4
// TnzTools includes
9a49d4
#include <tools assistant.h=""></tools>
d5090c
#include <tools assistants="" guidelineline.h=""></tools>
d5090c
#include <tools assistants="" guidelineellipse.h=""></tools>
9a49d4
9a49d4
// TnzCore includes
9a49d4
#include <tgl.h></tgl.h>
9a49d4
9a49d4
// std includes
9a49d4
#include <limits></limits>
9a49d4
9a49d4
9a49d4
//*****************************************************************************************
9a49d4
//    TAssistantEllipse implementation
9a49d4
//*****************************************************************************************
9a49d4
f36ea4
class TAssistantEllipse final : public TAssistant {
9a49d4
  Q_DECLARE_TR_FUNCTIONS(TAssistantEllipse)
9a49d4
public:
9a49d4
  const TStringId m_idRestricktA;
9a49d4
  const TStringId m_idRestricktB;
9a49d4
  const TStringId m_idRepeat;
9a49d4
  const TStringId m_idGrid;
9a49d4
  const TStringId m_idPerspective;
9a49d4
9a49d4
protected:
9a49d4
  TAssistantPoint &m_center;
9a49d4
  TAssistantPoint &m_a;
9a49d4
  TAssistantPoint &m_b;
9a49d4
  TAssistantPoint &m_grid0;
9a49d4
  TAssistantPoint &m_grid1;
9a49d4
9a49d4
public:
9a49d4
  TAssistantEllipse(TMetaObject &object):
9a49d4
    TAssistant(object),
9a49d4
    m_idRestricktA("restrictA"),
9a49d4
    m_idRestricktB("restrictB"),
9a49d4
    m_idRepeat("repeat"),
9a49d4
    m_idGrid("grid"),
9a49d4
    m_idPerspective("perspective"),
9a49d4
    m_center( addPoint("center", TAssistantPoint::CircleCross) ),
9a49d4
    m_a( addPoint("a", TAssistantPoint::CircleFill, TPointD(100.0, 0.0)) ),
9a49d4
    m_b( addPoint("b", TAssistantPoint::Circle,     TPointD(0.0,  50.0)) ),
9a49d4
    m_grid0( addPoint("grid0",  TAssistantPoint::CircleDoubleDots, TPointD(  0.0,-50.0)) ),
9a49d4
    m_grid1( addPoint("grid1",  TAssistantPoint::CircleDots,       TPointD( 25.0,-75.0)) )
9a49d4
  {
9a49d4
    addProperty( new TBoolProperty(m_idRestricktA.str(), getRestrictA()) );
9a49d4
    addProperty( new TBoolProperty(m_idRestricktB.str(), getRestrictB()) );
9a49d4
    addProperty( new TBoolProperty(m_idRepeat.str(), getRepeat()) );
9a49d4
    addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) );
9a49d4
    addProperty( new TBoolProperty(m_idPerspective.str(), getPerspective()) );
9a49d4
  }
9a49d4
9a49d4
  static QString getLocalName()
9a49d4
    { return tr("Ellipse"); }
9a49d4
9a49d4
  void updateTranslation() const override {
096df2
    TAssistant::updateTranslation();
9a49d4
    setTranslation(m_idRestricktA, tr("Restrict A"));
9a49d4
    setTranslation(m_idRestricktB, tr("Restrict B"));
9a49d4
    setTranslation(m_idRepeat, tr("Repeat"));
9a49d4
    setTranslation(m_idGrid, tr("Grid"));
9a49d4
    setTranslation(m_idPerspective, tr("Perspective"));
9a49d4
  }
9a49d4
9a49d4
  inline bool getRestrictA() const
9a49d4
    { return data()[m_idRestricktA].getBool(); }
9a49d4
  inline bool getRestrictB() const
9a49d4
    { return data()[m_idRestricktB].getBool(); }
9a49d4
  inline bool getRepeat() const
9a49d4
    { return data()[m_idRepeat].getBool(); }
9a49d4
  inline bool getGrid() const
9a49d4
    { return data()[m_idGrid].getBool(); }
9a49d4
  inline bool getPerspective() const
9a49d4
    { return data()[m_idPerspective].getBool(); }
9a49d4
9a49d4
  void onDataChanged(const TVariant &value) override {
9a49d4
    TAssistant::onDataChanged(value);
9a49d4
    m_grid0.visible = m_grid1.visible = getGrid();
9a49d4
  }
9a49d4
9a49d4
private:
9a49d4
  void fixBAndGgid1(const TPointD &previousCenter, const TPointD &previousA) {
9a49d4
    TPointD dx = previousA - previousCenter;
9a49d4
    double l = norm2(dx);
9a49d4
    if (l <= TConsts::epsilon*TConsts::epsilon) return;
9a49d4
    dx = dx*(1.0/sqrt(l));
9a49d4
    TPointD dy(-dx.y, dx.x);
9a49d4
9a49d4
    double r2 = dy*(m_b.position - m_center.position);
9a49d4
9a49d4
    TPointD g1 = m_grid1.position - m_grid0.position;
9a49d4
    g1 = TPointD(dx*g1, dy*g1);
9a49d4
9a49d4
    dx = m_a.position - m_center.position;
9a49d4
    l = norm2(dx);
9a49d4
    if (l <= TConsts::epsilon*TConsts::epsilon) return;
9a49d4
    dx = dx*(1.0/sqrt(l));
9a49d4
    dy = TPointD(-dx.y, dx.x);
9a49d4
9a49d4
    m_grid1.position = m_grid0.position + dx*g1.x + dy*g1.y;
9a49d4
    m_b.position = m_center.position + dy*r2;
9a49d4
  }
9a49d4
9a49d4
public:
9a49d4
  void onMovePoint(TAssistantPoint &point, const TPointD &position) override {
9a49d4
    TPointD previousCenter = m_center.position;
9a49d4
    TPointD previousA = m_a.position;
9a49d4
    point.position = position;
9a49d4
    if (&point == &m_center) {
9a49d4
      m_a.position += m_center.position - previousCenter;
9a49d4
      m_b.position += m_center.position - previousCenter;
9a49d4
    } else
9a49d4
    if (&point == &m_a || &point == &m_b)
9a49d4
      fixBAndGgid1(previousCenter, previousA);
9a49d4
  }
9a49d4
9a49d4
  TAffine calcEllipseMatrix() const {
9a49d4
    TPointD da = m_a.position - m_center.position;
9a49d4
    TPointD db = m_b.position - m_center.position;
9a49d4
    double r1 = norm(da);
9a49d4
    if (r1 <= TConsts::epsilon) return TAffine::zero();
9a49d4
    double r2 = fabs( (rotate90(da)*db)*(1.0/r1) );
9a49d4
    if (r2 <= TConsts::epsilon) return TAffine::zero();
9a49d4
    return TAffine::translation(m_center.position)
9a49d4
         * TAffine::rotation(atan(da))
9a49d4
         * TAffine::scale(r1, r2);
9a49d4
  }
9a49d4
9a49d4
  void getGuidelines(
9a49d4
    const TPointD &position,
9a49d4
    const TAffine &toTool,
9a49d4
    TGuidelineList &outGuidelines ) const override
9a49d4
  {
9a49d4
    bool restrictA = getRestrictA();
9a49d4
    bool restrictB = getRestrictB();
9a49d4
    bool repeat = getRepeat();
9a49d4
9a49d4
    TAffine matrix = calcEllipseMatrix();
9a49d4
    if (matrix.isZero()) return;
9a49d4
    if (!restrictA && restrictB) {
9a49d4
      std::swap(matrix.a11, matrix.a12);
9a49d4
      std::swap(matrix.a21, matrix.a22);
9a49d4
      std::swap(restrictA, restrictB);
9a49d4
    }
9a49d4
9a49d4
    matrix = toTool*matrix;
9a49d4
    TAffine matrixInv = matrix.inv();
9a49d4
9a49d4
    if (restrictA && restrictB) {
9a49d4
      // ellipse
9a49d4
      outGuidelines.push_back(TGuidelineP(
9a49d4
        new TGuidelineEllipse(
9a49d4
          getEnabled(),
9a49d4
          getMagnetism(),
9a49d4
          matrix,
9a49d4
          matrixInv )));
9a49d4
    } else
9a49d4
    if (!restrictA && !restrictB) {
9a49d4
      // scaled ellipse
9a49d4
      TPointD p = matrixInv*position;
9a49d4
      double l = norm(p);
9a49d4
      outGuidelines.push_back(TGuidelineP(
9a49d4
        new TGuidelineEllipse(
9a49d4
          getEnabled(),
9a49d4
          getMagnetism(),
9a49d4
          matrix * TAffine::scale(l) )));
9a49d4
    } else { // restrictA
9a49d4
      TPointD p = matrixInv*position;
9a49d4
      if (repeat) {
9a49d4
        double ox = round(0.5*p.x)*2.0;
9a49d4
        p.x -= ox;
9a49d4
        matrix *= TAffine::translation(ox, 0.0);
9a49d4
      }
9a49d4
9a49d4
      // scale by Y
9a49d4
      if (p.x <= TConsts::epsilon - 1.0) {
9a49d4
        // line x = -1
9a49d4
        outGuidelines.push_back(TGuidelineP(
9a49d4
          new TGuidelineInfiniteLine(
9a49d4
            getEnabled(),
9a49d4
            getMagnetism(),
9a49d4
            matrix*TPointD(-1.0, 0.0),
9a49d4
            matrix*TPointD(-1.0, 1.0) )));
9a49d4
      } else
9a49d4
      if (p.x >= 1.0 - TConsts::epsilon) {
9a49d4
        // line x = 1
9a49d4
        outGuidelines.push_back(TGuidelineP(
9a49d4
          new TGuidelineInfiniteLine(
9a49d4
            getEnabled(),
9a49d4
            getMagnetism(),
9a49d4
            matrix*TPointD(1.0, 0.0),
9a49d4
            matrix*TPointD(1.0, 1.0) )));
9a49d4
      } else {
9a49d4
        // ellipse scaled by Y
9a49d4
        double k = fabs(p.y/sqrt(1.0 - p.x*p.x));
9a49d4
        outGuidelines.push_back(TGuidelineP(
9a49d4
          new TGuidelineEllipse(
9a49d4
            getEnabled(),
9a49d4
            getMagnetism(),
9a49d4
            matrix * TAffine::scale(1.0, k) )));
9a49d4
      }
9a49d4
    }
9a49d4
  }
9a49d4
9a49d4
private:
9a49d4
  void drawEllipseRanges(
9a49d4
    const TAngleRangeSet &ranges,
9a49d4
    const TAffine &ellipseMatrix,
9a49d4
    const TAffine &screenMatrixInv,
9a49d4
    double pixelSize,
9a49d4
    double alpha ) const
9a49d4
  {
9a49d4
    assert(ranges.check());
9a49d4
    TAngleRangeSet actualRanges(ranges);
9a49d4
    const TRectD oneBox(-1.0, -1.0, 1.0, 1.0);
9a49d4
    if (!TGuidelineEllipse::truncateEllipse(actualRanges, ellipseMatrix.inv()*screenMatrixInv, oneBox))
9a49d4
      return;
9a49d4
    assert(actualRanges.check());
9a49d4
9a49d4
    int segments = TGuidelineEllipse::calcSegmentsCount(ellipseMatrix, pixelSize);
9a49d4
    double da = M_2PI/segments;
9a49d4
    double s = sin(da);
9a49d4
    double c = cos(da);
9a49d4
9a49d4
    for(TAngleRangeSet::Iterator i(actualRanges); i; ++i) {
9a49d4
      double a0 = i.d0();
9a49d4
      double a1 = i.d1greater();
9a49d4
      int cnt = (int)floor((a1 - a0)/da);
9a49d4
      TPointD r(cos(a0), sin(a0));
9a49d4
      TPointD p0 = ellipseMatrix*r;
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 = ellipseMatrix*r;
9a49d4
        drawSegment(p0, p1, pixelSize, alpha);
9a49d4
        p0 = p1;
9a49d4
      }
9a49d4
      drawSegment(p0, ellipseMatrix*TPointD(cos(a1), sin(a1)), pixelSize, alpha);
9a49d4
    }
9a49d4
  }
9a49d4
9a49d4
  void drawEllipse(
9a49d4
    const TAffine &ellipseMatrix,
9a49d4
    const TAffine &screenMatrixInv,
9a49d4
    double pixelSize,
9a49d4
    double alpha ) const
9a49d4
      { drawEllipseRanges(TAngleRangeSet(true), ellipseMatrix, screenMatrixInv, pixelSize, alpha); }
9a49d4
9a49d4
  void drawRuler(const TAffine &ellipseMatrix, double pixelSize) const {
9a49d4
    double minStep = 10.0*pixelSize;
f278a5
    double alpha = getDrawingAlpha();
9a49d4
f278a5
    TAffine em = ellipseMatrix;
9a49d4
    TAffine ellipseMatrixInv = ellipseMatrix.inv();
9a49d4
    TPointD g0 = ellipseMatrixInv*m_grid0.position;
9a49d4
    TPointD g1 = ellipseMatrixInv*m_grid1.position;
9a49d4
    if (norm2(g0) <= TConsts::epsilon*TConsts::epsilon) return;
9a49d4
    if (norm2(g1) <= TConsts::epsilon*TConsts::epsilon) return;
9a49d4
    double ga0 = atan(g0);
9a49d4
    double ga1 = atan(g1);
9a49d4
f278a5
    // x and y radiuses
f278a5
    TPointD r( norm2(TPointD(em.a11, em.a21)), norm2(TPointD(em.a12, em.a22)) );
f278a5
    double avgR = 0.5*(r.x + r.y);
f278a5
    if (avgR <= TConsts::epsilon*TConsts::epsilon) return;
f278a5
    avgR = sqrt(avgR);
f278a5
    double actualMinStep = minStep/avgR;
f278a5
    r.x = sqrt(r.x);
f278a5
    r.y = sqrt(r.y);
f278a5
    
f278a5
    // remove radiuses from ellipse matrix
f278a5
    double rkx = r.x > TConsts::epsilon ? 1.0/r.x : 0.0;
f278a5
    double rky = r.y > TConsts::epsilon ? 1.0/r.y : 0.0;
f278a5
    em.a11 *= rkx; em.a21 *= rkx;
f278a5
    em.a12 *= rky; em.a22 *= rky;
f278a5
    
9a49d4
    if (getPerspective()) {
9a49d4
      // draw perspective
9a49d4
      if (ga0 < 0.0) { if (ga1 > 0.0) ga1 -= M_2PI; }
9a49d4
                else { if (ga1 < 0.0) ga1 += M_2PI; }
9a49d4
      double k = 0.0, begin = 0.0, end = 0.0;
9a49d4
      if (!calcPerspectiveStep(actualMinStep, 0.0, M_2PI, 0.0, fabs(ga0), ga1, k, begin, end)) return;
f278a5
      for(double a = begin; fabs(a) < fabs(end); a *= k) {
f278a5
        TPointD p( cos(a), (ga0 < 0.0 ? -1.0 : 1.0)*sin(a) );
f278a5
        TPointD n( p.x*r.y, p.y*r.x ); // perp to allipse
f278a5
        double nl2 = norm2(n);
f278a5
        if (nl2 > TConsts::epsilon*TConsts::epsilon) {
f278a5
          p.x *= r.x;
f278a5
          p.y *= r.y;
f278a5
          n = n*(1.0/sqrt(nl2));
f278a5
          drawMark(em*p, em.transformDirection(n), pixelSize, alpha);
f278a5
        }
f278a5
      }
9a49d4
    } else {
9a49d4
      // draw linear
9a49d4
      double da = ga1 - ga0;
9a49d4
      if (da < 0.0)         { da = -da;        std::swap(ga0, ga1); }
9a49d4
      if (ga1 - ga0 > M_PI) { da = M_2PI - da; std::swap(ga0, ga1); }
9a49d4
      if (da < actualMinStep) return;
f278a5
      for(double a = ga0 - floor(M_PI/da)*da; a < ga0 + M_PI; a += da) {
f278a5
        TPointD p( cos(a), sin(a) );
f278a5
        TPointD n( p.x*r.y, p.y*r.x ); // perp to allipse
f278a5
        double nl2 = norm2(n);
f278a5
        if (nl2 > TConsts::epsilon*TConsts::epsilon) {
f278a5
          p.x *= r.x;
f278a5
          p.y *= r.y;
f278a5
          n = n*(1.0/sqrt(nl2));
f278a5
          drawMark(em*p, em.transformDirection(n), pixelSize, alpha);
f278a5
        }
f278a5
      }
9a49d4
    }
9a49d4
  }
9a49d4
9a49d4
  void drawConcentricGrid(
9a49d4
    const TAffine &ellipseMatrix,
9a49d4
    const TAffine &screenMatrixInv,
9a49d4
    double pixelSize ) const
9a49d4
  {
9a49d4
    double minStep = 20.0*pixelSize;
9a49d4
    double alpha = getDrawingGridAlpha();
9a49d4
    TAffine ellipseMatrixInv = ellipseMatrix.inv();
9a49d4
9a49d4
    // calculate bounds
9a49d4
    TAffine matrixInv = ellipseMatrixInv * screenMatrixInv;
9a49d4
    TPointD o  = matrixInv * TPointD(-1.0, -1.0);
9a49d4
    TPointD dx = matrixInv.transformDirection( TPointD(2.0, 0.0) );
9a49d4
    TPointD dy = matrixInv.transformDirection( TPointD(0.0, 2.0) );
9a49d4
    double max = 0.0;
9a49d4
    double min = std::numeric_limits<double>::infinity();</double>
9a49d4
9a49d4
    // distance to points
9a49d4
    TPointD corners[] = { o, o+dx, o+dx+dy, o+dy };
9a49d4
    for(int i = 0; i < 4; ++i) {
9a49d4
      double k = norm(corners[i]);
9a49d4
      if (k < min) min = k;
9a49d4
      if (k > max) max = k;
9a49d4
    }
9a49d4
9a49d4
    // distance to sides
9a49d4
    TPointD lines[] = { dx, dy, -1.0*dx, -1.0*dy };
9a49d4
    int positive = 0, negative = 0;
9a49d4
    for(int i = 0; i < 4; ++i) {
9a49d4
      double len2 = norm2(lines[i]);
9a49d4
      if (len2 <= TConsts::epsilon*TConsts::epsilon) continue;
9a49d4
      double k = (corners[i]*rotate90(lines[i]))/sqrt(len2);
9a49d4
      if (k > TConsts::epsilon) ++positive;
9a49d4
      if (k < TConsts::epsilon) ++negative;
9a49d4
      double l = -(corners[i]*lines[i]);
9a49d4
      if (l <= TConsts::epsilon || l >= len2 - TConsts::epsilon) continue;
9a49d4
      k = fabs(k);
9a49d4
      if (k < min) min = k;
9a49d4
      if (k > max) max = k;
9a49d4
    }
9a49d4
9a49d4
    // if center is inside bounds
9a49d4
    if (min < 0.0 || positive == 0 || negative == 0) min = 0.0;
9a49d4
    if (max <= min) return;
9a49d4
9a49d4
    // draw
9a49d4
    const TAffine &em = ellipseMatrix;
9a49d4
    double r = sqrt(0.5*(norm2(TPointD(em.a11, em.a21)) + norm2(TPointD(em.a12, em.a22))));
9a49d4
    double actualMinStep = minStep/r;
9a49d4
    double gs0 = norm(ellipseMatrixInv*m_grid0.position);
9a49d4
    double gs1 = norm(ellipseMatrixInv*m_grid1.position);
9a49d4
    if (gs0 <= TConsts::epsilon*TConsts::epsilon) return;
9a49d4
    if (gs1 <= TConsts::epsilon*TConsts::epsilon) return;
9a49d4
9a49d4
    if (getPerspective()) {
9a49d4
      // draw perspective
9a49d4
      double k = 0.0, begin = 0.0, end = 0.0;
9a49d4
      if (!calcPerspectiveStep(actualMinStep, min, max, 0.0, gs0, gs1, k, begin, end)) return;
9a49d4
      for(double x = begin; fabs(x) < fabs(end); x *= k)
9a49d4
        drawEllipse(ellipseMatrix * TAffine::scale(x), screenMatrixInv, pixelSize, alpha);
9a49d4
    } else {
9a49d4
      // draw linear
9a49d4
      double dx = fabs(gs1 - gs0);
9a49d4
      if (dx*r < minStep) return;
9a49d4
      for(double x = gs0 + ceil((min - gs0)/dx)*dx; x < max; x += dx)
9a49d4
        drawEllipse(ellipseMatrix * TAffine::scale(x), screenMatrixInv, pixelSize, alpha);
9a49d4
    }
9a49d4
  }
9a49d4
9a49d4
  void drawParallelGrid(
9a49d4
    const TAffine &ellipseMatrix,
9a49d4
    const TAffine &screenMatrixInv,
9a49d4
    double pixelSize ) const
9a49d4
  {
9a49d4
    double minStep = 10.0*pixelSize;
9a49d4
    double alpha = getDrawingGridAlpha();
9a49d4
    TAffine ellipseMatrixInv = ellipseMatrix.inv();
9a49d4
9a49d4
    const TAffine &em = ellipseMatrix;
9a49d4
    double r = sqrt(0.5*(norm2(TPointD(em.a11, em.a21)) + norm2(TPointD(em.a12, em.a22))));
9a49d4
    double actualMinStep = minStep/r;
9a49d4
    TPointD g0 = ellipseMatrixInv*m_grid0.position;
9a49d4
    TPointD g1 = ellipseMatrixInv*m_grid1.position;
9a49d4
    if (getRepeat())
9a49d4
      { g0.x -= round(0.5*g0.x)*2.0; g1.x -= round(0.5*g1.x)*2.0; }
9a49d4
    if (fabs(g0.x) >= 1.0 - TConsts::epsilon) return;
9a49d4
    if (fabs(g1.x) >= 1.0 - TConsts::epsilon) return;
9a49d4
    double gs0 = g0.y/sqrt(1.0 - g0.x*g0.x);
9a49d4
    double gs1 = g1.y/sqrt(1.0 - g1.x*g1.x);
9a49d4
    if (fabs(gs0) >= 1.0 - TConsts::epsilon) return;
9a49d4
    if (fabs(gs1) >= 1.0 - TConsts::epsilon) return;
9a49d4
9a49d4
    TAngleRangeSet ranges;
9a49d4
    ranges.add( TAngleRangeSet::fromDouble(0.0), TAngleRangeSet::fromDouble(M_PI) );
9a49d4
9a49d4
    if (getPerspective()) {
9a49d4
      // draw perspective (actually angular)
9a49d4
      double k = 0.0, begin = 0.0, end = 0.0;
9a49d4
      double a0 = asin(gs0);
9a49d4
      double a1 = asin(gs1);
9a49d4
      double da = fabs(a1 - a0);
9a49d4
      if (fabs(sin(da)) < 2.0*actualMinStep) return;
9a49d4
      for(double a = a0 + ceil((-M_PI_2 - a0)/da)*da; a < M_PI_2; a += da)
9a49d4
        drawEllipseRanges(
9a49d4
          ranges,
9a49d4
          ellipseMatrix*TAffine::scale(a < 0.0 ? -1.0 : 1.0, sin(a)),
9a49d4
          screenMatrixInv,
9a49d4
          pixelSize,
9a49d4
          alpha );
9a49d4
    } else {
9a49d4
      // draw linear
9a49d4
      double dx = fabs(gs1 - gs0);
9a49d4
      if (dx < actualMinStep) return;
9a49d4
      for(double x = gs0 + ceil((-1.0 - gs0)/dx)*dx; x < 1.0; x += dx)
9a49d4
        drawEllipseRanges(
9a49d4
          ranges,
9a49d4
          ellipseMatrix*TAffine::scale(x < 0.0 ? -1.0 : 1.0, x),
9a49d4
          screenMatrixInv,
9a49d4
          pixelSize,
9a49d4
          alpha );
9a49d4
    }
9a49d4
  }
9a49d4
9a49d4
  void draw(
9a49d4
    const TAffine &ellipseMatrix,
9a49d4
    const TAffine &screenMatrixInv,
9a49d4
    double ox,
9a49d4
    double pixelSize,
9a49d4
    bool enabled ) const
9a49d4
  {
9a49d4
    const double crossSize = 0.1;
9a49d4
9a49d4
    double alpha = getDrawingAlpha(enabled);
9a49d4
    bool grid = getGrid();
9a49d4
    bool ruler = getRestrictA() && getRestrictB();
9a49d4
    bool concentric = !getRestrictA() && !getRestrictB();
9a49d4
9a49d4
    drawSegment( ellipseMatrix*TPointD(-crossSize, 0.0),
9a49d4
                 ellipseMatrix*TPointD( crossSize, 0.0), pixelSize, alpha);
9a49d4
    drawSegment( ellipseMatrix*TPointD(0.0, -crossSize),
9a49d4
                 ellipseMatrix*TPointD(0.0,  crossSize), pixelSize, alpha);
9a49d4
    drawEllipse(ellipseMatrix, screenMatrixInv, pixelSize, alpha);
9a49d4
    if (ox > 1.0)
9a49d4
      drawSegment( ellipseMatrix*TPointD(-1.0, -1.0),
9a49d4
                   ellipseMatrix*TPointD(-1.0,  1.0), pixelSize, alpha);
9a49d4
    else if (ox < -1.0)
9a49d4
      drawSegment( ellipseMatrix*TPointD( 1.0, -1.0),
9a49d4
                   ellipseMatrix*TPointD( 1.0,  1.0), pixelSize, alpha);
9a49d4
9a49d4
    if (!grid) return;
9a49d4
9a49d4
    if (ruler) {
9a49d4
      drawRuler(ellipseMatrix, pixelSize);
9a49d4
    } else
9a49d4
    if (concentric) {
9a49d4
      drawConcentricGrid(ellipseMatrix, screenMatrixInv, pixelSize);
9a49d4
    } else {
9a49d4
      drawParallelGrid(ellipseMatrix, screenMatrixInv, pixelSize);
9a49d4
    }
9a49d4
  }
9a49d4
9a49d4
public:
9a49d4
  void draw(TToolViewer *viewer, bool enabled) const override {
9a49d4
    bool restrictA = getRestrictA();
9a49d4
    bool restrictB = getRestrictB();
9a49d4
    bool repeat = getRepeat();
9a49d4
    double minStep = 30.0;
9a49d4
9a49d4
    TAffine ellipseMatrix = calcEllipseMatrix();
9a49d4
    if (ellipseMatrix.isZero()) return;
9a49d4
    if (!restrictA && restrictB) {
9a49d4
      std::swap(ellipseMatrix.a11, ellipseMatrix.a12);
9a49d4
      std::swap(ellipseMatrix.a21, ellipseMatrix.a22);
9a49d4
    }
9a49d4
9a49d4
    // common data about viewport
9a49d4
    const TRectD oneBox(-1.0, -1.0, 1.0, 1.0);
9a49d4
    TAffine4 modelview, projection;
9a49d4
    glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a);
9a49d4
    glGetDoublev(GL_PROJECTION_MATRIX, projection.a);
9a49d4
    TAffine matrix = (projection*modelview).get2d();
9a49d4
    TAffine matrixInv = matrix.inv();
9a49d4
    double pixelSize = sqrt(tglGetPixelSize2());
9a49d4
9a49d4
    if (!repeat || restrictA == restrictB || norm(TPointD(ellipseMatrix.a11, ellipseMatrix.a21)) < minStep*pixelSize) {
9a49d4
      draw(ellipseMatrix, matrixInv, 0.0, pixelSize, enabled);
9a49d4
    } else {
9a49d4
      // calculate bounds
9a49d4
      TPointD o(ellipseMatrix.a13, ellipseMatrix.a23);
9a49d4
      TPointD proj(ellipseMatrix.a11, ellipseMatrix.a21);
9a49d4
      proj = proj * (1.0/norm2(proj));
9a49d4
      TPointD corners[4] = {
9a49d4
        TPointD(oneBox.x0, oneBox.y0),
9a49d4
        TPointD(oneBox.x0, oneBox.y1),
9a49d4
        TPointD(oneBox.x1, oneBox.y0),
9a49d4
        TPointD(oneBox.x1, oneBox.y1) };
9a49d4
      double minX = 0.0, maxX = 0.0;
9a49d4
      for(int i = 0; i < 4; ++i) {
9a49d4
        double x = proj * (matrixInv*corners[i] - o);
9a49d4
        if (i == 0 || x < minX) minX = x;
9a49d4
        if (i == 0 || x > maxX) maxX = x;
9a49d4
      }
9a49d4
      if (maxX <= minX) return;
9a49d4
9a49d4
      // draw
9a49d4
      for(double ox = round(0.5*minX)*2.0; ox - 1.0 < maxX; ox += 2.0)
9a49d4
        draw(ellipseMatrix*TAffine::translation(ox, 0.0), matrixInv, ox, pixelSize, enabled);
9a49d4
    }
9a49d4
  }
9a49d4
};
9a49d4
9a49d4
9a49d4
//*****************************************************************************************
9a49d4
//    Registration
9a49d4
//*****************************************************************************************
9a49d4
9a49d4
static TAssistantTypeT<tassistantellipse> assistantEllipse("assistantEllipse");</tassistantellipse>
9a49d4