166af2
166af2
166af2
// TnzTools includes
166af2
#include <tools assistant.h=""></tools>
d5090c
#include <tools assistants="" guidelineline.h=""></tools>
d5090c
166af2
166af2
// TnzCore includes
166af2
#include <tgl.h></tgl.h>
166af2
166af2
166af2
//*****************************************************************************************
166af2
//    TAssistantLine implementation
166af2
//*****************************************************************************************
166af2
f36ea4
class TAssistantLine final : public TAssistant {
166af2
  Q_DECLARE_TR_FUNCTIONS(TAssistantVanishingPoint)
166af2
public:
166af2
  const TStringId m_idRestricktA;
166af2
  const TStringId m_idRestricktB;
166af2
  const TStringId m_idParallel;
166af2
  const TStringId m_idGrid;
166af2
  const TStringId m_idPerspective;
166af2
166af2
protected:
166af2
  TAssistantPoint &m_a;
166af2
  TAssistantPoint &m_b;
166af2
  TAssistantPoint &m_grid0;
166af2
  TAssistantPoint &m_grid1;
166af2
166af2
public:
166af2
  TAssistantLine(TMetaObject &object):
166af2
    TAssistant(object),
166af2
    m_idRestricktA("restrictA"),
166af2
    m_idRestricktB("restrictB"),
166af2
    m_idParallel("parallel"),
166af2
    m_idGrid("grid"),
166af2
    m_idPerspective("perspective"),
166af2
    m_a( addPoint("a", TAssistantPoint::CircleCross) ),
166af2
    m_b( addPoint("b", TAssistantPoint::Circle, TPointD(100.0, 0.0)) ),
166af2
    m_grid0( addPoint("grid0",  TAssistantPoint::CircleDoubleDots, TPointD(  0.0,-50.0)) ),
166af2
    m_grid1( addPoint("grid1",  TAssistantPoint::CircleDots,       TPointD( 25.0,-75.0)) )
166af2
  {
166af2
    addProperty( new TBoolProperty(m_idRestricktA.str(), getRestrictA()) );
166af2
    addProperty( new TBoolProperty(m_idRestricktB.str(), getRestrictB()) );
166af2
    addProperty( new TBoolProperty(m_idParallel.str(), getParallel()) );
166af2
    addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) );
166af2
    addProperty( new TBoolProperty(m_idPerspective.str(), getPerspective()) );
166af2
  }
166af2
166af2
  static QString getLocalName()
166af2
    { return tr("Line"); }
166af2
166af2
  void updateTranslation() const override {
166af2
    setTranslation(m_idRestricktA, tr("Restrict A"));
166af2
    setTranslation(m_idRestricktB, tr("Restrict B"));
166af2
    setTranslation(m_idParallel, tr("Parallel"));
166af2
    setTranslation(m_idGrid, tr("Grid"));
166af2
    setTranslation(m_idPerspective, tr("Perspective"));
166af2
  }
166af2
166af2
  inline bool getRestrictA() const
166af2
    { return data()[m_idRestricktA].getBool(); }
166af2
  inline bool getRestrictB() const
166af2
    { return data()[m_idRestricktB].getBool(); }
166af2
  inline bool getParallel() const
166af2
    { return data()[m_idParallel].getBool(); }
166af2
  inline bool getGrid() const
166af2
    { return data()[m_idGrid].getBool(); }
166af2
  inline bool getPerspective() const
166af2
    { return data()[m_idPerspective].getBool(); }
166af2
166af2
  void onDataChanged(const TVariant &value) override {
166af2
    TAssistant::onDataChanged(value);
166af2
    m_grid0.visible = getGrid()
166af2
                   || (getParallel() && (getRestrictA() || getRestrictB()));
166af2
    m_grid1.visible = getGrid();
166af2
  }
166af2
166af2
private:
166af2
  void fixGrid1(const TPointD &previousA, const TPointD &previousB) {
166af2
    TPointD dx = previousB - previousA;
166af2
    double l = norm2(dx);
166af2
    if (l <= TConsts::epsilon*TConsts::epsilon) return;
166af2
    dx = dx*(1.0/sqrt(l));
166af2
    TPointD dy(-dx.y, dx.x);
166af2
166af2
    TPointD g1 = m_grid1.position - m_grid0.position;
166af2
    g1 = TPointD(dx*g1, dy*g1);
166af2
166af2
    dx = m_b.position - m_a.position;
166af2
    l = norm2(dx);
166af2
    if (l <= TConsts::epsilon*TConsts::epsilon) return;
166af2
    dx = dx*(1.0/sqrt(l));
166af2
    dy = TPointD(-dx.y, dx.x);
166af2
166af2
    m_grid1.position = m_grid0.position + dx*g1.x + dy*g1.y;
166af2
  }
166af2
166af2
public:
166af2
  void onMovePoint(TAssistantPoint &point, const TPointD &position) override {
166af2
    TPointD previousA = m_a.position;
166af2
    TPointD previousB = m_b.position;
166af2
    point.position = position;
166af2
    if (&point != &m_grid1)
166af2
      fixGrid1(previousA, previousB);
166af2
  }
166af2
166af2
  void getGuidelines(
166af2
    const TPointD &position,
166af2
    const TAffine &toTool,
166af2
    TGuidelineList &outGuidelines ) const override
166af2
  {
166af2
    bool restrictA = getRestrictA();
166af2
    bool restrictB = getRestrictB();
166af2
    bool parallel = getParallel();
166af2
    bool perspective = getPerspective();
166af2
166af2
    TPointD a = toTool*m_a.position;
166af2
    TPointD b = toTool*m_b.position;
166af2
    TPointD ab = b - a;
166af2
    double abLen2 = norm2(ab);
166af2
    if (abLen2 < TConsts::epsilon*TConsts::epsilon) return;
166af2
166af2
    if (parallel) {
166af2
      TPointD abp = rotate90(ab);
166af2
      TPointD ag = toTool*m_grid0.position - a;
166af2
      double k = abp*ag;
166af2
      if (fabs(k) <= TConsts::epsilon) {
166af2
        if (restrictA || restrictB) return;
166af2
        a = position;
166af2
      } else {
166af2
        k = (abp*(position - a))/k;
166af2
        a = a + ag*k;
166af2
      }
166af2
      if (perspective && (restrictA || restrictB))
166af2
        ab = ab*k;
166af2
      b = a + ab;
166af2
    }
166af2
166af2
    if (restrictA && restrictB)
166af2
      outGuidelines.push_back(TGuidelineP(
166af2
        new TGuidelineLine(
166af2
          getEnabled(), getMagnetism(), a,  b )));
166af2
    else if (restrictA)
166af2
      outGuidelines.push_back(TGuidelineP(
166af2
        new TGuidelineRay(
166af2
          getEnabled(), getMagnetism(), a,  b )));
166af2
    else if (restrictB)
166af2
      outGuidelines.push_back(TGuidelineP(
166af2
        new TGuidelineRay(
166af2
          getEnabled(), getMagnetism(), b,  a ))); // b first
166af2
    else
166af2
      outGuidelines.push_back(TGuidelineP(
166af2
        new TGuidelineInfiniteLine(
166af2
          getEnabled(), getMagnetism(), a,  b )));
166af2
  }
166af2
166af2
private:
9a49d4
  void drawRuler(const TPointD &a, const TPointD &b, double pixelSize, bool perspective) const {
9a49d4
    double minStep = 10.0*pixelSize;
f278a5
    double alpha = getDrawingAlpha();
166af2
166af2
    TPointD direction = b - a;
166af2
    double l2 = norm2(direction);
166af2
    if (l2 <= TConsts::epsilon*TConsts::epsilon) return;
166af2
    double dirLen = sqrt(l2);
166af2
    TPointD dirProj = direction*(1.0/l2);
f278a5
    TPointD normal = TPointD(-direction.y, direction.x)*(1.0/dirLen);
166af2
166af2
    double xg0 = dirProj*(m_grid0.position - a);
166af2
    double xg1 = dirProj*(m_grid1.position - a);
166af2
166af2
    if (perspective) {
166af2
      // draw perspective
166af2
      double xa0 = dirProj*(m_a.position - a);
166af2
      double k = 0.0, begin = 0.0, end = 0.0;
166af2
      if (!calcPerspectiveStep(minStep/dirLen, 0.0, 1.0, xa0, xg0, xg1, k, begin, end)) return;
166af2
      for(double x = begin; fabs(x) < fabs(end); x *= k)
f278a5
        drawMark(a + direction*(xa0 + x), normal, pixelSize, alpha);
166af2
    } else {
166af2
      // draw linear
166af2
      double dx = fabs(xg1 - xg0);
166af2
      if (dx*dirLen < minStep) return;
166af2
      for(double x = xg0 - floor(xg0/dx)*dx; x < 1.0; x += dx)
f278a5
        drawMark(a + direction*x, normal, pixelSize, alpha);
166af2
    }
166af2
  }
166af2
166af2
  void drawLine(
166af2
    const TAffine &matrix,
166af2
    const TAffine &matrixInv,
166af2
    double pixelSize,
166af2
    const TPointD &a,
166af2
    const TPointD &b,
166af2
    bool restrictA,
166af2
    bool restrictB,
166af2
    double alpha ) const
166af2
  {
166af2
    const TRectD oneBox(-1.0, -1.0, 1.0, 1.0);
166af2
    TPointD aa = matrix*a;
166af2
    TPointD bb = matrix*b;
166af2
    if ( restrictA && restrictB ? TGuidelineLineBase::truncateLine(oneBox, aa, bb)
166af2
       : restrictA              ? TGuidelineLineBase::truncateRay (oneBox, aa, bb)
166af2
       : restrictB              ? TGuidelineLineBase::truncateRay (oneBox, bb, aa) // aa first
166af2
       :                  TGuidelineLineBase::truncateInfiniteLine(oneBox, aa, bb) )
166af2
          drawSegment(matrixInv*aa, matrixInv*bb, pixelSize, alpha);
166af2
  }
166af2
166af2
  void drawGrid(
166af2
    const TAffine &matrix,
166af2
    const TAffine &matrixInv,
166af2
    double pixelSize,
166af2
    bool restrictA,
166af2
    bool restrictB,
166af2
    bool perspective ) const
166af2
  {
9a49d4
    double minStep = 10.0*pixelSize;
166af2
166af2
    double alpha = getDrawingGridAlpha();
166af2
    TPointD a = m_a.position;
166af2
    TPointD b = m_b.position;
166af2
    TPointD ab = b - a;
166af2
    double abLen2 = norm2(ab);
166af2
    if (abLen2 < TConsts::epsilon*TConsts::epsilon) return;
166af2
    double abLen = sqrt(abLen2);
166af2
166af2
    TPointD g0 = m_grid0.position;
166af2
    TPointD g1 = m_grid1.position;
166af2
166af2
    TPointD abp = rotate90(ab);
166af2
    TPointD ag = g0 - a;
166af2
    if (fabs(abp*ag) <= TConsts::epsilon) {
166af2
      if (restrictA || restrictB) return;
166af2
      ag = abp;
166af2
    }
166af2
    double agLen2 = norm2(ag);
166af2
    if (agLen2 < TConsts::epsilon*TConsts::epsilon) return;
166af2
    double agLen = sqrt(agLen2);
166af2
    double abpAgK = 1.0/(abp*ag);
166af2
    TPointD abpAgProj = abp*abpAgK;
166af2
166af2
    // draw restriction lines
166af2
    if (perspective) {
166af2
      if (restrictA) drawLine(matrix, matrixInv, pixelSize, a, a + ag, false, false, alpha);
166af2
      if (restrictB) drawLine(matrix, matrixInv, pixelSize, a, a + ag + ab, false, false, alpha);
166af2
    } else {
166af2
      if (restrictA) drawLine(matrix, matrixInv, pixelSize, a, a + ag, false, false, alpha);
166af2
      if (restrictB) drawLine(matrix, matrixInv, pixelSize, b, b + ag, false, false, alpha);
166af2
    }
166af2
166af2
    double minStepX = fabs(minStep*abLen*abpAgK);
166af2
    if (minStepX <= TConsts::epsilon) return;
166af2
166af2
    // calculate bounds
166af2
    const TRectD oneBox(-1.0, -1.0, 1.0, 1.0);
166af2
    TPointD corners[4] = {
166af2
      TPointD(oneBox.x0, oneBox.y0),
166af2
      TPointD(oneBox.x0, oneBox.y1),
166af2
      TPointD(oneBox.x1, oneBox.y0),
166af2
      TPointD(oneBox.x1, oneBox.y1) };
166af2
    double minX = 0.0, maxX = 0.0;
166af2
    for(int i = 0; i < 4; ++i) {
166af2
      double x = abpAgProj * (matrixInv*corners[i] - a);
166af2
      if (i == 0 || x < minX) minX = x;
166af2
      if (i == 0 || x > maxX) maxX = x;
166af2
    }
166af2
    if (maxX <= minX) return;
166af2
166af2
    double x0 = abpAgProj*(g0 - a);
166af2
    double x1 = abpAgProj*(g1 - a);
166af2
166af2
    if (perspective) {
166af2
      double k = 0.0, begin = 0.0, end = 0.0;
166af2
      if (!calcPerspectiveStep(minStepX, minX, maxX, 0.0, x0, x1, k, begin, end)) return;
166af2
      double abk = 1.0/fabs(x0);
166af2
      for(double x = begin; fabs(x) < fabs(end); x *= k) {
166af2
        TPointD ca = a + ag*x;
166af2
        TPointD cb = ca + ab*(abk*x);
166af2
        drawLine(matrix, matrixInv, pixelSize, ca, cb, restrictA, restrictB, alpha);
166af2
      }
166af2
    } else {
166af2
      double dx = fabs(x1 - x0);
166af2
      if (dx < minStepX) return;
166af2
      for(double x = x0 + ceil((minX - x0)/dx)*dx; x < maxX; x += dx) {
166af2
        TPointD ca = a + ag*x;
166af2
        drawLine(matrix, matrixInv, pixelSize, ca, ca + ab, restrictA, restrictB, alpha);
166af2
      }
166af2
    }
166af2
  }
166af2
166af2
public:
166af2
  void draw(TToolViewer *viewer, bool enabled) const override {
166af2
    double alpha = getDrawingAlpha(enabled);
166af2
    bool restrictA = getRestrictA();
166af2
    bool restrictB = getRestrictB();
166af2
    bool parallel = getParallel();
166af2
    bool grid = getGrid();
166af2
    bool perspective = getPerspective();
166af2
166af2
    // common data about viewport
166af2
    const TRectD oneBox(-1.0, -1.0, 1.0, 1.0);
166af2
    TAffine4 modelview, projection;
166af2
    glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a);
166af2
    glGetDoublev(GL_PROJECTION_MATRIX, projection.a);
166af2
    TAffine matrix = (projection*modelview).get2d();
166af2
    TAffine matrixInv = matrix.inv();
166af2
    double pixelSize = sqrt(tglGetPixelSize2());
166af2
166af2
    // calculate range
166af2
    TPointD aa = matrix*m_a.position;
166af2
    TPointD bb = matrix*m_b.position;
166af2
    bool success = false;
166af2
    if (restrictA && restrictB)
166af2
      success = TGuidelineLineBase::truncateLine(oneBox, aa, bb);
166af2
    else if (restrictA)
166af2
      success = TGuidelineLineBase::truncateRay(oneBox, aa, bb);
166af2
    else if (restrictB)
166af2
      success = TGuidelineLineBase::truncateRay(oneBox, bb, aa);
166af2
    else
166af2
      success = TGuidelineLineBase::truncateInfiniteLine(oneBox, aa, bb);
f278a5
f278a5
    if (!success) {
f278a5
      // line is out of screen, bud grid still can be visible
f278a5
      if (grid && getParallel())
f278a5
          drawGrid(matrix, matrixInv, pixelSize, restrictA, restrictB, perspective);
f278a5
      return;
f278a5
    }
f278a5
    
166af2
    TPointD a = matrixInv*aa;
166af2
    TPointD b = matrixInv*bb;
166af2
166af2
    // draw line
166af2
    drawSegment(a, b, pixelSize, alpha);
166af2
166af2
    // draw restriction marks
166af2
    if (restrictA || (!parallel && grid && perspective))
166af2
      drawDot(m_a.position);
166af2
    if (restrictB)
166af2
      drawDot(m_b.position);
166af2
9a49d4
    if (grid) {
166af2
      if (getParallel()) {
166af2
        drawGrid(matrix, matrixInv, pixelSize, restrictA, restrictB, perspective);
166af2
      } else {
9a49d4
        drawRuler(a, b, pixelSize, perspective);
166af2
      }
166af2
    }
166af2
  }
166af2
};
166af2
166af2
166af2
//*****************************************************************************************
166af2
//    Registration
166af2
//*****************************************************************************************
166af2
166af2
static TAssistantTypeT<tassistantline> assistantLine("assistantLine");</tassistantline>