4df9cd
4df9cd
4da757
// TnzTools includes
4da757
#include <tools assistant.h=""></tools>
d5090c
#include <tools assistants="" guidelineline.h=""></tools>
4df9cd
4da757
// TnzCore includes
4da757
#include <tgl.h></tgl.h>
4df9cd
249386
249386
//*****************************************************************************************
4da757
//    TAssistantVanishingPoint implementation
249386
//*****************************************************************************************
249386
4da757
class DVAPI TAssistantVanishingPoint final : public TAssistant {
4da757
  Q_DECLARE_TR_FUNCTIONS(TAssistantVanishingPoint)
7900ea
public:
7900ea
  const TStringId m_idPassThrough;
7900ea
  const TStringId m_idGrid;
7900ea
  const TStringId m_idPerspective;
7900ea
c5e805
protected:
7900ea
  TAssistantPoint &m_center;
7900ea
  TAssistantPoint &m_a0;
7900ea
  TAssistantPoint &m_a1;
7900ea
  TAssistantPoint &m_b0;
7900ea
  TAssistantPoint &m_b1;
7900ea
  TAssistantPoint &m_grid0;
7900ea
  TAssistantPoint &m_grid1;
c5e805
4da757
public:
4da757
  TAssistantVanishingPoint(TMetaObject &object):
c5e805
    TAssistant(object),
7900ea
    m_idPassThrough("passThrough"),
7900ea
    m_idGrid("grid"),
7900ea
    m_idPerspective("perspective"),
7900ea
    m_center( addPoint("center", TAssistantPoint::CircleCross) ),
7c7225
    m_a0    ( addPoint("a0",     TAssistantPoint::Circle, TPointD(-50.0, 0.0)) ),
7c7225
    m_a1    ( addPoint("a1",     TAssistantPoint::Circle, TPointD(-75.0, 0.0)) ),
7c7225
    m_b0    ( addPoint("b0",     TAssistantPoint::Circle, TPointD( 50.0, 0.0)) ),
7c7225
    m_b1    ( addPoint("b1",     TAssistantPoint::Circle, TPointD( 75.0, 0.0)) ),
7c7225
    m_grid0 ( addPoint("grid0",  TAssistantPoint::CircleDoubleDots, TPointD(  0.0,-50.0)) ),
7c7225
    m_grid1 ( addPoint("grid1",  TAssistantPoint::CircleDots,       TPointD( 25.0,-50.0)) )
7900ea
  {
7900ea
    addProperty( new TBoolProperty(m_idPassThrough.str(), getPassThrough()) );
7900ea
    addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) );
7900ea
    addProperty( new TBoolProperty(m_idPerspective.str(), getPerspective()) );
7900ea
  }
4da757
4da757
  static QString getLocalName()
4da757
    { return tr("Vanishing Point"); }
4da757
c7854d
  void updateTranslation() const override {
7900ea
    setTranslation(m_idPassThrough, tr("Pass Through"));
7900ea
    setTranslation(m_idGrid, tr("Grid"));
7900ea
    setTranslation(m_idPerspective, tr("Perspective"));
7900ea
  }
7900ea
7900ea
  inline bool getPassThrough() const
7900ea
    { return data()[m_idPassThrough].getBool(); }
7900ea
  inline bool getGrid() const
7900ea
    { return data()[m_idGrid].getBool(); }
7900ea
  inline bool getPerspective() const
7900ea
    { return data()[m_idPerspective].getBool(); }
7900ea
c7854d
  void onDataChanged(const TVariant &value) override {
7900ea
    TAssistant::onDataChanged(value);
c7854d
    m_grid0.visible = m_grid1.visible = getGrid();
7900ea
  }
7900ea
7900ea
private:
7900ea
  void fixCenter() {
7900ea
    if ( !(m_a0.position == m_a1.position)
7900ea
      && !(m_b0.position == m_b1.position) )
7900ea
    {
7900ea
      const TPointD &a = m_a0.position;
7900ea
      const TPointD &b = m_b0.position;
7900ea
      const TPointD da = m_a1.position - a;
7900ea
      const TPointD db = m_b1.position - b;
7900ea
      const TPointD ab = b - a;
7900ea
      double k = db.x*da.y - db.y*da.x;
7900ea
      if (fabs(k) > TConsts::epsilon) {
7900ea
        double lb = (da.x*ab.y - da.y*ab.x)/k;
7900ea
        m_center.position.x = lb*db.x + b.x;
7900ea
        m_center.position.y = lb*db.y + b.y;
7900ea
      }
7900ea
    }
7900ea
  }
7900ea
7900ea
  void fixSidePoint(TAssistantPoint &p0, TAssistantPoint &p1, TPointD previousP0) {
7900ea
    if (p0.position != m_center.position && p0.position != p1.position) {
7900ea
      TPointD dp0 = p0.position - m_center.position;
7900ea
      TPointD dp1 = p1.position - previousP0;
7900ea
      double l0 = norm(dp0);
7900ea
      double l1 = norm(dp1);
7900ea
      if (l0 > TConsts::epsilon && l0 + l1 > TConsts::epsilon)
7900ea
        p1.position = m_center.position + dp0*((l0 + l1)/l0);
7900ea
    }
7900ea
  }
7900ea
7900ea
  void fixSidePoint(TAssistantPoint &p0, TAssistantPoint &p1)
7900ea
    { fixSidePoint(p0, p1, p0.position); }
7900ea
c7854d
  void fixGrid1(const TPointD &previousCenter, const TPointD &previousGrid0) {
c7854d
    TPointD dx = previousCenter - previousGrid0;
c7854d
    double l = norm2(dx);
c7854d
    if (l <= TConsts::epsilon*TConsts::epsilon) return;
c7854d
    dx = dx*(1.0/sqrt(l));
c7854d
    TPointD dy(-dx.y, dx.x);
c7854d
c7854d
    TPointD d = m_grid1.position - previousGrid0;
c7854d
    double x = (dx*d);
c7854d
    double y = (dy*d);
c7854d
c7854d
    dx = m_center.position - m_grid0.position;
c7854d
    l = norm2(dx);
c7854d
    if (l <= TConsts::epsilon*TConsts::epsilon) return;
c7854d
    dx = dx*(1.0/sqrt(l));
c7854d
    dy = TPointD(-dx.y, dx.x);
c7854d
c7854d
    m_grid1.position = m_grid0.position + dx*x + dy*y;
c7854d
  }
c7854d
7900ea
public:
7900ea
  void onFixPoints() override {
7900ea
    fixSidePoint(m_a0, m_a1);
7900ea
    fixSidePoint(m_b0, m_b1);
7900ea
    fixCenter();
7900ea
  }
7900ea
7900ea
  void onMovePoint(TAssistantPoint &point, const TPointD &position) override {
c7854d
    TPointD previousCenter = m_center.position;
7900ea
    TPointD previous = point.position;
7900ea
    point.position = position;
3f8ca2
    if (&point == &m_center) {
3f8ca2
      fixSidePoint(m_a0, m_a1);
3f8ca2
      fixSidePoint(m_b0, m_b1);
3f8ca2
    } else
7900ea
    if (&point == &m_a0) {
7900ea
      fixSidePoint(m_a0, m_a1, previous);
7900ea
      fixSidePoint(m_b0, m_b1);
7900ea
    } else
7900ea
    if (&point == &m_b0) {
7900ea
      fixSidePoint(m_a0, m_a1);
7900ea
      fixSidePoint(m_b0, m_b1, previous);
7900ea
    } else
7900ea
    if (&point == &m_a1) {
7900ea
      fixCenter();
7900ea
      fixSidePoint(m_a0, m_a1);
7900ea
      fixSidePoint(m_b0, m_b1);
7900ea
    } else
7900ea
    if (&point == &m_b1) {
7900ea
      fixCenter();
7900ea
      fixSidePoint(m_b0, m_b1);
7900ea
      fixSidePoint(m_a0, m_a1);
7900ea
    }
c7854d
c7854d
    if (&point == &m_grid0) {
c7854d
      fixGrid1(previousCenter, previous);
c7854d
    } else
c7854d
    if (&point != &m_grid1) {
c7854d
      fixGrid1(previousCenter, m_grid0.position);
c7854d
    }
7900ea
  }
7900ea
4da757
  void getGuidelines(
4da757
    const TPointD &position,
4da757
    const TAffine &toTool,
4da757
    TGuidelineList &outGuidelines ) const override
4da757
  {
7900ea
    if (getPassThrough()) {
7900ea
      outGuidelines.push_back(TGuidelineP(
7900ea
        new TGuidelineInfiniteLine(
7900ea
          getEnabled(),
7900ea
          getMagnetism(),
7900ea
          toTool * m_center.position,
7900ea
          position )));
7900ea
    } else {
7900ea
      outGuidelines.push_back(TGuidelineP(
7900ea
        new TGuidelineRay(
7900ea
          getEnabled(),
7900ea
          getMagnetism(),
7900ea
          toTool * m_center.position,
7900ea
          position )));
7900ea
    }
4da757
  }
4da757
c7854d
  void drawSimpleGrid() const {
7c7225
    double alpha = getDrawingGridAlpha();
7c7225
    const TPointD &p = m_center.position;
9a49d4
    double pixelSize = sqrt(tglGetPixelSize2());
9a49d4
    double minStep = 5.0*pixelSize;
7c7225
7c7225
    // calculate rays count and step
7c7225
    TPointD d0 = m_grid0.position - p;
7c7225
    TPointD d1 = m_grid1.position - p;
7c7225
    TPointD dp = d0;
7c7225
    double l = norm(d0);
7c7225
    if (l <= TConsts::epsilon) return;
7c7225
    if (norm2(d1) <= TConsts::epsilon*TConsts::epsilon) return;
7c7225
    double a0 = atan(d0);
7c7225
    double a1 = atan(d1);
7c7225
    double da = fabs(a1 - a0);
7c7225
    if (da > M_PI) da = M_PI - da;
7c7225
    if (da < TConsts::epsilon) da = TConsts::epsilon;
7c7225
    double count = M_2PI/da;
7c7225
    if (count > 1e6) return;
7c7225
    double radiusPart = minStep/(da*l);
7c7225
    if (radiusPart > 1.0) return;
7c7225
    int raysCount = (int)round(count);
7c7225
    double step = M_2PI/(double)raysCount;
c7854d
3f8ca2
    // common data about viewport
3f8ca2
    const TRectD oneBox(-1.0, -1.0, 1.0, 1.0);
c7854d
    TAffine4 modelview, projection;
c7854d
    glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a);
c7854d
    glGetDoublev(GL_PROJECTION_MATRIX, projection.a);
c7854d
    TAffine matrix = (projection*modelview).get2d();
c7854d
    TAffine matrixInv = matrix.inv();
c7854d
c7854d
    // calculate range
c7854d
    if (!(matrixInv*oneBox).contains(p)) {
c7854d
      TPointD corners[4] = {
c7854d
        TPointD(oneBox.x0, oneBox.y0),
c7854d
        TPointD(oneBox.x0, oneBox.y1),
c7854d
        TPointD(oneBox.x1, oneBox.y0),
c7854d
        TPointD(oneBox.x1, oneBox.y1) };
c7854d
      double angles[4];
c7854d
      double a0 = 0.0, a1 = 0.0, da = 0.0;
c7854d
      for(int i = 0; i < 4; ++i) {
c7854d
        angles[i] = atan(matrixInv*corners[i] - p) + M_2PI;
c7854d
        for(int j = 0; j < i; ++j) {
c7854d
          double d = fabs(angles[i] - angles[j]);
3f8ca2
          if (d > M_PI) d = M_2PI - d;
c7854d
          if (d > da) da = d, a0 = angles[i], a1 = angles[j];
c7854d
        }
c7854d
      }
c7854d
      if (a1 < a0) std::swap(a1, a0);
c7854d
      if (a1 - a0 > M_PI) { std::swap(a1, a0); a1 += M_2PI; }
c7854d
      double a = atan(dp) + M_2PI;
c7854d
      a0 = ceil ((a0 - a)/step)*step + a;
c7854d
      a1 = floor((a1 - a)/step)*step + a;
c7854d
c7854d
      double s = sin(a0 - a);
c7854d
      double c = cos(a0 - a);
c7854d
      dp = TPointD(c*dp.x - s*dp.y, s*dp.x + c*dp.y);
c7854d
      raysCount = (int)round((a1 - a0)/step);
c7854d
    }
c7854d
c7854d
    // draw rays
c7854d
    double s = sin(step);
c7854d
    double c = cos(step);
c7854d
    for(int i = 0; i < raysCount; ++i) {
7c7225
      TPointD p0 = matrix*(p + dp*radiusPart);
c7854d
      TPointD p1 = matrix*(p + dp);
7c7225
      if (TGuidelineLineBase::truncateRay(oneBox, p0, p1))
c7854d
        drawSegment(matrixInv*p0, matrixInv*p1, pixelSize, alpha);
c7854d
      dp = TPointD(c*dp.x - s*dp.y, s*dp.x + c*dp.y);
c7854d
    }
c7854d
  }
c7854d
c7854d
  void drawPerspectiveGrid() const {
3f8ca2
    // initial calculations
3f8ca2
    double alpha = getDrawingGridAlpha();
3f8ca2
    const TPointD ¢er = m_center.position;
9a49d4
    double pixelSize = sqrt(tglGetPixelSize2());
9a49d4
    double minStep = 5.0*pixelSize;
3f8ca2
3f8ca2
    TPointD step = m_grid1.position - m_grid0.position;
3f8ca2
    double stepLen2 = norm2(step);
3f8ca2
    double stepLen = sqrt(stepLen2);
3f8ca2
    if (stepLen <= minStep) return;
3f8ca2
    TPointD stepProj = step*(1.0/stepLen2);
3f8ca2
3f8ca2
    TPointD dp = center - m_grid0.position;
3f8ca2
    double startX = dp*stepProj;
3f8ca2
    TPointD zeroPoint = m_grid0.position + step*startX;
3f8ca2
    TPointD cz = zeroPoint - center;
3f8ca2
    double czLen2 = norm2(cz);
3f8ca2
    double czLen = sqrt(czLen2);
3f8ca2
    if (czLen <= TConsts::epsilon) return;
3f8ca2
    TPointD zeroProj = cz*(1.0/czLen2);
3f8ca2
3f8ca2
    double smallK = minStep/stepLen;
3f8ca2
    TPointD smallGrid0 = center - dp*smallK;
3f8ca2
    TPointD smallStep = step*smallK;
3f8ca2
    TPointD smallStepProj = stepProj*(1/smallK);
3f8ca2
3f8ca2
    // common data about viewport
3f8ca2
    const TRectD oneBox(-1.0, -1.0, 1.0, 1.0);
3f8ca2
    TAffine4 modelview, projection;
3f8ca2
    glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a);
3f8ca2
    glGetDoublev(GL_PROJECTION_MATRIX, projection.a);
3f8ca2
    TAffine matrix = (projection*modelview).get2d();
3f8ca2
    TAffine matrixInv = matrix.inv();
3f8ca2
3f8ca2
    // calculate bounds
3f8ca2
    bool found = false;
3f8ca2
    double minx = 0.0, maxx = 0.0;
3f8ca2
    TPointD p0 = matrix*(smallGrid0);
3f8ca2
    TPointD p1 = matrix*(smallGrid0 + smallStep);
3f8ca2
    if (TGuidelineLineBase::truncateInfiniteLine(oneBox, p0, p1)) {
3f8ca2
      p0 = matrixInv*p0;
3f8ca2
      p1 = matrixInv*p1;
3f8ca2
      minx = (p0 - smallGrid0)*smallStepProj;
3f8ca2
      maxx = (p1 - smallGrid0)*smallStepProj;
3f8ca2
      if (maxx < minx) std::swap(maxx, minx);
3f8ca2
      found = true;
3f8ca2
    }
3f8ca2
    if (!oneBox.contains(matrix*center)) {
3f8ca2
      TPointD corners[4] = {
3f8ca2
        TPointD(oneBox.x0, oneBox.y0),
3f8ca2
        TPointD(oneBox.x0, oneBox.y1),
3f8ca2
        TPointD(oneBox.x1, oneBox.y0),
3f8ca2
        TPointD(oneBox.x1, oneBox.y1) };
3f8ca2
      for(int i = 0; i < 4; ++i) {
3f8ca2
        TPointD p = matrixInv*corners[i] - center;
3f8ca2
        double k = p*zeroProj;
3f8ca2
        if (k < TConsts::epsilon) continue;
3f8ca2
        double x = startX + (p*stepProj)/k;
3f8ca2
        if (!found || x < minx) minx = x;
3f8ca2
        if (!found || x > maxx) maxx = x;
3f8ca2
        found = true;
3f8ca2
      }
3f8ca2
      if (maxx <= minx) return;
3f8ca2
    }
3f8ca2
3f8ca2
    // draw grid
3f8ca2
    if (maxx - minx > 1e6) return;
3f8ca2
    for(double x = ceil(minx); x < maxx; ++x) {
3f8ca2
      TPointD p = smallGrid0 + smallStep*x - center;
3f8ca2
      TPointD p0 = matrix*(center + p);
3f8ca2
      TPointD p1 = matrix*(center + p*2.0);
7c7225
      if (TGuidelineLineBase::truncateRay(oneBox, p0, p1))
3f8ca2
        drawSegment(matrixInv*p0, matrixInv*p1, pixelSize, alpha);
3f8ca2
    }
3f8ca2
3f8ca2
    // draw horizon
3f8ca2
    p0 = matrix*(center);
3f8ca2
    p1 = matrix*(center + step);
3f8ca2
    if (TGuidelineLineBase::truncateInfiniteLine(oneBox, p0, p1))
3f8ca2
      drawSegment(matrixInv*p0, matrixInv*p1, pixelSize, alpha);
c7854d
  }
c7854d
8074a5
  void draw(TToolViewer *viewer, bool enabled) const override {
4da757
    double pixelSize = sqrt(tglGetPixelSize2());
7900ea
    const TPointD &p = m_center.position;
4da757
    TPointD dx(20.0*pixelSize, 0.0);
4da757
    TPointD dy(0.0, 10.0*pixelSize);
c7854d
    double alpha = getDrawingAlpha(enabled);
c7854d
    drawSegment(p-dx-dy, p+dx+dy, pixelSize, alpha);
c7854d
    drawSegment(p-dx+dy, p+dx-dy, pixelSize, alpha);
c7854d
    if (getGrid()) {
c7854d
      if (getPerspective())
c7854d
        drawPerspectiveGrid();
c7854d
      else
c7854d
        drawSimpleGrid();
c7854d
    }
4da757
  }
7900ea
7900ea
  void drawEdit(TToolViewer *viewer) const override {
7900ea
    double pixelSize = sqrt(tglGetPixelSize2());
7900ea
    drawSegment(m_center.position, m_a1.position, pixelSize);
7900ea
    drawSegment(m_center.position, m_b1.position, pixelSize);
7900ea
    TAssistant::drawEdit(viewer);
7900ea
  }
4da757
};
249386
7900ea
4df9cd
//*****************************************************************************************
4da757
//    Registration
4df9cd
//*****************************************************************************************
4df9cd
4da757
static TAssistantTypeT<tassistantvanishingpoint> assistantVanishingPoint("assistantVanishingPoint");</tassistantvanishingpoint>