diff --git a/toonz/sources/common/tgeometry/tgeometry.cpp b/toonz/sources/common/tgeometry/tgeometry.cpp index ec1f3ae..7724948 100644 --- a/toonz/sources/common/tgeometry/tgeometry.cpp +++ b/toonz/sources/common/tgeometry/tgeometry.cpp @@ -61,9 +61,8 @@ TAffine TAffine::inv() const { (a22 == 0.0 ? std::numeric_limits::max() / (1 << 16) : 1.0 / a22); return TAffine(inv_a11, 0, -a13 * inv_a11, 0, inv_a22, -a23 * inv_a22); - } else if (a11 == 0.0 && a22 == 0.0) { - assert(a12 != 0.0); - assert(a21 != 0.0); + } else + if (a11 == 0.0 && a22 == 0.0) { double inv_a21 = (a21 == 0.0 ? std::numeric_limits::max() / (1 << 16) : 1.0 / a21); diff --git a/toonz/sources/tnztools/CMakeLists.txt b/toonz/sources/tnztools/CMakeLists.txt index b044977..648a50e 100644 --- a/toonz/sources/tnztools/CMakeLists.txt +++ b/toonz/sources/tnztools/CMakeLists.txt @@ -33,6 +33,7 @@ set(HEADERS stylepickertool.h toonzvectorbrushtool.h vectorselectiontool.h + assistants/assistantellipse.h assistants/assistantline.h assistants/assistantvanishingpoint.h ../include/tools/RGBpicker.h diff --git a/toonz/sources/tnztools/assistants/assistantellipse.cpp b/toonz/sources/tnztools/assistants/assistantellipse.cpp index 29e2060..260737f 100644 --- a/toonz/sources/tnztools/assistants/assistantellipse.cpp +++ b/toonz/sources/tnztools/assistants/assistantellipse.cpp @@ -1,528 +1,558 @@ // TnzTools includes -#include -#include -#include - -// TnzCore includes -#include - -// std includes -#include +#include "assistantellipse.h" //***************************************************************************************** // TAssistantEllipse implementation //***************************************************************************************** -class TAssistantEllipse final : public TAssistant { - Q_DECLARE_TR_FUNCTIONS(TAssistantEllipse) -public: - const TStringId m_idRestricktA; - const TStringId m_idRestricktB; - const TStringId m_idRepeat; - const TStringId m_idGrid; - const TStringId m_idPerspective; - -protected: - TAssistantPoint &m_center; - TAssistantPoint &m_a; - TAssistantPoint &m_b; - TAssistantPoint &m_grid0; - TAssistantPoint &m_grid1; - -public: - TAssistantEllipse(TMetaObject &object): - TAssistant(object), - m_idRestricktA("restrictA"), - m_idRestricktB("restrictB"), - m_idRepeat("repeat"), - m_idGrid("grid"), - m_idPerspective("perspective"), - m_center( addPoint("center", TAssistantPoint::CircleCross) ), - m_a( addPoint("a", TAssistantPoint::CircleFill, TPointD(100.0, 0.0)) ), - m_b( addPoint("b", TAssistantPoint::Circle, TPointD(0.0, 50.0)) ), - m_grid0( addPoint("grid0", TAssistantPoint::CircleDoubleDots, TPointD( 0.0,-50.0)) ), - m_grid1( addPoint("grid1", TAssistantPoint::CircleDots, TPointD( 25.0,-75.0)) ) - { - addProperty( new TBoolProperty(m_idRestricktA.str(), getRestrictA()) ); - addProperty( new TBoolProperty(m_idRestricktB.str(), getRestrictB()) ); - addProperty( new TBoolProperty(m_idRepeat.str(), getRepeat()) ); - addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) ); - addProperty( new TBoolProperty(m_idPerspective.str(), getPerspective()) ); +TAssistantEllipse::TAssistantEllipse(TMetaObject &object): + TAssistant(object), + m_idCircle("circle"), + m_idRestrictA("restrictA"), + m_idRestrictB("restrictB"), + m_idRepeat("repeat"), + m_idGrid("grid"), + m_idPerspective("perspective"), + m_center( addPoint("center", TAssistantPoint::CircleCross) ), + m_a( addPoint("a", TAssistantPoint::CircleFill, TPointD(150, 0)) ), + m_b( addPoint("b", TAssistantPoint::Circle, TPointD( 0, 100)) ), + m_grid0( addPoint("grid0", TAssistantPoint::CircleDoubleDots, TPointD( 10, -30)) ), + m_grid1( addPoint("grid1", TAssistantPoint::CircleDots, TPointD( 60, -60)) ) +{ + addProperty( new TBoolProperty(m_idCircle.str(), getCircle()) ); + addProperty( new TBoolProperty(m_idRestrictA.str(), getRestrictA()) ); + addProperty( new TBoolProperty(m_idRestrictB.str(), getRestrictB()) ); + addProperty( new TBoolProperty(m_idRepeat.str(), getRepeat()) ); + addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) ); + addProperty( new TBoolProperty(m_idPerspective.str(), getPerspective()) ); +} + + +QString TAssistantEllipse::getLocalName() + { return tr("Ellipse"); } + + +void TAssistantEllipse::updateTranslation() const { + TAssistant::updateTranslation(); + setTranslation(m_idCircle, tr("Circle")); + setTranslation(m_idRestrictA, tr("Restrict A")); + setTranslation(m_idRestrictB, tr("Restrict B")); + setTranslation(m_idRepeat, tr("Repeat")); + setTranslation(m_idGrid, tr("Grid")); + setTranslation(m_idPerspective, tr("Perspective")); +} + + +void TAssistantEllipse::onDataChanged(const TVariant &value) { + 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); } - - static QString getLocalName() - { return tr("Ellipse"); } - - void updateTranslation() const override { - TAssistant::updateTranslation(); - setTranslation(m_idRestricktA, tr("Restrict A")); - setTranslation(m_idRestricktB, tr("Restrict B")); - setTranslation(m_idRepeat, tr("Repeat")); - setTranslation(m_idGrid, tr("Grid")); - setTranslation(m_idPerspective, tr("Perspective")); +} + + +void TAssistantEllipse::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; } - inline bool getRestrictA() const - { return data()[m_idRestricktA].getBool(); } - inline bool getRestrictB() const - { return data()[m_idRestricktB].getBool(); } - inline bool getRepeat() const - { return data()[m_idRepeat].getBool(); } - inline bool getGrid() const - { return data()[m_idGrid].getBool(); } - inline bool getPerspective() const - { return data()[m_idPerspective].getBool(); } - - void onDataChanged(const TVariant &value) override { - TAssistant::onDataChanged(value); - m_grid0.visible = m_grid1.visible = getGrid(); + 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; +} + + +void TAssistantEllipse::onMovePoint(TAssistantPoint &point, const TPointD &position) { + 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); } - -private: - void fixBAndGgid1(const TPointD &previousCenter, const TPointD &previousA) { - TPointD dx = previousA - previousCenter; - double l = norm2(dx); - if (l <= TConsts::epsilon*TConsts::epsilon) return; - dx = dx*(1.0/sqrt(l)); - TPointD dy(-dx.y, dx.x); - - double r2 = dy*(m_b.position - m_center.position); - - TPointD g1 = m_grid1.position - m_grid0.position; - g1 = TPointD(dx*g1, dy*g1); - - dx = m_a.position - m_center.position; - l = norm2(dx); - if (l <= TConsts::epsilon*TConsts::epsilon) return; - dx = dx*(1.0/sqrt(l)); - dy = TPointD(-dx.y, dx.x); - - m_grid1.position = m_grid0.position + dx*g1.x + dy*g1.y; - m_b.position = m_center.position + dy*r2; - } - -public: - void onMovePoint(TAssistantPoint &point, const TPointD &position) override { - TPointD previousCenter = m_center.position; - TPointD previousA = m_a.position; - point.position = position; - if (&point == &m_center) { - m_a.position += m_center.position - previousCenter; - m_b.position += m_center.position - previousCenter; - } else - if (&point == &m_a || &point == &m_b) - fixBAndGgid1(previousCenter, previousA); +} + + +TAffine TAssistantEllipse::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 TAssistantEllipse::getGuidelines( + const TPointD &position, + const TAffine &toTool, + TGuidelineList &outGuidelines ) const +{ + bool restrictA = getRestrictA(); + bool restrictB = getRestrictB(); + bool repeat = getRepeat(); + + TAffine matrix = calcEllipseMatrix(); + if (matrix.isZero()) return; + if (!restrictA && restrictB) { + std::swap(matrix.a11, matrix.a12); + std::swap(matrix.a21, matrix.a22); + std::swap(restrictA, restrictB); } - 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 - { - bool restrictA = getRestrictA(); - bool restrictB = getRestrictB(); - bool repeat = getRepeat(); - - TAffine matrix = calcEllipseMatrix(); - if (matrix.isZero()) return; - if (!restrictA && restrictB) { - std::swap(matrix.a11, matrix.a12); - std::swap(matrix.a21, matrix.a22); - std::swap(restrictA, restrictB); + matrix = toTool*matrix; + TAffine matrixInv = matrix.inv(); + + if (restrictA && restrictB) { + // ellipse + outGuidelines.push_back(TGuidelineP( + new TGuidelineEllipse( + getEnabled(), + getMagnetism(), + matrix, + matrixInv ))); + } else + if (!restrictA && !restrictB) { + // scaled ellipse + TPointD p = matrixInv*position; + double l = norm(p); + outGuidelines.push_back(TGuidelineP( + new TGuidelineEllipse( + getEnabled(), + getMagnetism(), + matrix * TAffine::scale(l) ))); + } else { // restrictA + TPointD p = matrixInv*position; + if (repeat) { + double ox = round(0.5*p.x)*2.0; + p.x -= ox; + matrix *= TAffine::translation(ox, 0.0); } - matrix = toTool*matrix; - TAffine matrixInv = matrix.inv(); - - if (restrictA && restrictB) { - // ellipse + // scale by Y + if (p.x <= TConsts::epsilon - 1.0) { + // line x = -1 outGuidelines.push_back(TGuidelineP( - new TGuidelineEllipse( + new TGuidelineInfiniteLine( getEnabled(), getMagnetism(), - matrix, - matrixInv ))); + matrix*TPointD(-1.0, 0.0), + matrix*TPointD(-1.0, 1.0) ))); } else - if (!restrictA && !restrictB) { - // scaled ellipse - TPointD p = matrixInv*position; - double l = norm(p); + if (p.x >= 1.0 - TConsts::epsilon) { + // line x = 1 + outGuidelines.push_back(TGuidelineP( + new TGuidelineInfiniteLine( + getEnabled(), + getMagnetism(), + matrix*TPointD(1.0, 0.0), + matrix*TPointD(1.0, 1.0) ))); + } else { + // ellipse scaled by Y + double k = fabs(p.y/sqrt(1.0 - p.x*p.x)); outGuidelines.push_back(TGuidelineP( new TGuidelineEllipse( getEnabled(), getMagnetism(), - matrix * TAffine::scale(l) ))); - } else { // restrictA - TPointD p = matrixInv*position; - if (repeat) { - double ox = round(0.5*p.x)*2.0; - p.x -= ox; - matrix *= TAffine::translation(ox, 0.0); - } - - // scale by Y - if (p.x <= TConsts::epsilon - 1.0) { - // line x = -1 - outGuidelines.push_back(TGuidelineP( - new TGuidelineInfiniteLine( - getEnabled(), - getMagnetism(), - matrix*TPointD(-1.0, 0.0), - matrix*TPointD(-1.0, 1.0) ))); - } else - if (p.x >= 1.0 - TConsts::epsilon) { - // line x = 1 - outGuidelines.push_back(TGuidelineP( - new TGuidelineInfiniteLine( - getEnabled(), - getMagnetism(), - matrix*TPointD(1.0, 0.0), - matrix*TPointD(1.0, 1.0) ))); - } else { - // ellipse scaled by Y - double k = fabs(p.y/sqrt(1.0 - p.x*p.x)); - outGuidelines.push_back(TGuidelineP( - new TGuidelineEllipse( - getEnabled(), - getMagnetism(), - matrix * TAffine::scale(1.0, k) ))); - } + matrix * TAffine::scale(1.0, k) ))); } } - -private: - void drawEllipseRanges( - const TAngleRangeSet &ranges, - const TAffine &ellipseMatrix, - const TAffine &screenMatrixInv, - double pixelSize, - double alpha ) const - { - assert(ranges.check()); - TAngleRangeSet actualRanges(ranges); - const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); - if (!TGuidelineEllipse::truncateEllipse(actualRanges, ellipseMatrix.inv()*screenMatrixInv, oneBox)) - return; - assert(actualRanges.check()); - - int segments = TGuidelineEllipse::calcSegmentsCount(ellipseMatrix, pixelSize); - double da = M_2PI/segments; - double s = sin(da); - double c = cos(da); - - for(TAngleRangeSet::Iterator i(actualRanges); i; ++i) { - double a0 = i.d0(); - double a1 = i.d1greater(); - int cnt = (int)floor((a1 - a0)/da); - TPointD r(cos(a0), sin(a0)); - TPointD p0 = ellipseMatrix*r; - for(int j = 0; j < cnt; ++j) { - r = TPointD(r.x*c - r.y*s, r.y*c + r.x*s); - TPointD p1 = ellipseMatrix*r; - drawSegment(p0, p1, pixelSize, alpha); - p0 = p1; - } - drawSegment(p0, ellipseMatrix*TPointD(cos(a1), sin(a1)), pixelSize, alpha); +} + + +void TAssistantEllipse::drawEllipseRanges( + const TAngleRangeSet &ranges, + const TAffine &ellipseMatrix, + const TAffine &screenMatrixInv, + double pixelSize, + double alpha ) +{ + assert(ranges.check()); + TAngleRangeSet actualRanges(ranges); + const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); + if (!TGuidelineEllipse::truncateEllipse(actualRanges, ellipseMatrix.inv()*screenMatrixInv, oneBox)) + return; + assert(actualRanges.check()); + + int segments = TGuidelineEllipse::calcSegmentsCount(ellipseMatrix, pixelSize); + double da = M_2PI/segments; + double s = sin(da); + double c = cos(da); + + for(TAngleRangeSet::Iterator i(actualRanges); i; ++i) { + double a0 = i.d0(); + double a1 = i.d1greater(); + int cnt = (int)floor((a1 - a0)/da); + TPointD r(cos(a0), sin(a0)); + TPointD p0 = ellipseMatrix*r; + for(int j = 0; j < cnt; ++j) { + r = TPointD(r.x*c - r.y*s, r.y*c + r.x*s); + TPointD p1 = ellipseMatrix*r; + drawSegment(p0, p1, pixelSize, alpha); + p0 = p1; } + drawSegment(p0, ellipseMatrix*TPointD(cos(a1), sin(a1)), pixelSize, alpha); } - - void drawEllipse( - const TAffine &ellipseMatrix, - const TAffine &screenMatrixInv, - double pixelSize, - double alpha ) const - { drawEllipseRanges(TAngleRangeSet(true), ellipseMatrix, screenMatrixInv, pixelSize, alpha); } - - void drawRuler(const TAffine &ellipseMatrix, double pixelSize) const { - double minStep = 10.0*pixelSize; - double alpha = getDrawingAlpha(); - - TAffine em = ellipseMatrix; - TAffine ellipseMatrixInv = ellipseMatrix.inv(); - TPointD g0 = ellipseMatrixInv*m_grid0.position; - TPointD g1 = ellipseMatrixInv*m_grid1.position; - if (norm2(g0) <= TConsts::epsilon*TConsts::epsilon) return; - if (norm2(g1) <= TConsts::epsilon*TConsts::epsilon) return; - double ga0 = atan(g0); - double ga1 = atan(g1); - - // x and y radiuses - TPointD r( norm2(TPointD(em.a11, em.a21)), norm2(TPointD(em.a12, em.a22)) ); - double avgR = 0.5*(r.x + r.y); - if (avgR <= TConsts::epsilon*TConsts::epsilon) return; - avgR = sqrt(avgR); - double actualMinStep = minStep/avgR; - r.x = sqrt(r.x); - r.y = sqrt(r.y); - - // remove radiuses from ellipse matrix - double rkx = r.x > TConsts::epsilon ? 1.0/r.x : 0.0; - double rky = r.y > TConsts::epsilon ? 1.0/r.y : 0.0; - em.a11 *= rkx; em.a21 *= rkx; - em.a12 *= rky; em.a22 *= rky; - - if (getPerspective()) { - // draw perspective - if (ga0 < 0.0) { if (ga1 > 0.0) ga1 -= M_2PI; } - else { if (ga1 < 0.0) ga1 += M_2PI; } - double k = 0.0, begin = 0.0, end = 0.0; - if (!calcPerspectiveStep(actualMinStep, 0.0, M_2PI, 0.0, fabs(ga0), ga1, k, begin, end)) return; - for(double a = begin; fabs(a) < fabs(end); a *= k) { - TPointD p( cos(a), (ga0 < 0.0 ? -1.0 : 1.0)*sin(a) ); - TPointD n( p.x*r.y, p.y*r.x ); // perp to allipse - double nl2 = norm2(n); - if (nl2 > TConsts::epsilon*TConsts::epsilon) { - p.x *= r.x; - p.y *= r.y; - n = n*(1.0/sqrt(nl2)); - drawMark(em*p, em.transformDirection(n), pixelSize, alpha); - } +} + + +void TAssistantEllipse::drawRuler( + const TAffine &ellipseMatrix, + const TPointD &grid0, + const TPointD &grid1, + bool perspective, + double alpha +) { + double pixelSize = sqrt(tglGetPixelSize2()); + double minStep = 10.0*pixelSize; + + TAffine em = ellipseMatrix; + TAffine ellipseMatrixInv = ellipseMatrix.inv(); + TPointD g0 = ellipseMatrixInv * grid0; + TPointD g1 = ellipseMatrixInv * grid1; + if (norm2(g0) <= TConsts::epsilon*TConsts::epsilon) return; + if (norm2(g1) <= TConsts::epsilon*TConsts::epsilon) return; + double ga0 = atan(g0); + double ga1 = atan(g1); + + // x and y radiuses + TPointD r( norm2(TPointD(em.a11, em.a21)), norm2(TPointD(em.a12, em.a22)) ); + double avgR = 0.5*(r.x + r.y); + if (avgR <= TConsts::epsilon*TConsts::epsilon) return; + avgR = sqrt(avgR); + double actualMinStep = minStep/avgR; + r.x = sqrt(r.x); + r.y = sqrt(r.y); + + // remove radiuses from ellipse matrix + double rkx = r.x > TConsts::epsilon ? 1.0/r.x : 0.0; + double rky = r.y > TConsts::epsilon ? 1.0/r.y : 0.0; + em.a11 *= rkx; em.a21 *= rkx; + em.a12 *= rky; em.a22 *= rky; + + if (perspective) { + // draw perspective + if (ga0 < 0.0) { if (ga1 > 0.0) ga1 -= M_2PI; } + else { if (ga1 < 0.0) ga1 += M_2PI; } + double k = 0.0, begin = 0.0, end = 0.0; + if (!calcPerspectiveStep(actualMinStep, 0.0, M_2PI, 0.0, fabs(ga0), ga1, k, begin, end)) return; + for(double a = begin; fabs(a) < fabs(end); a *= k) { + TPointD p( cos(a), (ga0 < 0.0 ? -1.0 : 1.0)*sin(a) ); + TPointD n( p.x*r.y, p.y*r.x ); // perp to allipse + double nl2 = norm2(n); + if (nl2 > TConsts::epsilon*TConsts::epsilon) { + p.x *= r.x; + p.y *= r.y; + n = n*(1.0/sqrt(nl2)); + drawMark(em*p, em.transformDirection(n), pixelSize, alpha); } - } else { - // draw linear - double da = ga1 - ga0; - if (da < 0.0) { da = -da; std::swap(ga0, ga1); } - if (ga1 - ga0 > M_PI) { da = M_2PI - da; std::swap(ga0, ga1); } - if (da < actualMinStep) return; - for(double a = ga0 - floor(M_PI/da)*da; a < ga0 + M_PI; a += da) { - TPointD p( cos(a), sin(a) ); - TPointD n( p.x*r.y, p.y*r.x ); // perp to allipse - double nl2 = norm2(n); - if (nl2 > TConsts::epsilon*TConsts::epsilon) { - p.x *= r.x; - p.y *= r.y; - n = n*(1.0/sqrt(nl2)); - drawMark(em*p, em.transformDirection(n), pixelSize, alpha); - } + } + } else { + // draw linear + double da = ga1 - ga0; + if (da < 0.0) { da = -da; std::swap(ga0, ga1); } + if (ga1 - ga0 > M_PI) { da = M_2PI - da; std::swap(ga0, ga1); } + if (da < actualMinStep) return; + for(double a = ga0 - floor(M_PI/da)*da; a < ga0 + M_PI; a += da) { + TPointD p( cos(a), sin(a) ); + TPointD n( p.x*r.y, p.y*r.x ); // perp to allipse + double nl2 = norm2(n); + if (nl2 > TConsts::epsilon*TConsts::epsilon) { + p.x *= r.x; + p.y *= r.y; + n = n*(1.0/sqrt(nl2)); + drawMark(em*p, em.transformDirection(n), pixelSize, alpha); } } } - - void drawConcentricGrid( - const TAffine &ellipseMatrix, - const TAffine &screenMatrixInv, - double pixelSize ) const - { - double minStep = 20.0*pixelSize; - double alpha = getDrawingGridAlpha(); - TAffine ellipseMatrixInv = ellipseMatrix.inv(); - - // calculate bounds - TAffine matrixInv = ellipseMatrixInv * screenMatrixInv; - TPointD o = matrixInv * TPointD(-1.0, -1.0); - TPointD dx = matrixInv.transformDirection( TPointD(2.0, 0.0) ); - TPointD dy = matrixInv.transformDirection( TPointD(0.0, 2.0) ); - double max = 0.0; - double min = std::numeric_limits::infinity(); - - // distance to points - TPointD corners[] = { o, o+dx, o+dx+dy, o+dy }; - for(int i = 0; i < 4; ++i) { - double k = norm(corners[i]); - if (k < min) min = k; - if (k > max) max = k; - } - - // distance to sides - TPointD lines[] = { dx, dy, -1.0*dx, -1.0*dy }; - int positive = 0, negative = 0; - for(int i = 0; i < 4; ++i) { - double len2 = norm2(lines[i]); - if (len2 <= TConsts::epsilon*TConsts::epsilon) continue; - double k = (corners[i]*rotate90(lines[i]))/sqrt(len2); - if (k > TConsts::epsilon) ++positive; - if (k < TConsts::epsilon) ++negative; - double l = -(corners[i]*lines[i]); - if (l <= TConsts::epsilon || l >= len2 - TConsts::epsilon) continue; - k = fabs(k); - if (k < min) min = k; - if (k > max) max = k; - } - - // if center is inside bounds - if (min < 0.0 || positive == 0 || negative == 0) min = 0.0; - if (max <= min) return; - - // draw - const TAffine &em = ellipseMatrix; - double r = sqrt(0.5*(norm2(TPointD(em.a11, em.a21)) + norm2(TPointD(em.a12, em.a22)))); - double actualMinStep = minStep/r; - double gs0 = norm(ellipseMatrixInv*m_grid0.position); - double gs1 = norm(ellipseMatrixInv*m_grid1.position); - if (gs0 <= TConsts::epsilon*TConsts::epsilon) return; - if (gs1 <= TConsts::epsilon*TConsts::epsilon) return; - - if (getPerspective()) { - // draw perspective - double k = 0.0, begin = 0.0, end = 0.0; - if (!calcPerspectiveStep(actualMinStep, min, max, 0.0, gs0, gs1, k, begin, end)) return; - for(double x = begin; fabs(x) < fabs(end); x *= k) - drawEllipse(ellipseMatrix * TAffine::scale(x), screenMatrixInv, pixelSize, alpha); - } else { - // draw linear - double dx = fabs(gs1 - gs0); - if (dx*r < minStep) return; - for(double x = gs0 + ceil((min - gs0)/dx)*dx; x < max; x += dx) - drawEllipse(ellipseMatrix * TAffine::scale(x), screenMatrixInv, pixelSize, alpha); - } +} + + +void TAssistantEllipse::drawConcentricGrid( + const TAffine &ellipseMatrix, + const TPointD &grid0, + const TPointD &grid1, + bool perspective, + double alpha ) +{ + TAffine4 modelview, projection; + glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); + glGetDoublev(GL_PROJECTION_MATRIX, projection.a); + TAffine screenMatrix = (projection*modelview).get2d(); + TAffine screenMatrixInv = screenMatrix.inv(); + + double pixelSize = sqrt(tglGetPixelSize2()); + double minStep = 20.0*pixelSize; + TAffine ellipseMatrixInv = ellipseMatrix.inv(); + + // calculate bounds + TAffine matrixInv = ellipseMatrixInv * screenMatrixInv; + TPointD o = matrixInv * TPointD(-1.0, -1.0); + TPointD dx = matrixInv.transformDirection( TPointD(2.0, 0.0) ); + TPointD dy = matrixInv.transformDirection( TPointD(0.0, 2.0) ); + double max = 0.0; + double min = std::numeric_limits::infinity(); + + // distance to points + TPointD corners[] = { o, o+dx, o+dx+dy, o+dy }; + for(int i = 0; i < 4; ++i) { + double k = norm(corners[i]); + if (k < min) min = k; + if (k > max) max = k; } - void drawParallelGrid( - const TAffine &ellipseMatrix, - const TAffine &screenMatrixInv, - double pixelSize ) const - { - double minStep = 10.0*pixelSize; - double alpha = getDrawingGridAlpha(); - TAffine ellipseMatrixInv = ellipseMatrix.inv(); - - const TAffine &em = ellipseMatrix; - double r = sqrt(0.5*(norm2(TPointD(em.a11, em.a21)) + norm2(TPointD(em.a12, em.a22)))); - double actualMinStep = minStep/r; - TPointD g0 = ellipseMatrixInv*m_grid0.position; - TPointD g1 = ellipseMatrixInv*m_grid1.position; - if (getRepeat()) - { g0.x -= round(0.5*g0.x)*2.0; g1.x -= round(0.5*g1.x)*2.0; } - if (fabs(g0.x) >= 1.0 - TConsts::epsilon) return; - if (fabs(g1.x) >= 1.0 - TConsts::epsilon) return; - double gs0 = g0.y/sqrt(1.0 - g0.x*g0.x); - double gs1 = g1.y/sqrt(1.0 - g1.x*g1.x); - if (fabs(gs0) >= 1.0 - TConsts::epsilon) return; - if (fabs(gs1) >= 1.0 - TConsts::epsilon) return; - - TAngleRangeSet ranges; - ranges.add( TAngleRangeSet::fromDouble(0.0), TAngleRangeSet::fromDouble(M_PI) ); - - if (getPerspective()) { - // draw perspective (actually angular) - double k = 0.0, begin = 0.0, end = 0.0; - double a0 = asin(gs0); - double a1 = asin(gs1); - double da = fabs(a1 - a0); - if (fabs(sin(da)) < 2.0*actualMinStep) return; - for(double a = a0 + ceil((-M_PI_2 - a0)/da)*da; a < M_PI_2; a += da) - drawEllipseRanges( - ranges, - ellipseMatrix*TAffine::scale(a < 0.0 ? -1.0 : 1.0, sin(a)), - screenMatrixInv, - pixelSize, - alpha ); - } else { - // draw linear - double dx = fabs(gs1 - gs0); - if (dx < actualMinStep) return; - for(double x = gs0 + ceil((-1.0 - gs0)/dx)*dx; x < 1.0; x += dx) - drawEllipseRanges( - ranges, - ellipseMatrix*TAffine::scale(x < 0.0 ? -1.0 : 1.0, x), - screenMatrixInv, - pixelSize, - alpha ); - } + // distance to sides + TPointD lines[] = { dx, dy, -1.0*dx, -1.0*dy }; + int positive = 0, negative = 0; + for(int i = 0; i < 4; ++i) { + double len2 = norm2(lines[i]); + if (len2 <= TConsts::epsilon*TConsts::epsilon) continue; + double k = (corners[i]*rotate90(lines[i]))/sqrt(len2); + if (k > TConsts::epsilon) ++positive; + if (k < TConsts::epsilon) ++negative; + double l = -(corners[i]*lines[i]); + if (l <= TConsts::epsilon || l >= len2 - TConsts::epsilon) continue; + k = fabs(k); + if (k < min) min = k; + if (k > max) max = k; } - void draw( - const TAffine &ellipseMatrix, - const TAffine &screenMatrixInv, - double ox, - double pixelSize, - bool enabled ) const - { - const double crossSize = 0.1; - - double alpha = getDrawingAlpha(enabled); - bool grid = getGrid(); - bool ruler = getRestrictA() && getRestrictB(); - bool concentric = !getRestrictA() && !getRestrictB(); - - 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); - drawEllipse(ellipseMatrix, screenMatrixInv, pixelSize, alpha); - if (ox > 1.0) - drawSegment( ellipseMatrix*TPointD(-1.0, -1.0), - ellipseMatrix*TPointD(-1.0, 1.0), pixelSize, alpha); - else if (ox < -1.0) - drawSegment( ellipseMatrix*TPointD( 1.0, -1.0), - ellipseMatrix*TPointD( 1.0, 1.0), pixelSize, alpha); - - if (!grid) return; - - if (ruler) { - drawRuler(ellipseMatrix, pixelSize); - } else - if (concentric) { - drawConcentricGrid(ellipseMatrix, screenMatrixInv, pixelSize); - } else { - drawParallelGrid(ellipseMatrix, screenMatrixInv, pixelSize); - } + // if center is inside bounds + if (min < 0.0 || positive == 0 || negative == 0) min = 0.0; + if (max <= min) return; + + // draw + const TAffine &em = ellipseMatrix; + double r = sqrt(0.5*(norm2(TPointD(em.a11, em.a21)) + norm2(TPointD(em.a12, em.a22)))); + double actualMinStep = minStep/r; + double gs0 = norm(ellipseMatrixInv*grid0); + double gs1 = norm(ellipseMatrixInv*grid1); + if (gs0 <= TConsts::epsilon*TConsts::epsilon) return; + if (gs1 <= TConsts::epsilon*TConsts::epsilon) return; + + if (perspective) { + // draw perspective + double k = 0.0, begin = 0.0, end = 0.0; + if (!calcPerspectiveStep(actualMinStep, min, max, 0.0, gs0, gs1, k, begin, end)) return; + for(double x = begin; fabs(x) < fabs(end); x *= k) + drawEllipse(ellipseMatrix * TAffine::scale(x), screenMatrixInv, pixelSize, alpha); + } else { + // draw linear + double dx = fabs(gs1 - gs0); + if (dx*r < minStep) return; + for(double x = gs0 + ceil((min - gs0)/dx)*dx; x < max; x += dx) + drawEllipse(ellipseMatrix * TAffine::scale(x), screenMatrixInv, pixelSize, alpha); + } +} + + +void TAssistantEllipse::drawParallelGrid( + const TAffine &ellipseMatrix, + const TPointD &grid0, + const TPointD &grid1, + bool perspective, + bool repeat, + double alpha ) +{ + TAffine4 modelview, projection; + glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); + glGetDoublev(GL_PROJECTION_MATRIX, projection.a); + TAffine screenMatrix = (projection*modelview).get2d(); + TAffine screenMatrixInv = screenMatrix.inv(); + + double pixelSize = sqrt(tglGetPixelSize2()); + double minStep = 10.0*pixelSize; + TAffine ellipseMatrixInv = ellipseMatrix.inv(); + + const TAffine &em = ellipseMatrix; + double r = sqrt(0.5*(norm2(TPointD(em.a11, em.a21)) + norm2(TPointD(em.a12, em.a22)))); + double actualMinStep = minStep/r; + TPointD g0 = ellipseMatrixInv*grid0; + TPointD g1 = ellipseMatrixInv*grid1; + if (repeat) + { g0.x -= round(0.5*g0.x)*2.0; g1.x -= round(0.5*g1.x)*2.0; } + if (fabs(g0.x) >= 1.0 - TConsts::epsilon) return; + if (fabs(g1.x) >= 1.0 - TConsts::epsilon) return; + double gs0 = g0.y/sqrt(1.0 - g0.x*g0.x); + double gs1 = g1.y/sqrt(1.0 - g1.x*g1.x); + if (fabs(gs0) >= 1.0 - TConsts::epsilon) return; + if (fabs(gs1) >= 1.0 - TConsts::epsilon) return; + + TAngleRangeSet ranges; + ranges.add( TAngleRangeSet::fromDouble(0.0), TAngleRangeSet::fromDouble(M_PI) ); + + if (perspective) { + // draw perspective (actually angular) + double a0 = asin(gs0); + double a1 = asin(gs1); + double da = fabs(a1 - a0); + if (fabs(sin(da)) < 2.0*actualMinStep) return; + for(double a = a0 + ceil((-M_PI_2 - a0)/da)*da; a < M_PI_2; a += da) + drawEllipseRanges( + ranges, + ellipseMatrix*TAffine::scale(a < 0.0 ? -1.0 : 1.0, sin(a)), + screenMatrixInv, + pixelSize, + alpha ); + } else { + // draw linear + double dx = fabs(gs1 - gs0); + if (dx < actualMinStep) return; + for(double x = gs0 + ceil((-1.0 - gs0)/dx)*dx; x < 1.0; x += dx) + drawEllipseRanges( + ranges, + ellipseMatrix*TAffine::scale(x < 0.0 ? -1.0 : 1.0, x), + screenMatrixInv, + pixelSize, + alpha ); } +} + + +void TAssistantEllipse::draw( + const TAffine &ellipseMatrix, + const TAffine &screenMatrixInv, + double ox, + double pixelSize, + bool enabled ) const +{ + const double crossSize = 0.1; + + double alpha = getDrawingAlpha(enabled); + double gridAlpha = getDrawingGridAlpha(); + bool grid = getGrid(); + bool ruler = getRestrictA() && getRestrictB(); + bool concentric = !getRestrictA() && !getRestrictB(); + + 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); + drawEllipse(ellipseMatrix, screenMatrixInv, pixelSize, alpha); + if (ox > 1.0) + drawSegment( ellipseMatrix*TPointD(-1.0, -1.0), + ellipseMatrix*TPointD(-1.0, 1.0), pixelSize, alpha); + else if (ox < -1.0) + drawSegment( ellipseMatrix*TPointD( 1.0, -1.0), + ellipseMatrix*TPointD( 1.0, 1.0), pixelSize, alpha); + + if (!grid) return; + + if (ruler) { + drawRuler( + ellipseMatrix, + m_grid0.position, + m_grid1.position, + getPerspective(), + alpha ); + } else + if (concentric) { + drawConcentricGrid( + ellipseMatrix, + m_grid0.position, + m_grid1.position, + getPerspective(), + gridAlpha ); + } else { + drawParallelGrid( + ellipseMatrix, + m_grid0.position, + m_grid1.position, + getPerspective(), + getRepeat(), + gridAlpha ); + } +} -public: - void draw(TToolViewer *viewer, bool enabled) const override { - bool restrictA = getRestrictA(); - bool restrictB = getRestrictB(); - bool repeat = getRepeat(); - double minStep = 30.0; - - TAffine ellipseMatrix = calcEllipseMatrix(); - if (ellipseMatrix.isZero()) return; - if (!restrictA && restrictB) { - std::swap(ellipseMatrix.a11, ellipseMatrix.a12); - std::swap(ellipseMatrix.a21, ellipseMatrix.a22); - } - // 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()); - - if (!repeat || restrictA == restrictB || norm(TPointD(ellipseMatrix.a11, ellipseMatrix.a21)) < minStep*pixelSize) { - draw(ellipseMatrix, matrixInv, 0.0, pixelSize, enabled); - } else { - // calculate bounds - TPointD o(ellipseMatrix.a13, ellipseMatrix.a23); - TPointD proj(ellipseMatrix.a11, ellipseMatrix.a21); - proj = proj * (1.0/norm2(proj)); - TPointD corners[4] = { - TPointD(oneBox.x0, oneBox.y0), - TPointD(oneBox.x0, oneBox.y1), - TPointD(oneBox.x1, oneBox.y0), - TPointD(oneBox.x1, oneBox.y1) }; - double minX = 0.0, maxX = 0.0; - for(int i = 0; i < 4; ++i) { - double x = proj * (matrixInv*corners[i] - o); - if (i == 0 || x < minX) minX = x; - if (i == 0 || x > maxX) maxX = x; - } - if (maxX <= minX) return; +void TAssistantEllipse::draw(TToolViewer*, bool enabled) const { + bool restrictA = getRestrictA(); + bool restrictB = getRestrictB(); + bool repeat = getRepeat(); + double minStep = 30.0; + + TAffine ellipseMatrix = calcEllipseMatrix(); + if (ellipseMatrix.isZero()) return; + if (!restrictA && restrictB) { + std::swap(ellipseMatrix.a11, ellipseMatrix.a12); + std::swap(ellipseMatrix.a21, ellipseMatrix.a22); + } - // draw - for(double ox = round(0.5*minX)*2.0; ox - 1.0 < maxX; ox += 2.0) - draw(ellipseMatrix*TAffine::translation(ox, 0.0), matrixInv, ox, pixelSize, enabled); + // 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()); + + if (!repeat || restrictA == restrictB || norm(TPointD(ellipseMatrix.a11, ellipseMatrix.a21)) < minStep*pixelSize) { + draw(ellipseMatrix, matrixInv, 0.0, pixelSize, enabled); + } else { + // calculate bounds + TPointD o(ellipseMatrix.a13, ellipseMatrix.a23); + TPointD proj(ellipseMatrix.a11, ellipseMatrix.a21); + proj = proj * (1.0/norm2(proj)); + TPointD corners[4] = { + TPointD(oneBox.x0, oneBox.y0), + TPointD(oneBox.x0, oneBox.y1), + TPointD(oneBox.x1, oneBox.y0), + TPointD(oneBox.x1, oneBox.y1) }; + double minX = 0.0, maxX = 0.0; + for(int i = 0; i < 4; ++i) { + double x = proj * (matrixInv*corners[i] - o); + if (i == 0 || x < minX) minX = x; + if (i == 0 || x > maxX) maxX = x; } + if (maxX <= minX) return; + + // draw + for(double ox = round(0.5*minX)*2.0; ox - 1.0 < maxX; ox += 2.0) + draw(ellipseMatrix*TAffine::translation(ox, 0.0), matrixInv, ox, pixelSize, enabled); } -}; +} //***************************************************************************************** diff --git a/toonz/sources/tnztools/assistants/assistantellipse.h b/toonz/sources/tnztools/assistants/assistantellipse.h new file mode 100644 index 0000000..dfef625 --- /dev/null +++ b/toonz/sources/tnztools/assistants/assistantellipse.h @@ -0,0 +1,127 @@ +#pragma once + +#ifndef ASSISTANTELLIPSE_INCLUDED +#define ASSISTANTELLIPSE_INCLUDED + + +// TnzTools includes +#include +#include +#include + +// TnzCore includes +#include + +// std includes +#include + + +//***************************************************************************************** +// TAssistantEllipse definition +//***************************************************************************************** + +class TAssistantEllipse final : public TAssistant { + Q_DECLARE_TR_FUNCTIONS(TAssistantEllipse) +public: + const TStringId m_idCircle; + const TStringId m_idRestrictA; + const TStringId m_idRestrictB; + const TStringId m_idRepeat; + const TStringId m_idGrid; + const TStringId m_idPerspective; + +protected: + TAssistantPoint &m_center; + TAssistantPoint &m_a; + TAssistantPoint &m_b; + TAssistantPoint &m_grid0; + TAssistantPoint &m_grid1; + +public: + TAssistantEllipse(TMetaObject &object); + + static QString getLocalName(); + + void updateTranslation() const override; + + inline bool getCircle() const + { return data()[m_idCircle].getBool(); } + inline bool getRestrictA() const + { return data()[m_idRestrictA].getBool(); } + inline bool getRestrictB() const + { return data()[m_idRestrictB].getBool(); } + inline bool getRepeat() const + { return data()[m_idRepeat].getBool(); } + inline bool getGrid() const + { return data()[m_idGrid].getBool(); } + inline bool getPerspective() const + { return data()[m_idPerspective].getBool(); } + + void onDataChanged(const TVariant &value) override; + +private: + void fixBAndGrid( + TPointD prevCenter, + TPointD prevA, + TPointD prevB ); + +public: + void onMovePoint(TAssistantPoint &point, const TPointD &position) override; + + TAffine calcEllipseMatrix() const; + + void getGuidelines( + const TPointD &position, + const TAffine &toTool, + TGuidelineList &outGuidelines ) const override; + + static void drawEllipseRanges( + const TAngleRangeSet &ranges, + const TAffine &ellipseMatrix, + const TAffine &screenMatrixInv, + double pixelSize, + double alpha ); + + static inline void drawEllipse( + const TAffine &ellipseMatrix, + const TAffine &screenMatrixInv, + double pixelSize, + double alpha ) + { drawEllipseRanges(TAngleRangeSet(true), ellipseMatrix, screenMatrixInv, pixelSize, alpha); } + + static void drawRuler( + const TAffine &ellipseMatrix, + const TPointD &grid0, + const TPointD &grid1, + bool perspective, + double alpha ); + + static void drawConcentricGrid( + const TAffine &ellipseMatrix, + const TPointD &grid0, + const TPointD &grid1, + bool perspective, + double alpha ); + + static void drawParallelGrid( + const TAffine &ellipseMatrix, + const TPointD &grid0, + const TPointD &grid1, + bool perspective, + bool repeat, + double alpha ); + +private: + void draw( + const TAffine &ellipseMatrix, + const TAffine &screenMatrixInv, + double ox, + double pixelSize, + bool enabled ) const; + +public: + void draw(TToolViewer *viewer, bool enabled) const override; +}; + + +#endif