From c7854d10c91f01691932213215402afe2bb6fa03 Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: May 01 2023 08:50:48 +0000 Subject: #assistants: simple grid for vanishing point --- diff --git a/toonz/sources/include/tools/assistant.h b/toonz/sources/include/tools/assistant.h index a3ca619..52b7247 100644 --- a/toonz/sources/include/tools/assistant.h +++ b/toonz/sources/include/tools/assistant.h @@ -99,7 +99,8 @@ public: Circle, CircleFill, CircleCross, - CircleGrid + CircleDots, + CircleDoubleDots, }; const TStringId name; @@ -245,7 +246,7 @@ public: TPropertyGroup& getProperties() const { return m_properties; } void propertyChanged(const TStringId &name) - { LockEvents lock(*this); onPropertyChanged(name); } + { onPropertyChanged(name); } const TAssistantPoint& getBasePoint() const; @@ -299,10 +300,18 @@ protected: //! put value from property to variant virtual void onPropertyChanged(const TStringId &name); - void drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, bool enabled = true) const; - void drawDot(const TPointD &p, bool enabled = true) const; + double getDrawingAlpha(bool enabled = true) const; + double getDrawingGridAlpha() const; + + void drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, double alpha) const; + void drawDot(const TPointD &p, double alpha) const; void drawPoint(const TAssistantPoint &point, double pixelSize) const; + inline void drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize) const + { drawSegment(p0, p1, pixelSize, getDrawingAlpha()); } + inline void drawDot(const TPointD &p) const + { drawDot(p, getDrawingAlpha()); } + void addProperty(TProperty *p); void setTranslation(const TStringId &name, const QString &localName) const; diff --git a/toonz/sources/tnztools/assistant.cpp b/toonz/sources/tnztools/assistant.cpp index ddd523a..84d2725 100644 --- a/toonz/sources/tnztools/assistant.cpp +++ b/toonz/sources/tnztools/assistant.cpp @@ -380,12 +380,22 @@ TAssistant::onPropertyChanged(const TStringId &name) { //--------------------------------------------------------------------------------------------------- +double +TAssistant::getDrawingAlpha(bool enabled) const + { return enabled && this->getEnabled() ? 0.5 : 0.25; } + +//--------------------------------------------------------------------------------------------------- + +double +TAssistant::getDrawingGridAlpha() const + { return 0.2; } + +//--------------------------------------------------------------------------------------------------- + void -TAssistant::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, bool enabled) const { - double colorBlack[4] = { 0.0, 0.0, 0.0, 0.5 }; - double colorWhite[4] = { 1.0, 1.0, 1.0, 0.5 }; - if (!enabled || !this->getEnabled()) - colorBlack[3] = (colorWhite[3] *= 0.5); +TAssistant::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, double alpha) const { + double colorBlack[4] = { 0.0, 0.0, 0.0, alpha }; + double colorWhite[4] = { 1.0, 1.0, 1.0, alpha }; glPushAttrib(GL_ALL_ATTRIB_BITS); tglEnableBlending(); @@ -406,17 +416,15 @@ TAssistant::drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize, //--------------------------------------------------------------------------------------------------- void -TAssistant::drawDot(const TPointD &p, bool enabled) const { - double colorBlack[4] = { 0.0, 0.0, 0.0, 0.5 }; - double colorWhite[4] = { 1.0, 1.0, 1.0, 0.5 }; - if (!enabled || !this->getEnabled()) - colorBlack[3] = (colorWhite[3] *= 0.5); +TAssistant::drawDot(const TPointD &p, double alpha) const { + double colorBlack[4] = { 0.0, 0.0, 0.0, alpha }; + double colorWhite[4] = { 1.0, 1.0, 1.0, alpha }; glPushAttrib(GL_ALL_ATTRIB_BITS); tglEnableBlending(); glColor4dv(colorWhite); - tglEnablePointSmooth(3.0); + tglEnablePointSmooth(4.0); glBegin(GL_POINTS); glVertex2d(p.x, p.y); glEnd(); @@ -439,9 +447,10 @@ TAssistant::drawPoint(const TAssistantPoint &point, double pixelSize) const { double radius = point.radius; double crossSize = 1.2*radius; - double colorBlack[4] = { 0.0, 0.0, 0.0, 0.5 }; - double colorGray[4] = { 0.5, 0.5, 0.5, 0.5 }; - double colorWhite[4] = { 1.0, 1.0, 1.0, 0.5 }; + double alpha = 0.5; + double colorBlack[4] = { 0.0, 0.0, 0.0, alpha }; + double colorGray[4] = { 0.5, 0.5, 0.5, alpha }; + double colorWhite[4] = { 1.0, 1.0, 1.0, alpha }; double width = 0.5; if (point.selected) { @@ -461,6 +470,8 @@ TAssistant::drawPoint(const TAssistantPoint &point, double pixelSize) const { TPointD crossDx(pixelSize*crossSize, 0.0); TPointD crossDy(0.0, pixelSize*crossSize); + TPointD gridDx(pixelSize*radius, 0.0); + TPointD gridDy(0.0, pixelSize*radius); // back line tglEnableLineSmooth(true, 2.0*std::max(1.0, width)); @@ -468,7 +479,7 @@ TAssistant::drawPoint(const TAssistantPoint &point, double pixelSize) const { if (point.type == TAssistantPoint::CircleCross) { tglDrawSegment(point.position - crossDx, point.position + crossDx); tglDrawSegment(point.position - crossDy, point.position + crossDy); - } + } else tglDrawCircle(point.position, radius*pixelSize); // front line @@ -480,6 +491,27 @@ TAssistant::drawPoint(const TAssistantPoint &point, double pixelSize) const { } tglDrawCircle(point.position, radius*pixelSize); + // dots + switch(point.type) { + case TAssistantPoint::CircleDoubleDots: + drawDot(point.position - gridDx*0.5, alpha); + drawDot(point.position + gridDx*0.5, alpha); + drawDot(point.position - gridDy*0.5, alpha); + drawDot(point.position + gridDy*0.5, alpha); + //no break + case TAssistantPoint::CircleDots: + drawDot(point.position - gridDx, alpha); + drawDot(point.position + gridDx, alpha); + drawDot(point.position - gridDy, alpha); + drawDot(point.position + gridDy, alpha); + //no break + case TAssistantPoint::Circle: + drawDot(point.position, alpha); + break; + default: + break; + } + glPopAttrib(); } diff --git a/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp b/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp index 949c0af..54047bd 100644 --- a/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp +++ b/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp @@ -29,6 +29,9 @@ protected: TAssistantPoint &m_grid0; TAssistantPoint &m_grid1; + int m_gridRays; //!< for simple grid only, not perspective + double m_gridRadiusPart; + public: TAssistantVanishingPoint(TMetaObject &object): TAssistant(object), @@ -40,8 +43,10 @@ public: m_a1 ( addPoint("A1", TAssistantPoint::Circle, TPointD(-75.0, 0.0)) ), m_b0 ( addPoint("B0", TAssistantPoint::Circle, TPointD( 50.0, 0.0)) ), m_b1 ( addPoint("B1", TAssistantPoint::Circle, TPointD( 75.0, 0.0)) ), - m_grid0 ( addPoint("grid0", TAssistantPoint::CircleGrid) ), - m_grid1 ( addPoint("grid1", TAssistantPoint::CircleGrid) ) + m_grid0 ( addPoint("grid0", TAssistantPoint::CircleDoubleDots, TPointD(-12.5,-25.0)) ), + m_grid1 ( addPoint("grid1", TAssistantPoint::CircleDots, TPointD( 12.5,-25.0)) ), + m_gridRays(), + m_gridRadiusPart() { addProperty( new TBoolProperty(m_idPassThrough.str(), getPassThrough()) ); addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) ); @@ -51,13 +56,12 @@ public: static QString getLocalName() { return tr("Vanishing Point"); } - void updateTranslation() const { + void updateTranslation() const override { setTranslation(m_idPassThrough, tr("Pass Through")); setTranslation(m_idGrid, tr("Grid")); setTranslation(m_idPerspective, tr("Perspective")); } - inline bool getPassThrough() const { return data()[m_idPassThrough].getBool(); } inline bool getGrid() const @@ -65,9 +69,9 @@ public: inline bool getPerspective() const { return data()[m_idPerspective].getBool(); } - void onDataChanged(const TVariant &value) { + void onDataChanged(const TVariant &value) override { TAssistant::onDataChanged(value); - m_grid0.visible = m_grid1.visible = ((const TVariant&)data())[m_idGrid].getBool(); + m_grid0.visible = m_grid1.visible = getGrid(); } private: @@ -103,17 +107,58 @@ private: void fixSidePoint(TAssistantPoint &p0, TAssistantPoint &p1) { fixSidePoint(p0, p1, p0.position); } + void fixGrid1(const TPointD &previousCenter, const TPointD &previousGrid0) { + TPointD dx = previousCenter - previousGrid0; + double l = norm2(dx); + if (l <= TConsts::epsilon*TConsts::epsilon) return; + dx = dx*(1.0/sqrt(l)); + TPointD dy(-dx.y, dx.x); + + TPointD d = m_grid1.position - previousGrid0; + double x = (dx*d); + double y = (dy*d); + + dx = m_center.position - m_grid0.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*x + dy*y; + } + + void recalcGrid() { + const double minStep = 5.0; + + TPointD d0 = m_grid0.position - m_center.position; + TPointD d1 = m_grid1.position - m_center.position; + double l = norm(d0); + if (l <= TConsts::epsilon) return; + if (norm2(d1) <= TConsts::epsilon*TConsts::epsilon) return; + double a0 = atan(d0); + double a1 = atan(d1); + double da = fabs(a1 - a0); + if (da > M_PI) da = M_PI - da; + if (da < TConsts::epsilon) da = TConsts::epsilon; + double count = M_2PI/da; + m_gridRadiusPart = minStep/da/l; + m_gridRays = count < 1e6 && m_gridRadiusPart < 1.0 + ? (int)round(count) : 0; + } + public: void onFixPoints() override { fixSidePoint(m_a0, m_a1); fixSidePoint(m_b0, m_b1); fixCenter(); + recalcGrid(); } void onMovePoint(TAssistantPoint &point, const TPointD &position) override { if (&point == &getBasePoint()) { move(position); return; } + TPointD previousCenter = m_center.position; TPointD previous = point.position; point.position = position; if (&point == &m_a0) { @@ -134,6 +179,15 @@ public: fixSidePoint(m_b0, m_b1); fixSidePoint(m_a0, m_a1); } + + if (&point == &m_grid0) { + fixGrid1(previousCenter, previous); + } else + if (&point != &m_grid1) { + fixGrid1(previousCenter, m_grid0.position); + } + + recalcGrid(); } void getGuidelines( @@ -158,23 +212,88 @@ public: } } + void drawSimpleGrid() const { + if (m_gridRays <= 0) return; + + double alpha = getDrawingGridAlpha(); + 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 TPointD &p = m_center.position; + const TRectD oneBox(-1.0, -1.0, 1.0, 1.0); + TPointD dp = m_grid0.position - p; + double step = M_2PI/(double)m_gridRays; + + // calculate range + int raysCount = m_gridRays; + if (!(matrixInv*oneBox).contains(p)) { + TPointD corners[4] = { + TPointD(oneBox.x0, oneBox.y0), + TPointD(oneBox.x0, oneBox.y1), + TPointD(oneBox.x1, oneBox.y0), + TPointD(oneBox.x1, oneBox.y1) }; + double angles[4]; + double a0 = 0.0, a1 = 0.0, da = 0.0; + for(int i = 0; i < 4; ++i) { + angles[i] = atan(matrixInv*corners[i] - p) + M_2PI; + for(int j = 0; j < i; ++j) { + double d = fabs(angles[i] - angles[j]); + if (d > M_PI) d = M_PI - d; + if (d > da) da = d, a0 = angles[i], a1 = angles[j]; + } + } + if (a1 < a0) std::swap(a1, a0); + if (a1 - a0 > M_PI) { std::swap(a1, a0); a1 += M_2PI; } + double a = atan(dp) + M_2PI; + a0 = ceil ((a0 - a)/step)*step + a; + a1 = floor((a1 - a)/step)*step + a; + + double s = sin(a0 - a); + double c = cos(a0 - a); + dp = TPointD(c*dp.x - s*dp.y, s*dp.x + c*dp.y); + raysCount = (int)round((a1 - a0)/step); + } + + // draw rays + double s = sin(step); + double c = cos(step); + for(int i = 0; i < raysCount; ++i) { + TPointD p0 = matrix*(p + dp*m_gridRadiusPart); + TPointD p1 = matrix*(p + dp); + if (TGuidelineLineBase::truncateInfiniteRay(oneBox, p0, p1)) + drawSegment(matrixInv*p0, matrixInv*p1, pixelSize, alpha); + dp = TPointD(c*dp.x - s*dp.y, s*dp.x + c*dp.y); + } + } + + void drawPerspectiveGrid() const { + // TODO: + } + void draw(TToolViewer *viewer, bool enabled) const override { double pixelSize = sqrt(tglGetPixelSize2()); const TPointD &p = m_center.position; TPointD dx(20.0*pixelSize, 0.0); TPointD dy(0.0, 10.0*pixelSize); - drawSegment(p-dx-dy, p+dx+dy, pixelSize, enabled); - drawSegment(p-dx+dy, p+dx-dy, pixelSize, enabled); + double alpha = getDrawingAlpha(enabled); + drawSegment(p-dx-dy, p+dx+dy, pixelSize, alpha); + drawSegment(p-dx+dy, p+dx-dy, pixelSize, alpha); + if (getGrid()) { + if (getPerspective()) + drawPerspectiveGrid(); + else + drawSimpleGrid(); + } } void drawEdit(TToolViewer *viewer) const override { double pixelSize = sqrt(tglGetPixelSize2()); drawSegment(m_center.position, m_a1.position, pixelSize); drawSegment(m_center.position, m_b1.position, pixelSize); - drawDot(m_a0.position); - drawDot(m_a1.position); - drawDot(m_b0.position); - drawDot(m_b1.position); TAssistant::drawEdit(viewer); } }; diff --git a/toonz/sources/tnztools/assistants/guidelineline.cpp b/toonz/sources/tnztools/assistants/guidelineline.cpp index ff30738..faa5e16 100644 --- a/toonz/sources/tnztools/assistants/guidelineline.cpp +++ b/toonz/sources/tnztools/assistants/guidelineline.cpp @@ -20,37 +20,54 @@ TGuidelineLineBase::calcDirection(const TPointD &p0, const TPointD &p1) { return k > TConsts::epsilon*TConsts::epsilon ? d*(1.0/sqrt(k)) : TPointD(); } -void + +static bool +truncateX(double &x0, double &y0, double &x1, double &y1, double minx, double maxx) { + double dx = x1 - x0; + if (fabs(dx) < TConsts::epsilon) return false; + double k = (y1 - y0)/dx; + if (dx > 0.0) { + y0 += k*(minx - x0); x0 = minx; + y1 += k*(maxx - x1); x1 = maxx; + } else { + y0 += k*(maxx - x0); x0 = maxx; + y1 += k*(minx - x1); x1 = minx; + } + return true; +} + +bool TGuidelineLineBase::truncateInfiniteLine(const TRectD &bounds, TPointD &p0, TPointD &p1) { + if (bounds.isEmpty()) return false; TPointD d = p0 - p1; TDimensionD size = bounds.getSize(); if (fabs(d.x)*bounds.y0 > bounds.x0*fabs(d.y)) { // horizontal - if (fabs(d.x) < TConsts::epsilon) return; - double k = d.y/d.x; - if (d.x > 0.0) { - p1 = TPointD(bounds.x0, p0.y + k*(bounds.x0 - p0.x)); - p0 = TPointD(bounds.x1, p0.y + k*(bounds.x1 - p0.x)); - } else { - p1 = TPointD(bounds.x1, p0.y + k*(bounds.x1 - p0.x)); - p0 = TPointD(bounds.x0, p0.y + k*(bounds.x0 - p0.x)); - } + if (!truncateX(p0.x, p0.y, p1.x, p1.y, bounds.x0, bounds.x1)) return false; + if (p0.y <= bounds.y0 && p1.y <= bounds.y0) return false; + if (p0.y >= bounds.y1 && p1.y >= bounds.y1) return false; } else { // vertical - if (fabs(d.y) < TConsts::epsilon) return; - double k = d.x/d.y; - if (d.y > 0.0) { - p1 = TPointD(p0.x + k*(bounds.y0 - p0.y), bounds.y0); - p0 = TPointD(p0.x + k*(bounds.y1 - p0.y), bounds.y1); - } else { - p1 = TPointD(p0.x + k*(bounds.y1 - p0.y), bounds.y1); - p0 = TPointD(p0.x + k*(bounds.y0 - p0.y), bounds.y0); - } + if (!truncateX(p0.y, p0.x, p1.y, p1.x, bounds.y0, bounds.y1)) return false; + if (p0.x <= bounds.x0 && p1.x <= bounds.x0) return false; + if (p0.x >= bounds.x1 && p1.x >= bounds.x1) return false; + } + return true; +} + +bool +TGuidelineLineBase::truncateInfiniteRay(const TRectD &bounds, TPointD &p0, TPointD &p1) { + if (bounds.isEmpty()) return false; + TRectD b(bounds); + if (b.contains(p0)) { + (p0.x < p1.x ? b.x0 : b.x1) = p0.x; + (p0.y < p1.y ? b.y0 : b.y1) = p0.y; } + return truncateInfiniteLine(b, p0, p1); } void -TGuidelineLineBase::drawInliniteLine(const TPointD &p0, const TPointD &p1, bool ray, bool active, bool enabled) const { +TGuidelineLineBase::drawInfiniteLine(const TPointD &p0, const TPointD &p1, bool ray, bool active, bool enabled) const { TAffine4 modelview, projection; glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a); glGetDoublev(GL_PROJECTION_MATRIX, projection.a); @@ -58,11 +75,13 @@ TGuidelineLineBase::drawInliniteLine(const TPointD &p0, const TPointD &p1, bool TAffine matrix = (projection*modelview).get2d(); TPointD pp0 = matrix*p0; TPointD pp1 = matrix*p1; - truncateInfiniteLine(TRectD(-1.0, -1.0, 1.0, 1.0), pp0, pp1); + if ( ray + ? !truncateInfiniteRay (TRectD(-1.0, -1.0, 1.0, 1.0), pp0, pp1) + : !truncateInfiniteLine(TRectD(-1.0, -1.0, 1.0, 1.0), pp0, pp1) ) return; double pixelSize = sqrt(tglGetPixelSize2()); TAffine matrixInv = matrix.inv(); - drawSegment((ray ? p0 : matrixInv*pp0), matrixInv*pp1, pixelSize, active, enabled); + drawSegment(matrixInv*pp0, matrixInv*pp1, pixelSize, active, enabled); } @@ -104,7 +123,7 @@ TGuidelineInfiniteLine::transformPoint(const TTrackPoint &point) const { void TGuidelineInfiniteLine::draw(bool active, bool enabled) const - { drawInliniteLine(p0, p1, false, active, enabled); } + { drawInfiniteLine(p0, p1, false, active, enabled); } //***************************************************************************************** @@ -124,5 +143,5 @@ TGuidelineRay::transformPoint(const TTrackPoint &point) const { void TGuidelineRay::draw(bool active, bool enabled) const - { drawInliniteLine(p0, p1, true, active, enabled); } + { drawInfiniteLine(p0, p1, true, active, enabled); } diff --git a/toonz/sources/tnztools/assistants/guidelineline.h b/toonz/sources/tnztools/assistants/guidelineline.h index cb2d76d..27262ca 100644 --- a/toonz/sources/tnztools/assistants/guidelineline.h +++ b/toonz/sources/tnztools/assistants/guidelineline.h @@ -32,9 +32,10 @@ public: const TPointD p1; TGuidelineLineBase(bool enabled, double magnetism, const TPointD &p0, const TPointD &p1); - void drawInliniteLine(const TPointD &p0, const TPointD &p1, bool ray, bool active, bool enabled) const; + void drawInfiniteLine(const TPointD &p0, const TPointD &p1, bool ray, bool active, bool enabled) const; static TPointD calcDirection(const TPointD &p0, const TPointD &p1); - static void truncateInfiniteLine(const TRectD &bounds, TPointD &p0, TPointD &p1); + static bool truncateInfiniteLine(const TRectD &bounds, TPointD &p0, TPointD &p1); + static bool truncateInfiniteRay(const TRectD &bounds, TPointD &p0, TPointD &p1); };