373f71
373f71
373f71
// TnzTools includes
373f71
#include <tools assistant.h=""></tools>
373f71
#include <tools assistants="" guidelineline.h=""></tools>
373f71
#include <tools assistants="" guidelineellipse.h=""></tools>
373f71
373f71
#include "assistantellipse.h"
373f71
373f71
// TnzCore includes
373f71
#include <tgl.h></tgl.h>
373f71
373f71
// std includes
373f71
#include <limits></limits>
373f71
373f71
373f71
//*****************************************************************************************
373f71
//    TAssistantFisheye implementation
373f71
//*****************************************************************************************
373f71
373f71
class TAssistantFisheye final : public TAssistant {
373f71
  Q_DECLARE_TR_FUNCTIONS(TAssistantEllipse)
373f71
public:
373f71
  const TStringId m_idCircle;
373f71
  const TStringId m_idGrid;
373f71
373f71
protected:
373f71
  TAssistantPoint &m_center;
373f71
  TAssistantPoint &m_a;
373f71
  TAssistantPoint &m_b;
373f71
  TAssistantPoint &m_grid0;
373f71
  TAssistantPoint &m_grid1;
373f71
373f71
public:
373f71
  TAssistantFisheye(TMetaObject &object):
373f71
    TAssistant(object),
373f71
    m_idCircle("circle"),
373f71
    m_idGrid("grid"),
373f71
    m_center( addPoint("center", TAssistantPoint::CircleCross) ),
373f71
    m_a( addPoint("a", TAssistantPoint::CircleFill, TPointD(200,   0)) ),
373f71
    m_b( addPoint("b", TAssistantPoint::Circle,     TPointD(  0, 200)) ),
373f71
    m_grid0( addPoint("grid0",  TAssistantPoint::CircleDoubleDots, TPointD( -25,  25)) ),
373f71
    m_grid1( addPoint("grid1",  TAssistantPoint::CircleDots,       TPointD(  25, -25)) )
373f71
  {
373f71
    addProperty( new TBoolProperty(m_idCircle.str(), getCircle()) );
373f71
    addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) );
373f71
  }
373f71
373f71
  static QString getLocalName()
373f71
    { return tr("Fish Eye"); }
373f71
373f71
  void updateTranslation() const override {
373f71
    TAssistant::updateTranslation();
373f71
    setTranslation(m_idCircle, tr("Circle"));
373f71
    setTranslation(m_idGrid, tr("Grid"));
373f71
  }
373f71
373f71
  inline bool getCircle() const
373f71
    { return data()[m_idCircle].getBool(); }
373f71
  inline bool getGrid() const
373f71
    { return data()[m_idGrid].getBool(); }
373f71
373f71
  void onDataChanged(const TVariant &value) override {
373f71
    TAssistant::onDataChanged(value);
373f71
    m_grid0.visible = m_grid1.visible = getGrid();
373f71
    if (getCircle() == m_b.visible) {
373f71
      m_b.visible = !getCircle();
373f71
      if (!m_b.visible)
373f71
        fixBAndGrid(m_center.position, m_a.position, m_b.position);
373f71
    }
373f71
  }
373f71
373f71
private:
373f71
  void fixBAndGrid(
373f71
    TPointD prevCenter,
373f71
    TPointD prevA,
373f71
    TPointD prevB )
373f71
  {
373f71
    const TPointD ¢er = m_center.position;
373f71
    TPointD da0 = prevA - prevCenter;
373f71
    TPointD da1 = m_a.position - center;
373f71
    double la0 = norm2(da0);
373f71
    double la1 = norm2(da1);
373f71
    if (!(la0 > TConsts::epsilon) || !(la1 > TConsts::epsilon))
373f71
      return;
373f71
    
373f71
    TPointD db = m_b.position - center;
373f71
    TPointD dp0 = TPointD(-da0.y, da0.x);
373f71
    TPointD dp1 = TPointD(-da1.y, da1.x);
373f71
    if (getCircle()) {
373f71
      m_b.position = center + (db*dp0 < 0 ? -dp1 : dp1);
373f71
    } else {
373f71
      m_b.position = db*dp0/la0*dp1 + center;
373f71
    }
373f71
373f71
    TPointD db0 = prevB - prevCenter;
373f71
    TPointD db1 = m_b.position - center;
373f71
    double lb0 = norm2(db0);
373f71
    double lb1 = norm2(db1);
373f71
    if (!(lb0 > TConsts::epsilon) || !(lb1 > TConsts::epsilon))
373f71
      return;
373f71
373f71
    TPointD dg0 = m_grid0.position - center;
373f71
    TPointD dg1 = m_grid1.position - center;
373f71
    m_grid0.position = dg0*da0/la0*da1 + dg0*db0/lb0*db1 + center;
373f71
    m_grid1.position = dg1*da0/la0*da1 + dg1*db0/lb0*db1 + center;
373f71
  }
373f71
373f71
373f71
public:
373f71
  void onMovePoint(TAssistantPoint &point, const TPointD &position) override {
373f71
    TPointD prevCenter = m_center.position;
373f71
    TPointD prevA = m_a.position;
373f71
    TPointD prevB = m_b.position;
373f71
    point.position = position;
373f71
    if (&point == &m_center) {
373f71
      TPointD d = m_center.position - prevCenter;
373f71
      m_a.position += d;
373f71
      m_b.position += d;
373f71
      m_grid0.position += d;
373f71
      m_grid1.position += d;
373f71
    } else
373f71
    if (&point == &m_a || &point == &m_b) {
373f71
      fixBAndGrid(prevCenter, prevA, prevB);
373f71
    }
373f71
  }
373f71
373f71
  TAffine calcEllipseMatrix() const {
373f71
    TPointD da = m_a.position - m_center.position;
373f71
    TPointD db = m_b.position - m_center.position;
373f71
    double r1 = norm(da);
373f71
    if (r1 <= TConsts::epsilon) return TAffine::zero();
373f71
    double r2 = fabs( (rotate90(da)*db)*(1.0/r1) );
373f71
    if (r2 <= TConsts::epsilon) return TAffine::zero();
373f71
    return TAffine::translation(m_center.position)
373f71
         * TAffine::rotation(atan(da))
373f71
         * TAffine::scale(r1, r2);
373f71
  }
373f71
373f71
  void getGuidelines(
373f71
    const TPointD &position,
373f71
    const TAffine &toTool,
373f71
    TGuidelineList &outGuidelines ) const override
373f71
  {
373f71
    TAffine matrix = calcEllipseMatrix();
373f71
    if (matrix.isZero()) return;
373f71
373f71
    matrix = toTool*matrix;
373f71
    TAffine matrixInv = matrix.inv();
373f71
373f71
    TPointD p = matrixInv*position;
373f71
    double l = norm(p);
373f71
    
373f71
    if (l > TConsts::epsilon) {
373f71
      // radius
373f71
      outGuidelines.push_back(TGuidelineP(
373f71
        new TGuidelineLine(
373f71
          getEnabled(),
373f71
          getMagnetism(),
373f71
          matrix*TPointD(0, 0),
373f71
          matrix*(p/l) )));
373f71
    }
373f71
    
373f71
    if (!(l < 1.0 - TConsts::epsilon)) {
373f71
      // bound ellipse
373f71
      outGuidelines.push_back(TGuidelineP(
373f71
        new TGuidelineEllipse(
373f71
          getEnabled(),
373f71
          getMagnetism(),
373f71
          matrix )));
373f71
    } else {
373f71
      // ellipse scaled by X
373f71
      double kx = fabs(p.x/sqrt(1.0 - p.y*p.y));
373f71
      outGuidelines.push_back(TGuidelineP(
373f71
        new TGuidelineEllipse(
373f71
          getEnabled(),
373f71
          getMagnetism(),
373f71
          matrix * TAffine::scale(kx, 1.0) )));
373f71
373f71
      // ellipse scaled by Y
373f71
      double ky = fabs(p.y/sqrt(1.0 - p.x*p.x));
373f71
      outGuidelines.push_back(TGuidelineP(
373f71
        new TGuidelineEllipse(
373f71
          getEnabled(),
373f71
          getMagnetism(),
373f71
          matrix * TAffine::scale(1.0, ky) )));
373f71
    }
373f71
  }
373f71
373f71
public:
373f71
  void draw(TToolViewer *viewer, bool enabled) const override {
373f71
    TAffine ellipseMatrix = calcEllipseMatrix();
373f71
    if (ellipseMatrix.isZero()) return;
373f71
373f71
    // common data about viewport
373f71
    const TRectD oneBox(-1.0, -1.0, 1.0, 1.0);
373f71
    TAffine4 modelview, projection;
373f71
    glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a);
373f71
    glGetDoublev(GL_PROJECTION_MATRIX, projection.a);
373f71
    TAffine matrix = (projection*modelview).get2d();
373f71
    TAffine matrixInv = matrix.inv();
373f71
373f71
    double pixelSize = sqrt(tglGetPixelSize2());
373f71
    const double crossSize = 0.1;
373f71
    double alpha = getDrawingAlpha(enabled);
373f71
    
373f71
    drawSegment( ellipseMatrix*TPointD(-crossSize, 0.0),
373f71
                 ellipseMatrix*TPointD( crossSize, 0.0), pixelSize, alpha);
373f71
    drawSegment( ellipseMatrix*TPointD(0.0, -crossSize),
373f71
                 ellipseMatrix*TPointD(0.0,  crossSize), pixelSize, alpha);
373f71
    TAssistantEllipse::drawEllipse(ellipseMatrix, matrixInv, pixelSize, alpha);
373f71
373f71
    if (getGrid()) {
373f71
      TAssistantEllipse::drawParallelGrid(
373f71
        ellipseMatrix,
373f71
        m_grid0.position,
373f71
        m_grid1.position,
373f71
        true,
373f71
        false,
373f71
        getDrawingGridAlpha() );
373f71
      
373f71
      std::swap(ellipseMatrix.a11, ellipseMatrix.a12);
373f71
      std::swap(ellipseMatrix.a21, ellipseMatrix.a22);
373f71
      
373f71
      TAssistantEllipse::drawParallelGrid(
373f71
        ellipseMatrix,
373f71
        m_grid0.position,
373f71
        m_grid1.position,
373f71
        true,
373f71
        false,
373f71
        getDrawingGridAlpha() );
373f71
    }
373f71
  }
373f71
};
373f71
373f71
373f71
//*****************************************************************************************
373f71
//    Registration
373f71
//*****************************************************************************************
373f71
373f71
static TAssistantTypeT<tassistantfisheye> assistantFisheye("assistantFisheye");</tassistantfisheye>
373f71