From 7900ea1b79dc73221ab105c49097ec0243c7e8d8 Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: May 01 2023 08:50:48 +0000 Subject: #assistants: improve vanishing point --- diff --git a/toonz/sources/include/tools/assistant.h b/toonz/sources/include/tools/assistant.h index 448f53f..a3ca619 100644 --- a/toonz/sources/include/tools/assistant.h +++ b/toonz/sources/include/tools/assistant.h @@ -47,6 +47,7 @@ class TGuideline; typedef TSmartPointerT TGuidelineP; typedef std::vector TGuidelineList; typedef std::map TAssistantPointMap; +typedef std::vector TAssistantPointOrder; //=================================================================== @@ -102,6 +103,8 @@ public: }; const TStringId name; + const TPointD defPosition; + Type type; TPointD position; double radius; @@ -109,7 +112,7 @@ public: mutable bool selected; - explicit TAssistantPoint(const TStringId &name); + explicit TAssistantPoint(const TStringId &name, const TPointD &defPosition = TPointD()); }; @@ -193,6 +196,7 @@ protected: const TStringId m_idMagnetism; TAssistantPointMap m_points; + TAssistantPointOrder m_pointsOrder; TAssistantPoint* m_basePoint; mutable TPropertyGroup m_properties; @@ -205,6 +209,8 @@ public: inline const TAssistantPointMap& points() const { return m_points; } + inline const TAssistantPointOrder& pointsOrder() const + { return m_pointsOrder; } inline const TAssistantPoint* findPoint(const TStringId &name) const { TAssistantPointMap::const_iterator i = points().find(name); @@ -212,6 +218,7 @@ public: } void fixPoints(); + void move(const TPointD &position); void movePoint(const TStringId &name, const TPointD &position); void setPointSelection(const TStringId &name, bool selected) const; void setAllPointsSelection(bool selected) const; @@ -246,35 +253,37 @@ protected: TAssistantPoint& addPoint( const TStringId &name, TAssistantPoint::Type type, - const TPointD &position, + const TPointD &defPosition, bool visible, double radius ); TAssistantPoint& addPoint( const TStringId &name, TAssistantPoint::Type type = TAssistantPoint::Circle, - const TPointD &position = TPointD(), + const TPointD &defPosition = TPointD(), bool visible = true ); inline TAssistantPoint& addPoint( const std::string &name, TAssistantPoint::Type type, - const TPointD &position, + const TPointD &defPosition, bool visible, double radius ) - { return addPoint(TStringId(name), type, position, visible, radius); } + { return addPoint(TStringId(name), type, defPosition, visible, radius); } inline TAssistantPoint& addPoint( const std::string &name, TAssistantPoint::Type type = TAssistantPoint::Circle, - const TPointD &position = TPointD(), + const TPointD &defPosition = TPointD(), bool visible = true ) - { return addPoint(TStringId(name), type, position, visible); } + { return addPoint(TStringId(name), type, defPosition, visible); } //! usually called when meta-object created void onSetDefaults() override; //! called when part of variant data changed void onDataChanged(const TVariant &value) override; + //! called when field of root struct of variant data changed + virtual void onDataFieldChanged(const TStringId &name, const TVariant &value); //! load object data from variant virtual void onAllDataChanged(); //! fix positions of all points (as like as all points moved) @@ -291,6 +300,7 @@ protected: 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; void drawPoint(const TAssistantPoint &point, double pixelSize) const; void addProperty(TProperty *p); diff --git a/toonz/sources/tnztools/assistant.cpp b/toonz/sources/tnztools/assistant.cpp index a7a4451..ddd523a 100644 --- a/toonz/sources/tnztools/assistant.cpp +++ b/toonz/sources/tnztools/assistant.cpp @@ -106,8 +106,9 @@ TGuideline::findBest(const TGuidelineList &guidelines, const TTrack &track, cons // TAssistantPoint implementation //************************************************************************ -TAssistantPoint::TAssistantPoint(const TStringId &name): +TAssistantPoint::TAssistantPoint(const TStringId &name, const TPointD &defPosition): name(name), + defPosition(defPosition), type(Circle), position(position), radius(10.0), @@ -147,15 +148,15 @@ TAssistantPoint& TAssistant::addPoint( const TStringId &name, TAssistantPoint::Type type, - const TPointD &position, + const TPointD &defPosition, bool visible, double radius ) { assert(!m_points.count(name)); TAssistantPoint &p = m_points.insert( - TAssistantPointMap::value_type(name, TAssistantPoint(name)) ).first->second; + TAssistantPointMap::value_type(name, TAssistantPoint(name, defPosition)) ).first->second; + m_pointsOrder.push_back(&p); p.type = type; - p.position = position; p.radius = radius; p.visible = visible; if (!m_basePoint) m_basePoint = &p; @@ -168,9 +169,9 @@ TAssistantPoint& TAssistant::addPoint( const TStringId &name, TAssistantPoint::Type type, - const TPointD &position, + const TPointD &defPosition, bool visible ) - { return addPoint(name, type, position, visible, 10.0); } + { return addPoint(name, type, defPosition, visible, 10.0); } //--------------------------------------------------------------------------------------------------- @@ -204,6 +205,10 @@ void TAssistant::onSetDefaults() { setEnabled(true); setMagnetism(1.0); + for(TAssistantPointMap::iterator i = m_points.begin(); i != m_points.end(); ++i) + i->second.position = i->second.defPosition; + fixPoints(); + fixData(); } //--------------------------------------------------------------------------------------------------- @@ -215,6 +220,16 @@ TAssistant::fixPoints() //--------------------------------------------------------------------------------------------------- void +TAssistant::move(const TPointD &position) { + TPointD d = position - getBasePoint().position; + for(TAssistantPointMap::iterator i = m_points.begin(); i != m_points.end(); ++i) + i->second.position += d; + fixPoints(); +} + +//--------------------------------------------------------------------------------------------------- + +void TAssistant::movePoint(const TStringId &name, const TPointD &position) { TAssistantPointMap::iterator i = m_points.find(name); if (i != m_points.end()) @@ -255,13 +270,20 @@ TAssistant::onDataChanged(const TVariant &value) { movePoint(entry.field(), position); } else if (data().getChildPathEntry(value, entry) && entry.isField()) { - updateProperty(entry.field(), data()[entry.field()]); + onDataFieldChanged(entry.field(), data()[entry.field()]); } } //--------------------------------------------------------------------------------------------------- void +TAssistant::onDataFieldChanged(const TStringId &name, const TVariant &value) { + updateProperty(name, value); +} + +//--------------------------------------------------------------------------------------------------- + +void TAssistant::onAllDataChanged() { const TVariant& pointsData = data()[m_idPoints]; for(TAssistantPointMap::iterator i = m_points.begin(); i != m_points.end(); ++i) { @@ -384,6 +406,33 @@ 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); + + glPushAttrib(GL_ALL_ATTRIB_BITS); + tglEnableBlending(); + + glColor4dv(colorWhite); + tglEnablePointSmooth(3.0); + glBegin(GL_POINTS); + glVertex2d(p.x, p.y); + glEnd(); + + glColor4dv(colorBlack); + tglEnablePointSmooth(2.0); + glBegin(GL_POINTS); + glVertex2d(p.x, p.y); + glEnd(); + + glPopAttrib(); +} + +//--------------------------------------------------------------------------------------------------- + +void TAssistant::drawPoint(const TAssistantPoint &point, double pixelSize) const { if (!point.visible) return; diff --git a/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp b/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp index 0e7887b..949c0af 100644 --- a/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp +++ b/toonz/sources/tnztools/assistants/assistantvanishingpoint.cpp @@ -15,41 +15,171 @@ class DVAPI TAssistantVanishingPoint final : public TAssistant { Q_DECLARE_TR_FUNCTIONS(TAssistantVanishingPoint) +public: + const TStringId m_idPassThrough; + const TStringId m_idGrid; + const TStringId m_idPerspective; + protected: - TAssistantPoint &m_pointCenter; + TAssistantPoint &m_center; + TAssistantPoint &m_a0; + TAssistantPoint &m_a1; + TAssistantPoint &m_b0; + TAssistantPoint &m_b1; + TAssistantPoint &m_grid0; + TAssistantPoint &m_grid1; public: TAssistantVanishingPoint(TMetaObject &object): TAssistant(object), - m_pointCenter( addPoint("center", TAssistantPoint::CircleCross) ) - { } + m_idPassThrough("passThrough"), + m_idGrid("grid"), + m_idPerspective("perspective"), + m_center( addPoint("center", TAssistantPoint::CircleCross) ), + m_a0 ( addPoint("A0", TAssistantPoint::Circle, TPointD(-50.0, 0.0)) ), + 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) ) + { + addProperty( new TBoolProperty(m_idPassThrough.str(), getPassThrough()) ); + addProperty( new TBoolProperty(m_idGrid.str(), getGrid()) ); + addProperty( new TBoolProperty(m_idPerspective.str(), getPerspective()) ); + } static QString getLocalName() { return tr("Vanishing Point"); } + void updateTranslation() const { + 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 + { return data()[m_idGrid].getBool(); } + inline bool getPerspective() const + { return data()[m_idPerspective].getBool(); } + + void onDataChanged(const TVariant &value) { + TAssistant::onDataChanged(value); + m_grid0.visible = m_grid1.visible = ((const TVariant&)data())[m_idGrid].getBool(); + } + +private: + void fixCenter() { + if ( !(m_a0.position == m_a1.position) + && !(m_b0.position == m_b1.position) ) + { + const TPointD &a = m_a0.position; + const TPointD &b = m_b0.position; + const TPointD da = m_a1.position - a; + const TPointD db = m_b1.position - b; + const TPointD ab = b - a; + double k = db.x*da.y - db.y*da.x; + if (fabs(k) > TConsts::epsilon) { + double lb = (da.x*ab.y - da.y*ab.x)/k; + m_center.position.x = lb*db.x + b.x; + m_center.position.y = lb*db.y + b.y; + } + } + } + + void fixSidePoint(TAssistantPoint &p0, TAssistantPoint &p1, TPointD previousP0) { + if (p0.position != m_center.position && p0.position != p1.position) { + TPointD dp0 = p0.position - m_center.position; + TPointD dp1 = p1.position - previousP0; + double l0 = norm(dp0); + double l1 = norm(dp1); + if (l0 > TConsts::epsilon && l0 + l1 > TConsts::epsilon) + p1.position = m_center.position + dp0*((l0 + l1)/l0); + } + } + + void fixSidePoint(TAssistantPoint &p0, TAssistantPoint &p1) + { fixSidePoint(p0, p1, p0.position); } + +public: + void onFixPoints() override { + fixSidePoint(m_a0, m_a1); + fixSidePoint(m_b0, m_b1); + fixCenter(); + } + + void onMovePoint(TAssistantPoint &point, const TPointD &position) override { + if (&point == &getBasePoint()) + { move(position); return; } + + TPointD previous = point.position; + point.position = position; + if (&point == &m_a0) { + fixSidePoint(m_a0, m_a1, previous); + fixSidePoint(m_b0, m_b1); + } else + if (&point == &m_b0) { + fixSidePoint(m_a0, m_a1); + fixSidePoint(m_b0, m_b1, previous); + } else + if (&point == &m_a1) { + fixCenter(); + fixSidePoint(m_a0, m_a1); + fixSidePoint(m_b0, m_b1); + } else + if (&point == &m_b1) { + fixCenter(); + fixSidePoint(m_b0, m_b1); + fixSidePoint(m_a0, m_a1); + } + } + void getGuidelines( const TPointD &position, const TAffine &toTool, TGuidelineList &outGuidelines ) const override { - outGuidelines.push_back(TGuidelineP( - new TGuidelineInfiniteLine( - getEnabled(), - getMagnetism(), - toTool * m_pointCenter.position, - position ))); + if (getPassThrough()) { + outGuidelines.push_back(TGuidelineP( + new TGuidelineInfiniteLine( + getEnabled(), + getMagnetism(), + toTool * m_center.position, + position ))); + } else { + outGuidelines.push_back(TGuidelineP( + new TGuidelineRay( + getEnabled(), + getMagnetism(), + toTool * m_center.position, + position ))); + } } void draw(TToolViewer *viewer, bool enabled) const override { double pixelSize = sqrt(tglGetPixelSize2()); - const TPointD &p = m_pointCenter.position; + 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); } + + 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); + } }; + //***************************************************************************************** // Registration //***************************************************************************************** diff --git a/toonz/sources/tnztools/assistants/guidelineline.cpp b/toonz/sources/tnztools/assistants/guidelineline.cpp index 9ae3bf2..ff30738 100644 --- a/toonz/sources/tnztools/assistants/guidelineline.cpp +++ b/toonz/sources/tnztools/assistants/guidelineline.cpp @@ -28,14 +28,24 @@ TGuidelineLineBase::truncateInfiniteLine(const TRectD &bounds, TPointD &p0, TPoi // horizontal if (fabs(d.x) < TConsts::epsilon) return; double k = d.y/d.x; - p1 = TPointD(bounds.x1, p0.y + k*(bounds.x1 - p0.x)); - p0 = TPointD(bounds.x0, p0.y + k*(bounds.x0 - p0.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)); + } } else { // vertical if (fabs(d.y) < TConsts::epsilon) return; double k = d.x/d.y; - p1 = TPointD(p0.x + k*(bounds.y1 - p0.y), bounds.y1); - p0 = TPointD(p0.x + k*(bounds.y0 - p0.y), bounds.y0); + 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); + } } } diff --git a/toonz/sources/tnztools/editassistantstool.cpp b/toonz/sources/tnztools/editassistantstool.cpp index f694613..44b1e67 100644 --- a/toonz/sources/tnztools/editassistantstool.cpp +++ b/toonz/sources/tnztools/editassistantstool.cpp @@ -357,14 +357,20 @@ protected: if (!assistant) continue; assistant->deselectAll(); - const TAssistantPointMap &points = assistant->points(); - for(TAssistantPointMap::const_iterator j = points.begin(); j != points.end() && m_currentAssistantIndex < 0; ++j) { - const TAssistantPoint &p = j->second; + + // last points is less significant and don't affecting the first points + // so iterate points in reverse order to avoid unsolvable points overlapping + const TAssistantPointOrder &points = assistant->pointsOrder(); + for( TAssistantPointOrder::const_reverse_iterator j = points.rbegin(); + j != points.rend() && m_currentAssistantIndex < 0; + ++j ) + { + const TAssistantPoint &p = **j; TPointD offset = p.position - position; if (p.visible && norm2(offset) <= p.radius*p.radius*pixelSize*pixelSize) { m_currentAssistant.set(*i); m_currentAssistantIndex = i - (*m_reader)->begin(); - m_currentPointName = j->first; + m_currentPointName = p.name; m_currentPointOffset = offset; assistant->selectAll(); } @@ -379,7 +385,7 @@ protected: bool apply() { bool success = false; if (m_currentAssistantChanged || m_currentAssistantCreated) { - if (Closer closer = write(ModePoint)) { + if (Closer closer = write(ModeAssistant)) { m_writeAssistant->fixData(); TUndoManager::manager()->add(new EditAssistantsUndo( getApplication()->getCurrentLevel()->getLevel()->getSimpleLevel(), @@ -419,12 +425,13 @@ public: m_dragging = true; if (m_newAssisnantType) { // create assistant - resetCurrentPoint(); + resetCurrentPoint(false); if (Closer closer = write(ModeImage)) { TMetaObjectP object(new TMetaObject(m_newAssisnantType)); if (TAssistant *assistant = object->getHandler()) { assistant->setDefaults(); - assistant->movePoint(assistant->getBasePoint().name, position); + assistant->move(position); + assistant->selectAll(); m_currentAssistantCreated = true; m_currentAssistantChanged = true; m_currentAssistantIndex = (int)(*m_writer)->size(); @@ -435,6 +442,7 @@ public: (*m_writer)->push_back(object); } } + updateOptionsBox(); m_newAssisnantType.reset(); } else { findCurrentPoint(position); @@ -445,19 +453,29 @@ public: } void leftButtonDrag(const TPointD &position, const TMouseEvent&) override { - if (Closer closer = write(ModePoint, true)) - m_writeAssistant->movePoint( - m_currentPointName, - position + m_currentPointOffset); + if (m_currentAssistantCreated) { + if (Closer closer = write(ModeAssistant, true)) + m_writeAssistant->move( position + m_currentPointOffset ); + } else { + if (Closer closer = write(ModePoint, true)) + m_writeAssistant->movePoint( + m_currentPointName, + position + m_currentPointOffset); + } m_currentPosition = position; getViewer()->GLInvalidateAll(); } void leftButtonUp(const TPointD &position, const TMouseEvent&) override { - if (Closer closer = write(ModePoint, true)) - m_writeAssistant->movePoint( - m_currentPointName, - position + m_currentPointOffset); + if (m_currentAssistantCreated) { + if (Closer closer = write(ModeAssistant, true)) + m_writeAssistant->move( position + m_currentPointOffset ); + } else { + if (Closer closer = write(ModePoint, true)) + m_writeAssistant->movePoint( + m_currentPointName, + position + m_currentPointOffset); + } apply(); m_assistantType.setIndex(0);