diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 7bc5db5068..176cb63084 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -16649,7 +16649,7 @@ ValueTree ValueTree::getSibling (const int delta) const return invalid; const int index = object->parent->indexOf (*this) + delta; - return ValueTree (static_cast (object->children [index])); + return ValueTree (static_cast (object->parent->children [index])); } const var& ValueTree::operator[] (const Identifier& name) const @@ -85597,6 +85597,10 @@ const Identifier DrawablePath::ValueTreeWrapper::Element::lineToElement ("Line") const Identifier DrawablePath::ValueTreeWrapper::Element::quadraticToElement ("Quad"); const Identifier DrawablePath::ValueTreeWrapper::Element::cubicToElement ("Cubic"); +const char* DrawablePath::ValueTreeWrapper::Element::cornerMode = "corner"; +const char* DrawablePath::ValueTreeWrapper::Element::roundedMode = "round"; +const char* DrawablePath::ValueTreeWrapper::Element::symmetricMode = "symm"; + DrawablePath::ValueTreeWrapper::Element::Element (const ValueTree& state_) : state (state_) { @@ -85666,6 +85670,33 @@ const RelativePoint DrawablePath::ValueTreeWrapper::Element::getEndPoint() const return RelativePoint(); } +float DrawablePath::ValueTreeWrapper::Element::getLength (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const +{ + const Identifier i (state.getType()); + + if (i == lineToElement || i == closeSubPathElement) + return getEndPoint().resolve (nameFinder).getDistanceFrom (getStartPoint().resolve (nameFinder)); + + if (i == cubicToElement) + { + Path p; + p.startNewSubPath (getStartPoint().resolve (nameFinder)); + p.cubicTo (getControlPoint (0).resolve (nameFinder), getControlPoint (1).resolve (nameFinder), getControlPoint (2).resolve (nameFinder)); + return p.getLength(); + } + + if (i == quadraticToElement) + { + Path p; + p.startNewSubPath (getStartPoint().resolve (nameFinder)); + p.quadraticTo (getControlPoint (0).resolve (nameFinder), getControlPoint (1).resolve (nameFinder)); + return p.getLength(); + } + + jassert (i == startSubPathElement); + return 0; +} + const String DrawablePath::ValueTreeWrapper::Element::getModeOfEndPoint() const { return state [mode].toString(); @@ -85724,8 +85755,129 @@ void DrawablePath::ValueTreeWrapper::Element::convertToPathBreak (UndoManager* u } } -void DrawablePath::ValueTreeWrapper::Element::insertPoint (double, RelativeCoordinate::NamedCoordinateFinder*, UndoManager*) +static const Point findCubicSubdivisionPoint (double proportion, const Point points[4]) { + const Point mid1 (points[0] + (points[1] - points[0]) * proportion), + mid2 (points[1] + (points[2] - points[1]) * proportion), + mid3 (points[2] + (points[3] - points[2]) * proportion); + + const Point newCp1 (mid1 + (mid2 - mid1) * proportion), + newCp2 (mid2 + (mid3 - mid2) * proportion); + + return newCp1 + (newCp2 - newCp1) * proportion; +} + +static const Point findQuadraticSubdivisionPoint (double proportion, const Point points[3]) +{ + const Point mid1 (points[0] + (points[1] - points[0]) * proportion), + mid2 (points[1] + (points[2] - points[1]) * proportion); + + return mid1 + (mid2 - mid1) * proportion; +} + +ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager) +{ + ValueTree newTree; + const Identifier i (state.getType()); + + if (i == cubicToElement) + { + RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getControlPoint (1)), rp4 (getEndPoint()); + + const Point points[] = { rp1.resolve (nameFinder), rp2.resolve (nameFinder), rp3.resolve (nameFinder), rp4.resolve (nameFinder) }; + + double bestProp = 0; + float bestDistance = std::numeric_limits::max(); + + for (int i = 110; --i >= 0;) + { + double prop = i > 10 ? ((i - 10) / 100.0) : (bestProp + ((i - 5) / 1000.0)); + const Point centre (findCubicSubdivisionPoint (prop, points)); + const float distance = centre.getDistanceFrom (targetPoint); + + if (distance < bestDistance) + { + bestProp = prop; + bestDistance = distance; + } + } + + const Point mid1 (points[0] + (points[1] - points[0]) * bestProp), + mid2 (points[1] + (points[2] - points[1]) * bestProp), + mid3 (points[2] + (points[3] - points[2]) * bestProp); + + const Point newCp1 (mid1 + (mid2 - mid1) * bestProp), + newCp2 (mid2 + (mid3 - mid2) * bestProp); + + const Point newCentre (newCp1 + (newCp2 - newCp1) * bestProp); + + setControlPoint (0, mid1, undoManager); + setControlPoint (1, newCp1, undoManager); + setControlPoint (2, newCentre, undoManager); + setModeOfEndPoint (roundedMode, undoManager); + + Element newElement (newTree = ValueTree (cubicToElement)); + newElement.setControlPoint (0, newCp2, 0); + newElement.setControlPoint (1, mid3, 0); + newElement.setControlPoint (2, rp4, 0); + + state.getParent().addChild (newTree, state.getParent().indexOf (state) + 1, undoManager); + } + else if (i == quadraticToElement) + { + RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint()); + + const Point points[] = { rp1.resolve (nameFinder), rp2.resolve (nameFinder), rp3.resolve (nameFinder) }; + + double bestProp = 0; + float bestDistance = std::numeric_limits::max(); + + for (int i = 110; --i >= 0;) + { + double prop = i > 10 ? ((i - 10) / 100.0) : (bestProp + ((i - 5) / 1000.0)); + const Point centre (findQuadraticSubdivisionPoint (prop, points)); + const float distance = centre.getDistanceFrom (targetPoint); + + if (distance < bestDistance) + { + bestProp = prop; + bestDistance = distance; + } + } + + const Point mid1 (points[0] + (points[1] - points[0]) * bestProp), + mid2 (points[1] + (points[2] - points[1]) * bestProp); + + const Point newCentre (mid1 + (mid2 - mid1) * bestProp); + + setControlPoint (0, mid1, undoManager); + setControlPoint (1, newCentre, undoManager); + setModeOfEndPoint (roundedMode, undoManager); + + Element newElement (newTree = ValueTree (quadraticToElement)); + newElement.setControlPoint (0, mid2, 0); + newElement.setControlPoint (1, rp3, 0); + + state.getParent().addChild (newTree, state.getParent().indexOf (state) + 1, undoManager); + } + else if (i == lineToElement) + { + RelativePoint rp1 (getStartPoint()), rp2 (getEndPoint()); + const Line line (rp1.resolve (nameFinder), rp2.resolve (nameFinder)); + const Point newPoint (line.findNearestPointTo (targetPoint)); + + setControlPoint (0, newPoint, undoManager); + + Element newElement (newTree = ValueTree (lineToElement)); + newElement.setControlPoint (0, rp2, 0); + + state.getParent().addChild (newTree, state.getParent().indexOf (state) + 1, undoManager); + } + else if (i == closeSubPathElement) + { + } + + return newTree; } void DrawablePath::ValueTreeWrapper::Element::removePoint (UndoManager* undoManager) diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 0a3c1eedb3..250e83e90b 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -59392,6 +59392,7 @@ public: const RelativePoint getStartPoint() const; const RelativePoint getEndPoint() const; void setControlPoint (int index, const RelativePoint& point, UndoManager* undoManager); + float getLength (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const; ValueTreeWrapper getParent() const; Element getPreviousElement() const; @@ -59402,7 +59403,7 @@ public: void convertToLine (UndoManager* undoManager); void convertToCubic (RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager); void convertToPathBreak (UndoManager* undoManager); - void insertPoint (double proportionOfLength, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager); + ValueTree insertPoint (const Point& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager); void removePoint (UndoManager* undoManager); static const Identifier mode, startSubPathElement, closeSubPathElement, diff --git a/src/containers/juce_ValueTree.cpp b/src/containers/juce_ValueTree.cpp index ea270e8801..94110e9513 100644 --- a/src/containers/juce_ValueTree.cpp +++ b/src/containers/juce_ValueTree.cpp @@ -616,7 +616,7 @@ ValueTree ValueTree::getSibling (const int delta) const return invalid; const int index = object->parent->indexOf (*this) + delta; - return ValueTree (static_cast (object->children [index])); + return ValueTree (static_cast (object->parent->children [index])); } const var& ValueTree::operator[] (const Identifier& name) const diff --git a/src/gui/graphics/drawables/juce_DrawablePath.cpp b/src/gui/graphics/drawables/juce_DrawablePath.cpp index cb58d8e0dd..57e63882d3 100644 --- a/src/gui/graphics/drawables/juce_DrawablePath.cpp +++ b/src/gui/graphics/drawables/juce_DrawablePath.cpp @@ -295,6 +295,10 @@ const Identifier DrawablePath::ValueTreeWrapper::Element::lineToElement ("Line") const Identifier DrawablePath::ValueTreeWrapper::Element::quadraticToElement ("Quad"); const Identifier DrawablePath::ValueTreeWrapper::Element::cubicToElement ("Cubic"); +const char* DrawablePath::ValueTreeWrapper::Element::cornerMode = "corner"; +const char* DrawablePath::ValueTreeWrapper::Element::roundedMode = "round"; +const char* DrawablePath::ValueTreeWrapper::Element::symmetricMode = "symm"; + DrawablePath::ValueTreeWrapper::Element::Element (const ValueTree& state_) : state (state_) { @@ -364,6 +368,33 @@ const RelativePoint DrawablePath::ValueTreeWrapper::Element::getEndPoint() const return RelativePoint(); } +float DrawablePath::ValueTreeWrapper::Element::getLength (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const +{ + const Identifier i (state.getType()); + + if (i == lineToElement || i == closeSubPathElement) + return getEndPoint().resolve (nameFinder).getDistanceFrom (getStartPoint().resolve (nameFinder)); + + if (i == cubicToElement) + { + Path p; + p.startNewSubPath (getStartPoint().resolve (nameFinder)); + p.cubicTo (getControlPoint (0).resolve (nameFinder), getControlPoint (1).resolve (nameFinder), getControlPoint (2).resolve (nameFinder)); + return p.getLength(); + } + + if (i == quadraticToElement) + { + Path p; + p.startNewSubPath (getStartPoint().resolve (nameFinder)); + p.quadraticTo (getControlPoint (0).resolve (nameFinder), getControlPoint (1).resolve (nameFinder)); + return p.getLength(); + } + + jassert (i == startSubPathElement); + return 0; +} + const String DrawablePath::ValueTreeWrapper::Element::getModeOfEndPoint() const { return state [mode].toString(); @@ -422,8 +453,129 @@ void DrawablePath::ValueTreeWrapper::Element::convertToPathBreak (UndoManager* u } } -void DrawablePath::ValueTreeWrapper::Element::insertPoint (double, RelativeCoordinate::NamedCoordinateFinder*, UndoManager*) +static const Point findCubicSubdivisionPoint (double proportion, const Point points[4]) { + const Point mid1 (points[0] + (points[1] - points[0]) * proportion), + mid2 (points[1] + (points[2] - points[1]) * proportion), + mid3 (points[2] + (points[3] - points[2]) * proportion); + + const Point newCp1 (mid1 + (mid2 - mid1) * proportion), + newCp2 (mid2 + (mid3 - mid2) * proportion); + + return newCp1 + (newCp2 - newCp1) * proportion; +} + +static const Point findQuadraticSubdivisionPoint (double proportion, const Point points[3]) +{ + const Point mid1 (points[0] + (points[1] - points[0]) * proportion), + mid2 (points[1] + (points[2] - points[1]) * proportion); + + return mid1 + (mid2 - mid1) * proportion; +} + +ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager) +{ + ValueTree newTree; + const Identifier i (state.getType()); + + if (i == cubicToElement) + { + RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getControlPoint (1)), rp4 (getEndPoint()); + + const Point points[] = { rp1.resolve (nameFinder), rp2.resolve (nameFinder), rp3.resolve (nameFinder), rp4.resolve (nameFinder) }; + + double bestProp = 0; + float bestDistance = std::numeric_limits::max(); + + for (int i = 110; --i >= 0;) + { + double prop = i > 10 ? ((i - 10) / 100.0) : (bestProp + ((i - 5) / 1000.0)); + const Point centre (findCubicSubdivisionPoint (prop, points)); + const float distance = centre.getDistanceFrom (targetPoint); + + if (distance < bestDistance) + { + bestProp = prop; + bestDistance = distance; + } + } + + const Point mid1 (points[0] + (points[1] - points[0]) * bestProp), + mid2 (points[1] + (points[2] - points[1]) * bestProp), + mid3 (points[2] + (points[3] - points[2]) * bestProp); + + const Point newCp1 (mid1 + (mid2 - mid1) * bestProp), + newCp2 (mid2 + (mid3 - mid2) * bestProp); + + const Point newCentre (newCp1 + (newCp2 - newCp1) * bestProp); + + setControlPoint (0, mid1, undoManager); + setControlPoint (1, newCp1, undoManager); + setControlPoint (2, newCentre, undoManager); + setModeOfEndPoint (roundedMode, undoManager); + + Element newElement (newTree = ValueTree (cubicToElement)); + newElement.setControlPoint (0, newCp2, 0); + newElement.setControlPoint (1, mid3, 0); + newElement.setControlPoint (2, rp4, 0); + + state.getParent().addChild (newTree, state.getParent().indexOf (state) + 1, undoManager); + } + else if (i == quadraticToElement) + { + RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint()); + + const Point points[] = { rp1.resolve (nameFinder), rp2.resolve (nameFinder), rp3.resolve (nameFinder) }; + + double bestProp = 0; + float bestDistance = std::numeric_limits::max(); + + for (int i = 110; --i >= 0;) + { + double prop = i > 10 ? ((i - 10) / 100.0) : (bestProp + ((i - 5) / 1000.0)); + const Point centre (findQuadraticSubdivisionPoint (prop, points)); + const float distance = centre.getDistanceFrom (targetPoint); + + if (distance < bestDistance) + { + bestProp = prop; + bestDistance = distance; + } + } + + const Point mid1 (points[0] + (points[1] - points[0]) * bestProp), + mid2 (points[1] + (points[2] - points[1]) * bestProp); + + const Point newCentre (mid1 + (mid2 - mid1) * bestProp); + + setControlPoint (0, mid1, undoManager); + setControlPoint (1, newCentre, undoManager); + setModeOfEndPoint (roundedMode, undoManager); + + Element newElement (newTree = ValueTree (quadraticToElement)); + newElement.setControlPoint (0, mid2, 0); + newElement.setControlPoint (1, rp3, 0); + + state.getParent().addChild (newTree, state.getParent().indexOf (state) + 1, undoManager); + } + else if (i == lineToElement) + { + RelativePoint rp1 (getStartPoint()), rp2 (getEndPoint()); + const Line line (rp1.resolve (nameFinder), rp2.resolve (nameFinder)); + const Point newPoint (line.findNearestPointTo (targetPoint)); + + setControlPoint (0, newPoint, undoManager); + + Element newElement (newTree = ValueTree (lineToElement)); + newElement.setControlPoint (0, rp2, 0); + + state.getParent().addChild (newTree, state.getParent().indexOf (state) + 1, undoManager); + } + else if (i == closeSubPathElement) + { + } + + return newTree; } void DrawablePath::ValueTreeWrapper::Element::removePoint (UndoManager* undoManager) diff --git a/src/gui/graphics/drawables/juce_DrawablePath.h b/src/gui/graphics/drawables/juce_DrawablePath.h index 3bd8ab7fcf..af42f386bf 100644 --- a/src/gui/graphics/drawables/juce_DrawablePath.h +++ b/src/gui/graphics/drawables/juce_DrawablePath.h @@ -163,6 +163,7 @@ public: const RelativePoint getStartPoint() const; const RelativePoint getEndPoint() const; void setControlPoint (int index, const RelativePoint& point, UndoManager* undoManager); + float getLength (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const; ValueTreeWrapper getParent() const; Element getPreviousElement() const; @@ -173,7 +174,7 @@ public: void convertToLine (UndoManager* undoManager); void convertToCubic (RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager); void convertToPathBreak (UndoManager* undoManager); - void insertPoint (double proportionOfLength, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager); + ValueTree insertPoint (const Point& targetPoint, RelativeCoordinate::NamedCoordinateFinder* nameFinder, UndoManager* undoManager); void removePoint (UndoManager* undoManager); static const Identifier mode, startSubPathElement, closeSubPathElement,