diff --git a/toonz/sources/tnztools/CMakeLists.txt b/toonz/sources/tnztools/CMakeLists.txt index 648a50e..8c77431 100644 --- a/toonz/sources/tnztools/CMakeLists.txt +++ b/toonz/sources/tnztools/CMakeLists.txt @@ -144,10 +144,11 @@ set(SOURCES modifiers/modifiertest.cpp assistants/guidelineline.cpp assistants/guidelineellipse.cpp - assistants/assistantvanishingpoint.cpp + assistants/assistantellipse.cpp + assistants/assistantfisheye.cpp assistants/assistantline.cpp assistants/assistantperspective.cpp - assistants/assistantellipse.cpp + assistants/assistantvanishingpoint.cpp assistants/replicatoraffine.cpp assistants/replicatorgrid.cpp assistants/replicatorjitter.cpp diff --git a/toonz/sources/tnztools/assistants/assistantellipse.cpp b/toonz/sources/tnztools/assistants/assistantellipse.cpp index 260737f..19a2857 100644 --- a/toonz/sources/tnztools/assistants/assistantellipse.cpp +++ b/toonz/sources/tnztools/assistants/assistantellipse.cpp @@ -323,7 +323,7 @@ void TAssistantEllipse::drawConcentricGrid( TAffine screenMatrixInv = screenMatrix.inv(); double pixelSize = sqrt(tglGetPixelSize2()); - double minStep = 20.0*pixelSize; + double minStep = 10.0*pixelSize; TAffine ellipseMatrixInv = ellipseMatrix.inv(); // calculate bounds diff --git a/toonz/sources/tnztools/assistants/assistantfisheye.cpp b/toonz/sources/tnztools/assistants/assistantfisheye.cpp new file mode 100644 index 0000000..cdde562 --- /dev/null +++ b/toonz/sources/tnztools/assistants/assistantfisheye.cpp @@ -0,0 +1,242 @@ + + +// TnzTools includes +#include +#include +#include + +#include "assistantellipse.h" + +// TnzCore includes +#include + +// std includes +#include + + +//***************************************************************************************** +// TAssistantFisheye implementation +//***************************************************************************************** + +class TAssistantFisheye final : public TAssistant { + Q_DECLARE_TR_FUNCTIONS(TAssistantEllipse) +public: + const TStringId m_idCircle; + const TStringId m_idGrid; + +protected: + TAssistantPoint &m_center; + TAssistantPoint &m_a; + TAssistantPoint &m_b; + TAssistantPoint &m_grid0; + TAssistantPoint &m_grid1; + +public: + TAssistantFisheye(TMetaObject &object): + TAssistant(object), + m_idCircle("circle"), + m_idGrid("grid"), + m_center( addPoint("center", TAssistantPoint::CircleCross) ), + m_a( addPoint("a", TAssistantPoint::CircleFill, TPointD(200, 0)) ), + m_b( addPoint("b", TAssistantPoint::Circle, TPointD( 0, 200)) ), + m_grid0( addPoint("grid0", TAssistantPoint::CircleDoubleDots, TPointD( -25, 25)) ), + m_grid1( addPoint("grid1", TAssistantPoint::CircleDots, TPointD( 25, -25)) ) + { + addProperty( new TBoolProperty(m_idCircle.str(), getCircle()) ); + addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) ); + } + + static QString getLocalName() + { return tr("Fish Eye"); } + + void updateTranslation() const override { + TAssistant::updateTranslation(); + setTranslation(m_idCircle, tr("Circle")); + setTranslation(m_idGrid, tr("Grid")); + } + + inline bool getCircle() const + { return data()[m_idCircle].getBool(); } + inline bool getGrid() const + { return data()[m_idGrid].getBool(); } + + void onDataChanged(const TVariant &value) override { + TAssistant::onDataChanged(value); + m_grid0.visible = m_grid1.visible = getGrid(); + if (getCircle() == m_b.visible) { + m_b.visible = !getCircle(); + if (!m_b.visible) + fixBAndGrid(m_center.position, m_a.position, m_b.position); + } + } + +private: + void fixBAndGrid( + TPointD prevCenter, + TPointD prevA, + TPointD prevB ) + { + const TPointD ¢er = m_center.position; + TPointD da0 = prevA - prevCenter; + TPointD da1 = m_a.position - center; + double la0 = norm2(da0); + double la1 = norm2(da1); + if (!(la0 > TConsts::epsilon) || !(la1 > TConsts::epsilon)) + return; + + TPointD db = m_b.position - center; + TPointD dp0 = TPointD(-da0.y, da0.x); + TPointD dp1 = TPointD(-da1.y, da1.x); + if (getCircle()) { + m_b.position = center + (db*dp0 < 0 ? -dp1 : dp1); + } else { + m_b.position = db*dp0/la0*dp1 + center; + } + + TPointD db0 = prevB - prevCenter; + TPointD db1 = m_b.position - center; + double lb0 = norm2(db0); + double lb1 = norm2(db1); + if (!(lb0 > TConsts::epsilon) || !(lb1 > TConsts::epsilon)) + return; + + TPointD dg0 = m_grid0.position - center; + TPointD dg1 = m_grid1.position - center; + m_grid0.position = dg0*da0/la0*da1 + dg0*db0/lb0*db1 + center; + m_grid1.position = dg1*da0/la0*da1 + dg1*db0/lb0*db1 + center; + } + + +public: + void onMovePoint(TAssistantPoint &point, const TPointD &position) override { + TPointD prevCenter = m_center.position; + TPointD prevA = m_a.position; + TPointD prevB = m_b.position; + point.position = position; + if (&point == &m_center) { + TPointD d = m_center.position - prevCenter; + m_a.position += d; + m_b.position += d; + m_grid0.position += d; + m_grid1.position += d; + } else + if (&point == &m_a || &point == &m_b) { + fixBAndGrid(prevCenter, prevA, prevB); + } + } + + TAffine calcEllipseMatrix() const { + TPointD da = m_a.position - m_center.position; + TPointD db = m_b.position - m_center.position; + double r1 = norm(da); + if (r1 <= TConsts::epsilon) return TAffine::zero(); + double r2 = fabs( (rotate90(da)*db)*(1.0/r1) ); + if (r2 <= TConsts::epsilon) return TAffine::zero(); + return TAffine::translation(m_center.position) + * TAffine::rotation(atan(da)) + * TAffine::scale(r1, r2); + } + + void getGuidelines( + const TPointD &position, + const TAffine &toTool, + TGuidelineList &outGuidelines ) const override + { + TAffine matrix = calcEllipseMatrix(); + if (matrix.isZero()) return; + + matrix = toTool*matrix; + TAffine matrixInv = matrix.inv(); + + TPointD p = matrixInv*position; + double l = norm(p); + + if (l > TConsts::epsilon) { + // radius + outGuidelines.push_back(TGuidelineP( + new TGuidelineLine( + getEnabled(), + getMagnetism(), + matrix*TPointD(0, 0), + matrix*(p/l) ))); + } + + if (!(l < 1.0 - TConsts::epsilon)) { + // bound ellipse + outGuidelines.push_back(TGuidelineP( + new TGuidelineEllipse( + getEnabled(), + getMagnetism(), + matrix ))); + } else { + // ellipse scaled by X + double kx = fabs(p.x/sqrt(1.0 - p.y*p.y)); + outGuidelines.push_back(TGuidelineP( + new TGuidelineEllipse( + getEnabled(), + getMagnetism(), + matrix * TAffine::scale(kx, 1.0) ))); + + // ellipse scaled by Y + double ky = fabs(p.y/sqrt(1.0 - p.x*p.x)); + outGuidelines.push_back(TGuidelineP( + new TGuidelineEllipse( + getEnabled(), + getMagnetism(), + matrix * TAffine::scale(1.0, ky) ))); + } + } + +public: + void draw(TToolViewer *viewer, bool enabled) const override { + TAffine ellipseMatrix = calcEllipseMatrix(); + if (ellipseMatrix.isZero()) return; + + // common data about viewport + const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); + TAffine4 modelview, projection; + glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); + glGetDoublev(GL_PROJECTION_MATRIX, projection.a); + TAffine matrix = (projection*modelview).get2d(); + TAffine matrixInv = matrix.inv(); + + double pixelSize = sqrt(tglGetPixelSize2()); + const double crossSize = 0.1; + double alpha = getDrawingAlpha(enabled); + + drawSegment( ellipseMatrix*TPointD(-crossSize, 0.0), + ellipseMatrix*TPointD( crossSize, 0.0), pixelSize, alpha); + drawSegment( ellipseMatrix*TPointD(0.0, -crossSize), + ellipseMatrix*TPointD(0.0, crossSize), pixelSize, alpha); + TAssistantEllipse::drawEllipse(ellipseMatrix, matrixInv, pixelSize, alpha); + + if (getGrid()) { + TAssistantEllipse::drawParallelGrid( + ellipseMatrix, + m_grid0.position, + m_grid1.position, + true, + false, + getDrawingGridAlpha() ); + + std::swap(ellipseMatrix.a11, ellipseMatrix.a12); + std::swap(ellipseMatrix.a21, ellipseMatrix.a22); + + TAssistantEllipse::drawParallelGrid( + ellipseMatrix, + m_grid0.position, + m_grid1.position, + true, + false, + getDrawingGridAlpha() ); + } + } +}; + + +//***************************************************************************************** +// Registration +//***************************************************************************************** + +static TAssistantTypeT assistantFisheye("assistantFisheye"); +