Merge lp:~danieljabailey/inkscape/arc_node_editor into lp:~inkscape.dev/inkscape/trunk
- arc_node_editor
- Merge into trunk
Status: | Needs review |
---|---|
Proposed branch: | lp:~danieljabailey/inkscape/arc_node_editor |
Merge into: | lp:~inkscape.dev/inkscape/trunk |
Diff against target: |
1019 lines (+555/-41) 13 files modified
share/icons/icons.svg (+23/-0) src/helper/geom.cpp (+42/-0) src/helper/geom.h (+1/-0) src/ui/tool/control-point.cpp (+0/-1) src/ui/tool/multi-path-manipulator.cpp (+32/-5) src/ui/tool/multi-path-manipulator.h (+3/-0) src/ui/tool/node-types.h (+2/-1) src/ui/tool/node.cpp (+162/-14) src/ui/tool/node.h (+26/-2) src/ui/tool/path-manipulator.cpp (+182/-18) src/ui/tool/path-manipulator.h (+5/-0) src/widgets/node-toolbar.cpp (+72/-0) src/widgets/toolbox.cpp (+5/-0) |
To merge this branch: | bzr merge lp:~danieljabailey/inkscape/arc_node_editor |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Inkscape Developers | Pending | ||
Review via email: mp+294742@code.launchpad.net |
Commit message
Description of the change
Hi, This is my first time contributing to inkscape and also my first time using bazaar.
I noticed that inkscape converts arc segments in paths into bezier curves when you edit them using the node editor. I had an SVG file that contained some arcs that I wanted to manipulate and so decided to add this feature to avoid having the arcs changed to beziers.
Arc segments are manipulated with a pair of perpendicular handles that pivot around the mid point between two nodes. (This is not always the arc's mid-point) They control the rotation and the radii of the elipse upon which the arc sits. The two nodes define two arc end points and two flags control the direction and 'largeness'. The two flags can be controlled with buttons in the toolbar.
This feature required a change to the handle class so that the origin of a handle could be somewhere other than its parent node. This has been done by adding an 'offset' relative to the parent position. The length of the handle is measured from the offset point to the handle point, not from the parent. In most cases however, the offset is zero and the length, as before is from the parent node to the handle point.
This change adds four buttons to the node editor toolbar. I have created icons for these using the new arc editor tools. :-)
The four buttons are: "Make selected segments arcs", "Make selected arc segments shallow", "Make selected arc segments bulge" and "Flip selected arc segments"
I have done my best to stick to the style guides, however there was some code that I copied and pasted from something that did not match the style guide particularly well, I was unsure if I should clean up the old code or the new code and so left them both as they were (see geom.cpp function pathv_to_
Please let me know if this is a valuable change that can be merged.
Also let me know if I need to fix anything.
I am aware of one slight bug, but wanted to check if there was interest in this change before I put effort into fixing it.
When an arc segment is dragged (as you would to manipulate a bezier segment) the bezier handles appear and so both the arc handles and bezier handles are visible.
Let me know if there is interest in this feature and I will start hunting this bug.
Thanks,
Dan.
Jabiertxof (jabiertxof) wrote : | # |
When compiling wirh CMAKE I get this error:
[ 81%] Building CXX object src/CMakeFiles/
make[2]: *** No rule to make target 'src/inkscape-
CMakeFiles/
make[1]: *** [src/CMakeFiles
Makefile:127: recipe for target 'all' failed
make: *** [all] Error 2
Unmerged revisions
- 14893. By Daniel Bailey <email address hidden>
-
Merge upstream changes
- 14892. By Daniel Bailey <email address hidden>
-
fix bug where closing arc segment sometimes becomes as straight line
- 14891. By Daniel Bailey <email address hidden>
-
Handle conversion from arcs to beziers in node editor
- 14890. By Daniel Bailey <email address hidden>
-
fix TODO for arc segment handling
- 14889. By Daniel Bailey <email address hidden>
-
Added support for editting arc segments in the node editor
Preview Diff
1 | === modified file 'share/icons/icons.svg' | |||
2 | --- share/icons/icons.svg 2016-04-14 19:09:11 +0000 | |||
3 | +++ share/icons/icons.svg 2016-07-09 15:52:46 +0000 | |||
4 | @@ -1109,6 +1109,29 @@ | |||
5 | 1109 | <use xlink:href="#rect4374" height="1250" width="1250" id="use5756" y="0" x="0" transform="translate(-5.018707,-0.0241656)" /> | 1109 | <use xlink:href="#rect4374" height="1250" width="1250" id="use5756" y="0" x="0" transform="translate(-5.018707,-0.0241656)" /> |
6 | 1110 | <use xlink:href="#rect4374" height="1250" width="1250" id="use5758" y="0" x="0" transform="translate(6.0238,-11.02417)" /> | 1110 | <use xlink:href="#rect4374" height="1250" width="1250" id="use5758" y="0" x="0" transform="translate(6.0238,-11.02417)" /> |
7 | 1111 | </g> | 1111 | </g> |
8 | 1112 | <g id="node-segment-elliptical-arc" transform="translate(673.00134,25.751197)" inkscape:label="#node_curve"> | ||
9 | 1113 | <path style="fill:none;stroke:#646464;stroke-width:1.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1" d="m -38.600913,188.52882 a 6.0591792,7.0458508 48.505394 1 1 7.123238,-5.61527" id="path10679-5" sodipodi:nodetypes="cc" inkscape:connector-curvature="0" /> | ||
10 | 1114 | <rect y="175" x="-45" height="16" width="16" id="rect10681-3" style="color:#000000;display:inline;fill:none;stroke:none;stroke-width:1;marker:none" /> | ||
11 | 1115 | <use xlink:href="#rect4374" height="1250" width="1250" id="use5745-8" y="0" x="0" transform="translate(-1.0764281,-0.01573529)" /> | ||
12 | 1116 | <use xlink:href="#rect4374" height="1250" width="1250" id="use5747-9" y="0" x="0" transform="translate(5.9949619,-5.5106093)" /> | ||
13 | 1117 | </g> | ||
14 | 1118 | <g id="node-segment-elliptical-arc-bulge" transform="translate(693.93498,25.748197)" inkscape:label="#node_curve"> | ||
15 | 1119 | <rect y="175" x="-45" height="16" width="16" id="rect10681-3-6" style="color:#000000;display:inline;fill:none;stroke:none;stroke-width:1;marker:none" /> | ||
16 | 1120 | <path style="fill:none;stroke:#646464;stroke-width:1.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1" d="m -37.870441,189.45456 a 6.0591792,7.0458508 48.505394 1 1 7.123238,-5.61527" id="path10679-5-2" sodipodi:nodetypes="cc" inkscape:connector-curvature="0" /> | ||
17 | 1121 | <path style="fill:none;stroke:#ff8080;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:1, 2;stroke-dashoffset:0;stroke-opacity:1" d="m -37.870438,189.45456 a 6.0591792,7.0458508 48.505394 0 1 7.12324,-5.61527" id="path10679-5-2-8" sodipodi:nodetypes="cc" inkscape:connector-curvature="0" /> | ||
18 | 1122 | </g> | ||
19 | 1123 | <g id="node-segment-elliptical-arc-flip" transform="translate(736.50086,25.748197)" inkscape:label="#node_curve"> | ||
20 | 1124 | <rect y="175" x="-45" height="16" width="16" id="rect10681-3-67" style="color:#000000;display:inline;fill:none;stroke:none;stroke-width:1;marker:none" /> | ||
21 | 1125 | <path style="fill:none;stroke:#646464;stroke-width:1.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1" d="m -40.865556,188.5577 a 5.0424426,7.9529262 56.470438 1 1 7.281336,-12.07083" id="path10679-5-5" sodipodi:nodetypes="cc" inkscape:connector-curvature="0" /> | ||
22 | 1126 | <path style="fill:none;stroke:#ff5555;stroke-width:1.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1" d="m -40.866246,188.55761 a 5.0424427,7.9529265 56.470438 1 0 7.28134,-12.07083" id="path10679-5-5-6" sodipodi:nodetypes="cc" inkscape:connector-curvature="0" /> | ||
23 | 1127 | <use xlink:href="#path4376" height="1250" width="1250" id="use5754-7" y="0" x="0" transform="rotate(90,-38.492674,181.50732)" /> | ||
24 | 1128 | <use xlink:href="#path4376" height="1250" width="1250" id="use5754-7-9" y="0" x="0" transform="matrix(0,1,1,0,-217.00767,220)" /> | ||
25 | 1129 | </g> | ||
26 | 1130 | <g id="node-segment-elliptical-arc-shallow" transform="translate(714.9873,25.748197)" inkscape:label="#node_curve"> | ||
27 | 1131 | <rect y="175" x="-45" height="16" width="16" id="rect10681-3-6-6" style="color:#000000;display:inline;fill:none;stroke:none;stroke-width:1;marker:none" /> | ||
28 | 1132 | <path style="fill:none;stroke:#646464;stroke-width:1.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1" d="m -37.870441,189.45456 a 6.0591792,7.0458508 48.505394 0 1 7.123238,-5.61527" id="path10679-5-2-3" sodipodi:nodetypes="cc" inkscape:connector-curvature="0" /> | ||
29 | 1133 | <path style="fill:none;stroke:#ff8080;stroke-width:1.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:1.00000024, 2.00000048;stroke-dashoffset:0;stroke-opacity:1" d="m -37.870438,189.45456 a 6.0591792,7.0458508 48.505394 1 1 7.12324,-5.61527" id="path10679-5-2-8-2" sodipodi:nodetypes="cc" inkscape:connector-curvature="0" /> | ||
30 | 1134 | </g> | ||
31 | 1112 | <g id="object-to-path" transform="translate(280.041,-149.9465)" inkscape:label="#object_tocurve"> | 1135 | <g id="object-to-path" transform="translate(280.041,-149.9465)" inkscape:label="#object_tocurve"> |
32 | 1113 | <path style="fill:none;stroke:#646464;stroke-width:1.0000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0" d="M -37.0131,188.9855 C -35.0131,192.9855 -27.81159,188.3079 -29.81159,185.3079 -31.10674,183.3652 -39.24159,185.6729 -42.56979,183.4541 -45.56979,181.4541 -45.12074,174.9746 -40.0534,175.5422 -35.41611,176.0661 -38.62555,185.7606 -37.0131,188.9855 Z" id="path4438" sodipodi:nodetypes="cssss" inkscape:connector-curvature="0" /> | 1136 | <path style="fill:none;stroke:#646464;stroke-width:1.0000002;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0" d="M -37.0131,188.9855 C -35.0131,192.9855 -27.81159,188.3079 -29.81159,185.3079 -31.10674,183.3652 -39.24159,185.6729 -42.56979,183.4541 -45.56979,181.4541 -45.12074,174.9746 -40.0534,175.5422 -35.41611,176.0661 -38.62555,185.7606 -37.0131,188.9855 Z" id="path4438" sodipodi:nodetypes="cssss" inkscape:connector-curvature="0" /> |
33 | 1114 | <rect y="175" x="-45" height="16" width="16" id="rect4440" style="color:#000000;fill:none" /> | 1137 | <rect y="175" x="-45" height="16" width="16" id="rect4440" style="color:#000000;fill:none" /> |
34 | 1115 | 1138 | ||
35 | === modified file 'src/helper/geom.cpp' | |||
36 | --- src/helper/geom.cpp 2015-05-08 17:26:29 +0000 | |||
37 | +++ src/helper/geom.cpp 2016-07-09 15:52:46 +0000 | |||
38 | @@ -456,6 +456,48 @@ | |||
39 | 456 | 456 | ||
40 | 457 | //################################################################################# | 457 | //################################################################################# |
41 | 458 | 458 | ||
42 | 459 | /** | ||
43 | 460 | * Converts all segments in all paths to Geom::LineSegment or Geom::HLineSegment or | ||
44 | 461 | * Geom::VLineSegment or Geom::CubicBezier or Geom::EllipticalArc. | ||
45 | 462 | */ | ||
46 | 463 | Geom::PathVector | ||
47 | 464 | pathv_to_linear_and_cubic_beziers_and_arcs( Geom::PathVector const &pathv ) | ||
48 | 465 | { | ||
49 | 466 | Geom::PathVector output; | ||
50 | 467 | |||
51 | 468 | for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) { | ||
52 | 469 | output.push_back( Geom::Path() ); | ||
53 | 470 | output.back().setStitching(true); | ||
54 | 471 | output.back().start( pit->initialPoint() ); | ||
55 | 472 | |||
56 | 473 | for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) { | ||
57 | 474 | if (is_straight_curve(*cit)) { | ||
58 | 475 | Geom::LineSegment l(cit->initialPoint(), cit->finalPoint()); | ||
59 | 476 | output.back().append(l); | ||
60 | 477 | } else { | ||
61 | 478 | Geom::EllipticalArc const *arc = dynamic_cast<Geom::EllipticalArc const *>(&*cit); | ||
62 | 479 | if (arc){ | ||
63 | 480 | output.back().append(arc->duplicate()); | ||
64 | 481 | } | ||
65 | 482 | else{ | ||
66 | 483 | Geom::BezierCurve const *curve = dynamic_cast<Geom::BezierCurve const *>(&*cit); | ||
67 | 484 | if (curve && curve->order() == 3) { | ||
68 | 485 | Geom::CubicBezier b((*curve)[0], (*curve)[1], (*curve)[2], (*curve)[3]); | ||
69 | 486 | output.back().append(b); | ||
70 | 487 | } else { | ||
71 | 488 | // convert all other curve types to cubicbeziers | ||
72 | 489 | Geom::Path cubicbezier_path = Geom::cubicbezierpath_from_sbasis(cit->toSBasis(), 0.1); | ||
73 | 490 | cubicbezier_path.close(false); | ||
74 | 491 | output.back().append(cubicbezier_path); | ||
75 | 492 | } | ||
76 | 493 | } | ||
77 | 494 | } | ||
78 | 495 | } | ||
79 | 496 | output.back().close( pit->closed() ); | ||
80 | 497 | } | ||
81 | 498 | return output; | ||
82 | 499 | } | ||
83 | 500 | |||
84 | 459 | /* | 501 | /* |
85 | 460 | * Converts all segments in all paths to Geom::LineSegment or Geom::HLineSegment or | 502 | * Converts all segments in all paths to Geom::LineSegment or Geom::HLineSegment or |
86 | 461 | * Geom::VLineSegment or Geom::CubicBezier. | 503 | * Geom::VLineSegment or Geom::CubicBezier. |
87 | 462 | 504 | ||
88 | === modified file 'src/helper/geom.h' | |||
89 | --- src/helper/geom.h 2015-05-08 15:40:38 +0000 | |||
90 | +++ src/helper/geom.h 2016-07-09 15:52:46 +0000 | |||
91 | @@ -25,6 +25,7 @@ | |||
92 | 25 | Geom::Rect *bbox, int *wind, Geom::Coord *dist, | 25 | Geom::Rect *bbox, int *wind, Geom::Coord *dist, |
93 | 26 | Geom::Coord tolerance, Geom::Rect const *viewbox); | 26 | Geom::Coord tolerance, Geom::Rect const *viewbox); |
94 | 27 | 27 | ||
95 | 28 | Geom::PathVector pathv_to_linear_and_cubic_beziers_and_arcs( Geom::PathVector const &pathv ); | ||
96 | 28 | Geom::PathVector pathv_to_linear_and_cubic_beziers( Geom::PathVector const &pathv ); | 29 | Geom::PathVector pathv_to_linear_and_cubic_beziers( Geom::PathVector const &pathv ); |
97 | 29 | Geom::PathVector pathv_to_linear( Geom::PathVector const &pathv, double maxdisp ); | 30 | Geom::PathVector pathv_to_linear( Geom::PathVector const &pathv, double maxdisp ); |
98 | 30 | Geom::PathVector pathv_to_cubicbezier( Geom::PathVector const &pathv); | 31 | Geom::PathVector pathv_to_cubicbezier( Geom::PathVector const &pathv); |
99 | 31 | 32 | ||
100 | === modified file 'src/ui/tool/control-point.cpp' | |||
101 | --- src/ui/tool/control-point.cpp 2015-05-30 18:27:42 +0000 | |||
102 | +++ src/ui/tool/control-point.cpp 2016-07-09 15:52:46 +0000 | |||
103 | @@ -225,7 +225,6 @@ | |||
104 | 225 | { | 225 | { |
105 | 226 | // NOTE the static variables below are shared for all points! | 226 | // NOTE the static variables below are shared for all points! |
106 | 227 | // TODO handle clicks and drags from other buttons too | 227 | // TODO handle clicks and drags from other buttons too |
107 | 228 | |||
108 | 229 | if (event == NULL) | 228 | if (event == NULL) |
109 | 230 | { | 229 | { |
110 | 231 | return false; | 230 | return false; |
111 | 232 | 231 | ||
112 | === modified file 'src/ui/tool/multi-path-manipulator.cpp' | |||
113 | --- src/ui/tool/multi-path-manipulator.cpp 2015-08-08 20:19:02 +0000 | |||
114 | +++ src/ui/tool/multi-path-manipulator.cpp 2016-07-09 15:52:46 +0000 | |||
115 | @@ -319,11 +319,38 @@ | |||
116 | 319 | { | 319 | { |
117 | 320 | if (_selection.empty()) return; | 320 | if (_selection.empty()) return; |
118 | 321 | invokeForAll(&PathManipulator::setSegmentType, type); | 321 | invokeForAll(&PathManipulator::setSegmentType, type); |
124 | 322 | if (type == SEGMENT_STRAIGHT) { | 322 | switch (type){ |
125 | 323 | _done(_("Straighten segments")); | 323 | case SEGMENT_STRAIGHT: |
126 | 324 | } else { | 324 | _done(_("Straighten segments")); |
127 | 325 | _done(_("Make segments curves")); | 325 | break; |
128 | 326 | } | 326 | case SEGMENT_CUBIC_BEZIER: |
129 | 327 | _done(_("Make segments curves")); | ||
130 | 328 | break; | ||
131 | 329 | case SEGMENT_ELIPTICAL_ARC: | ||
132 | 330 | _done(_("Make segments arcs")); | ||
133 | 331 | break; | ||
134 | 332 | default: | ||
135 | 333 | g_error("Unknown segment type"); | ||
136 | 334 | } | ||
137 | 335 | } | ||
138 | 336 | |||
139 | 337 | void MultiPathManipulator::setArcSegmentLarge(bool large) | ||
140 | 338 | { | ||
141 | 339 | if (_selection.empty()) return; | ||
142 | 340 | invokeForAll(&PathManipulator::setArcSegmentLarge, large); | ||
143 | 341 | if (large){ | ||
144 | 342 | _done(_("Make arc segments bulge")); | ||
145 | 343 | } | ||
146 | 344 | else{ | ||
147 | 345 | _done(_("Make arc segments shallow")); | ||
148 | 346 | } | ||
149 | 347 | } | ||
150 | 348 | |||
151 | 349 | void MultiPathManipulator::toggleArcSegmentSweep() | ||
152 | 350 | { | ||
153 | 351 | if (_selection.empty()) return; | ||
154 | 352 | invokeForAll(&PathManipulator::toggleArcSegmentSweep); | ||
155 | 353 | _done(_("Flip arc segments")); | ||
156 | 327 | } | 354 | } |
157 | 328 | 355 | ||
158 | 329 | void MultiPathManipulator::insertNodes() | 356 | void MultiPathManipulator::insertNodes() |
159 | 330 | 357 | ||
160 | === modified file 'src/ui/tool/multi-path-manipulator.h' | |||
161 | --- src/ui/tool/multi-path-manipulator.h 2015-05-30 18:27:42 +0000 | |||
162 | +++ src/ui/tool/multi-path-manipulator.h 2016-07-09 15:52:46 +0000 | |||
163 | @@ -51,6 +51,9 @@ | |||
164 | 51 | void setNodeType(NodeType t); | 51 | void setNodeType(NodeType t); |
165 | 52 | void setSegmentType(SegmentType t); | 52 | void setSegmentType(SegmentType t); |
166 | 53 | 53 | ||
167 | 54 | void setArcSegmentLarge(bool large); | ||
168 | 55 | void toggleArcSegmentSweep(); | ||
169 | 56 | |||
170 | 54 | void insertNodesAtExtrema(ExtremumType extremum); | 57 | void insertNodesAtExtrema(ExtremumType extremum); |
171 | 55 | void insertNodes(); | 58 | void insertNodes(); |
172 | 56 | void insertNode(Geom::Point pt); | 59 | void insertNode(Geom::Point pt); |
173 | 57 | 60 | ||
174 | === modified file 'src/ui/tool/node-types.h' | |||
175 | --- src/ui/tool/node-types.h 2010-11-17 02:12:56 +0000 | |||
176 | +++ src/ui/tool/node-types.h 2016-07-09 15:52:46 +0000 | |||
177 | @@ -28,7 +28,8 @@ | |||
178 | 28 | /** Types of segments supported in the node tool. */ | 28 | /** Types of segments supported in the node tool. */ |
179 | 29 | enum SegmentType { | 29 | enum SegmentType { |
180 | 30 | SEGMENT_STRAIGHT, ///< Straight linear segment | 30 | SEGMENT_STRAIGHT, ///< Straight linear segment |
182 | 31 | SEGMENT_CUBIC_BEZIER ///< Bezier curve with two control points | 31 | SEGMENT_CUBIC_BEZIER, ///< Bezier curve with two control points |
183 | 32 | SEGMENT_ELIPTICAL_ARC ///< Eliptical arc (two radii, rotation, size flag, direction flag) | ||
184 | 32 | }; | 33 | }; |
185 | 33 | 34 | ||
186 | 34 | } // namespace UI | 35 | } // namespace UI |
187 | 35 | 36 | ||
188 | === modified file 'src/ui/tool/node.cpp' | |||
189 | --- src/ui/tool/node.cpp 2016-05-29 10:13:55 +0000 | |||
190 | +++ src/ui/tool/node.cpp 2016-07-09 15:52:46 +0000 | |||
191 | @@ -112,7 +112,8 @@ | |||
192 | 112 | _handle_colors, data.handle_group), | 112 | _handle_colors, data.handle_group), |
193 | 113 | _parent(parent), | 113 | _parent(parent), |
194 | 114 | _handle_line(ControlManager::getManager().createControlLine(data.handle_line_group)), | 114 | _handle_line(ControlManager::getManager().createControlLine(data.handle_line_group)), |
196 | 115 | _degenerate(true) | 115 | _degenerate(true), |
197 | 116 | _offset(Geom::Point())// Start with 0 offset | ||
198 | 116 | { | 117 | { |
199 | 117 | setVisible(false); | 118 | setVisible(false); |
200 | 118 | } | 119 | } |
201 | @@ -184,8 +185,8 @@ | |||
202 | 184 | 185 | ||
203 | 185 | if (_parent->type() == NODE_SMOOTH && Node::_is_line_segment(_parent, node_away)) { | 186 | if (_parent->type() == NODE_SMOOTH && Node::_is_line_segment(_parent, node_away)) { |
204 | 186 | // restrict movement to the line joining the nodes | 187 | // restrict movement to the line joining the nodes |
207 | 187 | Geom::Point direction = _parent->position() - node_away->position(); | 188 | Geom::Point direction = _parent->position() + _offset - node_away->position(); |
208 | 188 | Geom::Point delta = new_pos - _parent->position(); | 189 | Geom::Point delta = new_pos - _parent->position() + _offset; |
209 | 189 | // project the relative position on the direction line | 190 | // project the relative position on the direction line |
210 | 190 | Geom::Point new_delta = (Geom::dot(delta, direction) | 191 | Geom::Point new_delta = (Geom::dot(delta, direction) |
211 | 191 | / Geom::L2sq(direction)) * direction; | 192 | / Geom::L2sq(direction)) * direction; |
212 | @@ -212,7 +213,7 @@ | |||
213 | 212 | } break; | 213 | } break; |
214 | 213 | case NODE_SYMMETRIC: | 214 | case NODE_SYMMETRIC: |
215 | 214 | // for symmetric nodes, place the other handle on the opposite side | 215 | // for symmetric nodes, place the other handle on the opposite side |
217 | 215 | other->setRelativePos(-(new_pos - _parent->position())); | 216 | other->setRelativePos(-(new_pos - _parent->position() + _offset)); |
218 | 216 | break; | 217 | break; |
219 | 217 | default: break; | 218 | default: break; |
220 | 218 | } | 219 | } |
221 | @@ -229,10 +230,10 @@ | |||
222 | 229 | void Handle::setPosition(Geom::Point const &p) | 230 | void Handle::setPosition(Geom::Point const &p) |
223 | 230 | { | 231 | { |
224 | 231 | ControlPoint::setPosition(p); | 232 | ControlPoint::setPosition(p); |
226 | 232 | _handle_line->setCoords(_parent->position(), position()); | 233 | _handle_line->setCoords(_parent->position() + _offset, position()); |
227 | 233 | 234 | ||
228 | 234 | // update degeneration info and visibility | 235 | // update degeneration info and visibility |
230 | 235 | if (Geom::are_near(position(), _parent->position())) | 236 | if (Geom::are_near(position(), _parent->position() + _offset)) |
231 | 236 | _degenerate = true; | 237 | _degenerate = true; |
232 | 237 | else _degenerate = false; | 238 | else _degenerate = false; |
233 | 238 | 239 | ||
234 | @@ -243,6 +244,15 @@ | |||
235 | 243 | } | 244 | } |
236 | 244 | } | 245 | } |
237 | 245 | 246 | ||
238 | 247 | /** Set the offset between the parent node and the origin */ | ||
239 | 248 | void Handle::setOffset(Geom::Point const &offset){ | ||
240 | 249 | _offset = offset; | ||
241 | 250 | _handle_line->setCoords(_parent->position() + _offset, position()); | ||
242 | 251 | } | ||
243 | 252 | |||
244 | 253 | /** Set the length of the handle, if the handle is degenerate then | ||
245 | 254 | the function returns without changing the handle position as | ||
246 | 255 | it cannot determine the desired direction of the handle */ | ||
247 | 246 | void Handle::setLength(double len) | 256 | void Handle::setLength(double len) |
248 | 247 | { | 257 | { |
249 | 248 | if (isDegenerate()) return; | 258 | if (isDegenerate()) return; |
250 | @@ -250,9 +260,30 @@ | |||
251 | 250 | setRelativePos(dir * len); | 260 | setRelativePos(dir * len); |
252 | 251 | } | 261 | } |
253 | 252 | 262 | ||
254 | 263 | /** Set the angle of the handle, if the handle is degenerate then | ||
255 | 264 | the function returns without changing the handle position as | ||
256 | 265 | it is not possible to set the orientation of a zero length line */ | ||
257 | 266 | void Handle::setAngle(Geom::Angle a){ | ||
258 | 267 | if (isDegenerate()) return; | ||
259 | 268 | setRelativePos(Geom::Point::polar(a, length())); | ||
260 | 269 | } | ||
261 | 270 | |||
262 | 271 | /** Set the angle and the length of the handle by moving the handle position | ||
263 | 272 | unlike Handle::setLength and Handle::setAngle, this function works for | ||
264 | 273 | degenerate handles as both the desired length and angle are known*/ | ||
265 | 274 | void Handle::setAngleAndLength(Geom::Angle a, double len){ | ||
266 | 275 | // Degenerate case can be handled by setting the handle relative position | ||
267 | 276 | // to be any vector of non-zero length. A unit vector is used here. | ||
268 | 277 | if (isDegenerate()) { | ||
269 | 278 | setRelativePos(Geom::Point(0,1)); | ||
270 | 279 | } | ||
271 | 280 | setAngle(a); | ||
272 | 281 | setLength(len); | ||
273 | 282 | } | ||
274 | 283 | |||
275 | 253 | void Handle::retract() | 284 | void Handle::retract() |
276 | 254 | { | 285 | { |
278 | 255 | move(_parent->position()); | 286 | move(_parent->position() + _offset); |
279 | 256 | } | 287 | } |
280 | 257 | 288 | ||
281 | 258 | void Handle::setDirection(Geom::Point const &from, Geom::Point const &to) | 289 | void Handle::setDirection(Geom::Point const &from, Geom::Point const &to) |
282 | @@ -428,6 +459,7 @@ | |||
283 | 428 | new_pos=_last_drag_origin(); | 459 | new_pos=_last_drag_origin(); |
284 | 429 | } | 460 | } |
285 | 430 | move(new_pos); // needed for correct update, even though it's redundant | 461 | move(new_pos); // needed for correct update, even though it's redundant |
286 | 462 | _parent->updateArcHandleConstriants(this); | ||
287 | 431 | _pm().update(); | 463 | _pm().update(); |
288 | 432 | } | 464 | } |
289 | 433 | 465 | ||
290 | @@ -585,6 +617,10 @@ | |||
291 | 585 | node_colors, data.node_group), | 617 | node_colors, data.node_group), |
292 | 586 | _front(data, initial_pos, this), | 618 | _front(data, initial_pos, this), |
293 | 587 | _back(data, initial_pos, this), | 619 | _back(data, initial_pos, this), |
294 | 620 | _arc_rx(data, initial_pos, this), | ||
295 | 621 | _arc_ry(data, initial_pos, this), | ||
296 | 622 | _arc_large(true), | ||
297 | 623 | _arc_sweep(false), | ||
298 | 588 | _type(NODE_CUSP), | 624 | _type(NODE_CUSP), |
299 | 589 | _handles_shown(false) | 625 | _handles_shown(false) |
300 | 590 | { | 626 | { |
301 | @@ -622,6 +658,52 @@ | |||
302 | 622 | } | 658 | } |
303 | 623 | } | 659 | } |
304 | 624 | 660 | ||
305 | 661 | /** Update constrained arc handles given a new constraint. | ||
306 | 662 | This is used to keep the arc radius handles at right angles | ||
307 | 663 | @param constraint Pointer to the handle that was forced to a new position. | ||
308 | 664 | If the constraint is anything other than _arc_rx or _arc_ry then this function does nothing */ | ||
309 | 665 | void Node::updateArcHandleConstriants(Handle *constraint) | ||
310 | 666 | { | ||
311 | 667 | Handle *updateTarget = NULL; | ||
312 | 668 | Geom::Angle rotation = 0.0; | ||
313 | 669 | // Check which handle moved and set the update target and rotation | ||
314 | 670 | if (constraint == &_arc_rx){ updateTarget = &_arc_ry; rotation = Geom::rad_from_deg( 90.0); } | ||
315 | 671 | else if (constraint == &_arc_ry){ updateTarget = &_arc_rx; rotation = Geom::rad_from_deg(-90.0); } | ||
316 | 672 | else return; | ||
317 | 673 | // Calculate the new position and move the constrained handle (updateTarget) | ||
318 | 674 | Geom::Angle newAngle = constraint->angle() + rotation; | ||
319 | 675 | Geom::Coord newLength = updateTarget->length(); | ||
320 | 676 | Geom::Point newPos = Geom::Point::polar(newAngle, newLength); | ||
321 | 677 | updateTarget->setRelativePos(newPos); | ||
322 | 678 | } | ||
323 | 679 | |||
324 | 680 | /** Move the arc handles so that their origin is half way along the line between the nodes */ | ||
325 | 681 | void Node::moveArcHandles(Geom::Point lineBetweenNodes, double lenX, double lenY, Geom::Angle rotX, Geom::Angle rotY) | ||
326 | 682 | { | ||
327 | 683 | // Calculate the mid point between the two nodes, put the origin of the handles there. | ||
328 | 684 | // This usually changes the length and angle of the handle lines | ||
329 | 685 | Geom::Point midPointOffset = lineBetweenNodes / 2.0; | ||
330 | 686 | _arc_rx.setOffset(midPointOffset); | ||
331 | 687 | _arc_ry.setOffset(midPointOffset); | ||
332 | 688 | |||
333 | 689 | _arc_rx.setPosition(position()); | ||
334 | 690 | _arc_ry.setPosition(position()); | ||
335 | 691 | |||
336 | 692 | // Apply the stored angle and length | ||
337 | 693 | _arc_rx.setAngleAndLength(rotX, lenX); | ||
338 | 694 | _arc_ry.setAngleAndLength(rotY, lenY); | ||
339 | 695 | |||
340 | 696 | } | ||
341 | 697 | |||
342 | 698 | /** Retract this node's handles for controling arcs */ | ||
343 | 699 | void Node::retractArcHandles() | ||
344 | 700 | { | ||
345 | 701 | _arc_rx.setOffset(Geom::Point()); | ||
346 | 702 | _arc_ry.setOffset(Geom::Point()); | ||
347 | 703 | _arc_rx.retract(); | ||
348 | 704 | _arc_ry.retract(); | ||
349 | 705 | } | ||
350 | 706 | |||
351 | 625 | void Node::move(Geom::Point const &new_pos) | 707 | void Node::move(Geom::Point const &new_pos) |
352 | 626 | { | 708 | { |
353 | 627 | // move handles when the node moves. | 709 | // move handles when the node moves. |
354 | @@ -643,11 +725,47 @@ | |||
355 | 643 | nextNodeWeight = _pm()._bsplineHandlePosition(nextNode->back()); | 725 | nextNodeWeight = _pm()._bsplineHandlePosition(nextNode->back()); |
356 | 644 | } | 726 | } |
357 | 645 | 727 | ||
358 | 728 | // Save the lengths and angles of the two arc handles to apply again after the node is moved. | ||
359 | 729 | double lenX, lenY; | ||
360 | 730 | Geom::Angle rotX, rotY; | ||
361 | 731 | lenX = _arc_rx.length(); lenY = _arc_ry.length(); | ||
362 | 732 | rotX = _arc_rx.angle(); rotY = _arc_ry.angle(); | ||
363 | 733 | |||
364 | 646 | setPosition(new_pos); | 734 | setPosition(new_pos); |
365 | 647 | 735 | ||
366 | 736 | // _front and _back are translated with the node to which they belong. | ||
367 | 737 | // This means that if they were degenrate then they are still degenerate. | ||
368 | 648 | _front.setPosition(_front.position() + delta); | 738 | _front.setPosition(_front.position() + delta); |
369 | 649 | _back.setPosition(_back.position() + delta); | 739 | _back.setPosition(_back.position() + delta); |
370 | 650 | 740 | ||
371 | 741 | // For arc manipulation handles, the position relative to their parent nodes | ||
372 | 742 | // may change as the node moves. For this reason, the degenerate case must be | ||
373 | 743 | // handled as a special case. | ||
374 | 744 | if (nextNode) { // Must have a next node for this to be an arc segment | ||
375 | 745 | if (_arc_rx.isDegenerate() || _arc_ry.isDegenerate()){ | ||
376 | 746 | retractArcHandles(); | ||
377 | 747 | } | ||
378 | 748 | else{ | ||
379 | 749 | moveArcHandles(nextNode->position() - position(), lenX, lenY, rotX, rotY); | ||
380 | 750 | } | ||
381 | 751 | } | ||
382 | 752 | // If this is the end of an arc segment then the arc data for prevNode needs updating | ||
383 | 753 | if (prevNode){ | ||
384 | 754 | if (prevNode->arc_rx()->isDegenerate() || prevNode->arc_ry()->isDegenerate()){ | ||
385 | 755 | prevNode->retractArcHandles(); | ||
386 | 756 | } | ||
387 | 757 | else{ | ||
388 | 758 | // In this case, the parent node for the arc handles hasn't moved, | ||
389 | 759 | // for this reason, we didn't need to save the length and rotation earlier, | ||
390 | 760 | // we can get the length and rotation now. | ||
391 | 761 | lenX = prevNode->arc_rx()->length(); | ||
392 | 762 | lenY = prevNode->arc_ry()->length(); | ||
393 | 763 | rotX = prevNode->arc_rx()->angle(); | ||
394 | 764 | rotY = prevNode->arc_ry()->angle(); | ||
395 | 765 | prevNode->moveArcHandles(n->position() - prevNode->position(), lenX, lenY, rotX, rotY); | ||
396 | 766 | } | ||
397 | 767 | } | ||
398 | 768 | |||
399 | 651 | // if the node has a smooth handle after a line segment, it should be kept colinear | 769 | // if the node has a smooth handle after a line segment, it should be kept colinear |
400 | 652 | // with the segment | 770 | // with the segment |
401 | 653 | _fixNeighbors(old_pos, new_pos); | 771 | _fixNeighbors(old_pos, new_pos); |
402 | @@ -667,7 +785,6 @@ | |||
403 | 667 | 785 | ||
404 | 668 | void Node::transform(Geom::Affine const &m) | 786 | void Node::transform(Geom::Affine const &m) |
405 | 669 | { | 787 | { |
406 | 670 | |||
407 | 671 | Geom::Point old_pos = position(); | 788 | Geom::Point old_pos = position(); |
408 | 672 | 789 | ||
409 | 673 | // save the previous nodes strength to apply it again once the node is moved | 790 | // save the previous nodes strength to apply it again once the node is moved |
410 | @@ -685,10 +802,26 @@ | |||
411 | 685 | nextNodeWeight = _pm()._bsplineHandlePosition(nextNode->back()); | 802 | nextNodeWeight = _pm()._bsplineHandlePosition(nextNode->back()); |
412 | 686 | } | 803 | } |
413 | 687 | 804 | ||
414 | 805 | // Save the lengths and angles of the two arc handles to apply again after the node is moved. | ||
415 | 806 | double lenX, lenY; | ||
416 | 807 | Geom::Angle rotX, rotY; | ||
417 | 808 | lenX = _arc_rx.length(); lenY = _arc_ry.length(); | ||
418 | 809 | rotX = _arc_rx.angle(); rotY = _arc_ry.angle(); | ||
419 | 810 | |||
420 | 688 | setPosition(position() * m); | 811 | setPosition(position() * m); |
421 | 689 | _front.setPosition(_front.position() * m); | 812 | _front.setPosition(_front.position() * m); |
422 | 690 | _back.setPosition(_back.position() * m); | 813 | _back.setPosition(_back.position() * m); |
423 | 691 | 814 | ||
424 | 815 | |||
425 | 816 | if (nextNode){ | ||
426 | 817 | if (_arc_rx.isDegenerate() || _arc_ry.isDegenerate()){ | ||
427 | 818 | retractArcHandles(); | ||
428 | 819 | } | ||
429 | 820 | else{ | ||
430 | 821 | moveArcHandles(nextNode->position() - position(), lenX, lenY, rotX, rotY); | ||
431 | 822 | } | ||
432 | 823 | } | ||
433 | 824 | |||
434 | 692 | /* Affine transforms keep handle invariants for smooth and symmetric nodes, | 825 | /* Affine transforms keep handle invariants for smooth and symmetric nodes, |
435 | 693 | * but smooth nodes at ends of linear segments and auto nodes need special treatment */ | 826 | * but smooth nodes at ends of linear segments and auto nodes need special treatment */ |
436 | 694 | _fixNeighbors(old_pos, position()); | 827 | _fixNeighbors(old_pos, position()); |
437 | @@ -789,7 +922,12 @@ | |||
438 | 789 | if (!_back.isDegenerate()) { | 922 | if (!_back.isDegenerate()) { |
439 | 790 | _back.setVisible(v); | 923 | _back.setVisible(v); |
440 | 791 | } | 924 | } |
442 | 792 | 925 | if (!_arc_rx.isDegenerate()) { | |
443 | 926 | _arc_rx.setVisible(v); | ||
444 | 927 | } | ||
445 | 928 | if (!_arc_ry.isDegenerate()) { | ||
446 | 929 | _arc_ry.setVisible(v); | ||
447 | 930 | } | ||
448 | 793 | } | 931 | } |
449 | 794 | 932 | ||
450 | 795 | void Node::updateHandles() | 933 | void Node::updateHandles() |
451 | @@ -798,6 +936,8 @@ | |||
452 | 798 | 936 | ||
453 | 799 | _front._handleControlStyling(); | 937 | _front._handleControlStyling(); |
454 | 800 | _back._handleControlStyling(); | 938 | _back._handleControlStyling(); |
455 | 939 | _arc_rx._handleControlStyling(); | ||
456 | 940 | _arc_ry._handleControlStyling(); | ||
457 | 801 | } | 941 | } |
458 | 802 | 942 | ||
459 | 803 | 943 | ||
460 | @@ -1332,7 +1472,6 @@ | |||
461 | 1332 | } | 1472 | } |
462 | 1333 | 1473 | ||
463 | 1334 | sm.unSetup(); | 1474 | sm.unSetup(); |
464 | 1335 | |||
465 | 1336 | SelectableControlPoint::dragged(new_pos, event); | 1475 | SelectableControlPoint::dragged(new_pos, event); |
466 | 1337 | } | 1476 | } |
467 | 1338 | 1477 | ||
468 | @@ -1361,6 +1500,15 @@ | |||
469 | 1361 | return SnapCandidatePoint(position(), _snapSourceType(), _snapTargetType()); | 1500 | return SnapCandidatePoint(position(), _snapSourceType(), _snapTargetType()); |
470 | 1362 | } | 1501 | } |
471 | 1363 | 1502 | ||
472 | 1503 | Geom::EllipticalArc Node::getEllipticalArc(){ | ||
473 | 1504 | // Must have a next node for this to be an arc | ||
474 | 1505 | if (not _next()) return Geom::EllipticalArc(); | ||
475 | 1506 | Geom::Point r = Geom::Point(_arc_rx.length(), _arc_ry.length()); | ||
476 | 1507 | Geom::Coord rot = _arc_rx.angle(); | ||
477 | 1508 | // Create an arc and return it | ||
478 | 1509 | return Geom::EllipticalArc(position(), r, rot, _arc_large, _arc_sweep, _next()->position()); | ||
479 | 1510 | } | ||
480 | 1511 | |||
481 | 1364 | Handle *Node::handleToward(Node *to) | 1512 | Handle *Node::handleToward(Node *to) |
482 | 1365 | { | 1513 | { |
483 | 1366 | if (_next() == to) { | 1514 | if (_next() == to) { |
484 | @@ -1378,7 +1526,7 @@ | |||
485 | 1378 | if (front() == dir) { | 1526 | if (front() == dir) { |
486 | 1379 | return _next(); | 1527 | return _next(); |
487 | 1380 | } | 1528 | } |
489 | 1381 | if (back() == dir) { | 1529 | if ( (back() == dir) || (arc_rx() == dir) || (arc_ry() == dir) ) { |
490 | 1382 | return _prev(); | 1530 | return _prev(); |
491 | 1383 | } | 1531 | } |
492 | 1384 | g_error("Node::nodeToward(): handle is not a child of this node!"); | 1532 | g_error("Node::nodeToward(): handle is not a child of this node!"); |
493 | @@ -1402,7 +1550,7 @@ | |||
494 | 1402 | if (front() == h) { | 1550 | if (front() == h) { |
495 | 1403 | return _prev(); | 1551 | return _prev(); |
496 | 1404 | } | 1552 | } |
498 | 1405 | if (back() == h) { | 1553 | if ( (back() == h) || (arc_rx() == h) || (arc_ry() == h) ) { |
499 | 1406 | return _next(); | 1554 | return _next(); |
500 | 1407 | } | 1555 | } |
501 | 1408 | g_error("Node::nodeAwayFrom(): handle is not a child of this node!"); | 1556 | g_error("Node::nodeAwayFrom(): handle is not a child of this node!"); |
502 | @@ -1492,9 +1640,9 @@ | |||
503 | 1492 | { | 1640 | { |
504 | 1493 | if (!first || !second) return false; | 1641 | if (!first || !second) return false; |
505 | 1494 | if (first->_next() == second) | 1642 | if (first->_next() == second) |
507 | 1495 | return first->_front.isDegenerate() && second->_back.isDegenerate(); | 1643 | return first->_front.isDegenerate() && second->_back.isDegenerate() && first->_arc_rx.isDegenerate() && first->_arc_ry.isDegenerate(); |
508 | 1496 | if (second->_next() == first) | 1644 | if (second->_next() == first) |
510 | 1497 | return second->_front.isDegenerate() && first->_back.isDegenerate(); | 1645 | return second->_front.isDegenerate() && first->_back.isDegenerate() && second->_arc_rx.isDegenerate() && second->_arc_ry.isDegenerate(); |
511 | 1498 | return false; | 1646 | return false; |
512 | 1499 | } | 1647 | } |
513 | 1500 | 1648 | ||
514 | 1501 | 1649 | ||
515 | === modified file 'src/ui/tool/node.h' | |||
516 | --- src/ui/tool/node.h 2015-04-30 09:17:07 +0000 | |||
517 | +++ src/ui/tool/node.h 2016-07-09 15:52:46 +0000 | |||
518 | @@ -29,6 +29,7 @@ | |||
519 | 29 | 29 | ||
520 | 30 | #include <boost/enable_shared_from_this.hpp> | 30 | #include <boost/enable_shared_from_this.hpp> |
521 | 31 | #include <boost/shared_ptr.hpp> | 31 | #include <boost/shared_ptr.hpp> |
522 | 32 | #include <2geom/elliptical-arc.h> | ||
523 | 32 | #include "ui/tool/selectable-control-point.h" | 33 | #include "ui/tool/selectable-control-point.h" |
524 | 33 | #include "snapped-point.h" | 34 | #include "snapped-point.h" |
525 | 34 | #include "ui/tool/node-types.h" | 35 | #include "ui/tool/node-types.h" |
526 | @@ -100,6 +101,7 @@ | |||
527 | 100 | virtual ~Handle(); | 101 | virtual ~Handle(); |
528 | 101 | inline Geom::Point relativePos() const; | 102 | inline Geom::Point relativePos() const; |
529 | 102 | inline double length() const; | 103 | inline double length() const; |
530 | 104 | inline Geom::Angle angle() const; | ||
531 | 103 | bool isDegenerate() const { return _degenerate; } // True if the handle is retracted, i.e. has zero length. | 105 | bool isDegenerate() const { return _degenerate; } // True if the handle is retracted, i.e. has zero length. |
532 | 104 | 106 | ||
533 | 105 | virtual void setVisible(bool); | 107 | virtual void setVisible(bool); |
534 | @@ -107,7 +109,10 @@ | |||
535 | 107 | 109 | ||
536 | 108 | virtual void setPosition(Geom::Point const &p); | 110 | virtual void setPosition(Geom::Point const &p); |
537 | 109 | inline void setRelativePos(Geom::Point const &p); | 111 | inline void setRelativePos(Geom::Point const &p); |
538 | 112 | virtual void setOffset(Geom::Point const &offset); | ||
539 | 110 | void setLength(double len); | 113 | void setLength(double len); |
540 | 114 | void setAngle(Geom::Angle a); | ||
541 | 115 | void setAngleAndLength(Geom::Angle a, double len); | ||
542 | 111 | void retract(); | 116 | void retract(); |
543 | 112 | void setDirection(Geom::Point const &from, Geom::Point const &to); | 117 | void setDirection(Geom::Point const &from, Geom::Point const &to); |
544 | 113 | void setDirection(Geom::Point const &dir); | 118 | void setDirection(Geom::Point const &dir); |
545 | @@ -140,6 +145,8 @@ | |||
546 | 140 | SPCtrlLine *_handle_line; | 145 | SPCtrlLine *_handle_line; |
547 | 141 | bool _degenerate; // True if the handle is retracted, i.e. has zero length. This is used often internally so it makes sense to cache this | 146 | bool _degenerate; // True if the handle is retracted, i.e. has zero length. This is used often internally so it makes sense to cache this |
548 | 142 | 147 | ||
549 | 148 | Geom::Point _offset; | ||
550 | 149 | |||
551 | 143 | /** | 150 | /** |
552 | 144 | * Control point of a cubic Bezier curve in a path. | 151 | * Control point of a cubic Bezier curve in a path. |
553 | 145 | * | 152 | * |
554 | @@ -183,6 +190,10 @@ | |||
555 | 183 | void showHandles(bool v); | 190 | void showHandles(bool v); |
556 | 184 | 191 | ||
557 | 185 | void updateHandles(); | 192 | void updateHandles(); |
558 | 193 | void updateArcHandleConstriants(Handle *constraint); | ||
559 | 194 | void moveArcHandles(Geom::Point lineBetweenNodes, double lenX, double lenY, Geom::Angle rotX, Geom::Angle rotY); | ||
560 | 195 | |||
561 | 196 | void retractArcHandles(); | ||
562 | 186 | 197 | ||
563 | 187 | 198 | ||
564 | 188 | /** | 199 | /** |
565 | @@ -195,6 +206,11 @@ | |||
566 | 195 | bool isEndNode() const; | 206 | bool isEndNode() const; |
567 | 196 | Handle *front() { return &_front; } | 207 | Handle *front() { return &_front; } |
568 | 197 | Handle *back() { return &_back; } | 208 | Handle *back() { return &_back; } |
569 | 209 | Handle *arc_rx() { return &_arc_rx; } | ||
570 | 210 | Handle *arc_ry() { return &_arc_ry; } | ||
571 | 211 | bool *arc_large() { return &_arc_large; } | ||
572 | 212 | bool *arc_sweep() { return &_arc_sweep; } | ||
573 | 213 | Geom::EllipticalArc getEllipticalArc(); | ||
574 | 198 | 214 | ||
575 | 199 | /** | 215 | /** |
576 | 200 | * Gets the handle that faces the given adjacent node. | 216 | * Gets the handle that faces the given adjacent node. |
577 | @@ -278,6 +294,11 @@ | |||
578 | 278 | // as a line segment | 294 | // as a line segment |
579 | 279 | Handle _front; ///< Node handle in the backward direction of the path | 295 | Handle _front; ///< Node handle in the backward direction of the path |
580 | 280 | Handle _back; ///< Node handle in the forward direction of the path | 296 | Handle _back; ///< Node handle in the forward direction of the path |
581 | 297 | // The arc control points and flags relate to an arc starting at this node and ending at the next node | ||
582 | 298 | Handle _arc_rx; ///< Handle for controling the x radius and rotation of the elipse (in eliptical arc mode) | ||
583 | 299 | Handle _arc_ry; ///< Handle for controling the y radius and rotation of the elipse (in eliptical arc mode) | ||
584 | 300 | bool _arc_large; ///< Flag that determines if arc mode is shallow or bulge | ||
585 | 301 | bool _arc_sweep; ///< Flag that determines the direction of an arc's sweep | ||
586 | 281 | NodeType _type; ///< Type of node - cusp, smooth... | 302 | NodeType _type; ///< Type of node - cusp, smooth... |
587 | 282 | bool _handles_shown; | 303 | bool _handles_shown; |
588 | 283 | static ColorSet node_colors; | 304 | static ColorSet node_colors; |
589 | @@ -489,14 +510,17 @@ | |||
590 | 489 | 510 | ||
591 | 490 | // define inline Handle funcs after definition of Node | 511 | // define inline Handle funcs after definition of Node |
592 | 491 | inline Geom::Point Handle::relativePos() const { | 512 | inline Geom::Point Handle::relativePos() const { |
594 | 492 | return position() - _parent->position(); | 513 | return position() - (_parent->position() + _offset); |
595 | 493 | } | 514 | } |
596 | 494 | inline void Handle::setRelativePos(Geom::Point const &p) { | 515 | inline void Handle::setRelativePos(Geom::Point const &p) { |
598 | 495 | setPosition(_parent->position() + p); | 516 | setPosition(_parent->position() + _offset + p); |
599 | 496 | } | 517 | } |
600 | 497 | inline double Handle::length() const { | 518 | inline double Handle::length() const { |
601 | 498 | return relativePos().length(); | 519 | return relativePos().length(); |
602 | 499 | } | 520 | } |
603 | 521 | inline Geom::Angle Handle::angle() const { | ||
604 | 522 | return Geom::Angle(position() - _parent->position() - _offset); | ||
605 | 523 | } | ||
606 | 500 | inline PathManipulator &Handle::_pm() { | 524 | inline PathManipulator &Handle::_pm() { |
607 | 501 | return _parent->_pm(); | 525 | return _parent->_pm(); |
608 | 502 | } | 526 | } |
609 | 503 | 527 | ||
610 | === modified file 'src/ui/tool/path-manipulator.cpp' | |||
611 | --- src/ui/tool/path-manipulator.cpp 2016-05-19 22:11:42 +0000 | |||
612 | +++ src/ui/tool/path-manipulator.cpp 2016-07-09 15:52:46 +0000 | |||
613 | @@ -21,6 +21,7 @@ | |||
614 | 21 | #include <2geom/bezier-curve.h> | 21 | #include <2geom/bezier-curve.h> |
615 | 22 | #include <2geom/bezier-utils.h> | 22 | #include <2geom/bezier-utils.h> |
616 | 23 | #include <2geom/path-sink.h> | 23 | #include <2geom/path-sink.h> |
617 | 24 | #include <2geom/pathvector.h> | ||
618 | 24 | #include <glibmm/i18n.h> | 25 | #include <glibmm/i18n.h> |
619 | 25 | #include "ui/tool/path-manipulator.h" | 26 | #include "ui/tool/path-manipulator.h" |
620 | 26 | #include "desktop.h" | 27 | #include "desktop.h" |
621 | @@ -801,7 +802,10 @@ | |||
622 | 801 | } | 802 | } |
623 | 802 | } | 803 | } |
624 | 803 | 804 | ||
626 | 804 | /** Make selected segments curves / lines. */ | 805 | // \todo The three functions setSegmentType, setArcSegmentLarge, setArcSegmentSweep could be |
627 | 806 | // refactored to share the code that iterates over all selected segments. | ||
628 | 807 | |||
629 | 808 | /** Make selected segments curves / lines / arcs. */ | ||
630 | 805 | void PathManipulator::setSegmentType(SegmentType type) | 809 | void PathManipulator::setSegmentType(SegmentType type) |
631 | 806 | { | 810 | { |
632 | 807 | if (_num_selected == 0) return; | 811 | if (_num_selected == 0) return; |
633 | @@ -811,18 +815,81 @@ | |||
634 | 811 | if (!(k && j->selected() && k->selected())) continue; | 815 | if (!(k && j->selected() && k->selected())) continue; |
635 | 812 | switch (type) { | 816 | switch (type) { |
636 | 813 | case SEGMENT_STRAIGHT: | 817 | case SEGMENT_STRAIGHT: |
638 | 814 | if (j->front()->isDegenerate() && k->back()->isDegenerate()) | 818 | if ((j->front()->isDegenerate() && k->back()->isDegenerate()) |
639 | 819 | && (j->arc_rx()->isDegenerate() && j->arc_ry()->isDegenerate())) | ||
640 | 815 | break; | 820 | break; |
641 | 816 | j->front()->move(*j); | 821 | j->front()->move(*j); |
642 | 817 | k->back()->move(*k); | 822 | k->back()->move(*k); |
643 | 823 | j->retractArcHandles(); | ||
644 | 818 | break; | 824 | break; |
645 | 819 | case SEGMENT_CUBIC_BEZIER: | 825 | case SEGMENT_CUBIC_BEZIER: |
648 | 820 | if (!j->front()->isDegenerate() || !k->back()->isDegenerate()) | 826 | if (!j->front()->isDegenerate() || !k->back()->isDegenerate()){ |
649 | 821 | break; | 827 | // Already a cubic bezier |
650 | 828 | break; | ||
651 | 829 | } | ||
652 | 830 | if (!j->arc_rx()->isDegenerate() || !j->arc_ry()->isDegenerate()){ | ||
653 | 831 | // This is an elliptical arc that is being converted to a cubic bezier | ||
654 | 832 | // Generate the bezier path and use it to replace the current segment | ||
655 | 833 | Geom::Path cubicbezier_path = Geom::cubicbezierpath_from_sbasis(j->getEllipticalArc().toSBasis(), 0.1); | ||
656 | 834 | replaceSegmentWithPath(j, cubicbezier_path); | ||
657 | 835 | break; | ||
658 | 836 | } | ||
659 | 837 | |||
660 | 822 | // move both handles to 1/3 of the line | 838 | // move both handles to 1/3 of the line |
661 | 823 | j->front()->move(j->position() + (k->position() - j->position()) / 3); | 839 | j->front()->move(j->position() + (k->position() - j->position()) / 3); |
662 | 824 | k->back()->move(k->position() + (j->position() - k->position()) / 3); | 840 | k->back()->move(k->position() + (j->position() - k->position()) / 3); |
664 | 825 | break; | 841 | j->arc_rx()->move(*j); |
665 | 842 | j->arc_ry()->move(*j); | ||
666 | 843 | j->retractArcHandles(); | ||
667 | 844 | break; | ||
668 | 845 | case SEGMENT_ELIPTICAL_ARC: | ||
669 | 846 | if (!(j->front()->isDegenerate() && k->back()->isDegenerate())){ | ||
670 | 847 | j->front()->move(*j); | ||
671 | 848 | k->back()->move(*k); | ||
672 | 849 | } | ||
673 | 850 | |||
674 | 851 | if (j->arc_rx()->isDegenerate() && j->arc_ry()->isDegenerate()){ | ||
675 | 852 | Geom::Point midPointOffset = ((k->position() - j->position()) / 2); | ||
676 | 853 | Geom::Point handleOrigin = j->position()+midPointOffset; | ||
677 | 854 | |||
678 | 855 | j->arc_rx()->setOffset(midPointOffset); | ||
679 | 856 | j->arc_ry()->setOffset(midPointOffset); | ||
680 | 857 | j->arc_rx()->move(j->position() + ((k->position() - handleOrigin) / 2)); | ||
681 | 858 | j->updateArcHandleConstriants(j->arc_rx()); | ||
682 | 859 | } | ||
683 | 860 | break; | ||
684 | 861 | } | ||
685 | 862 | } | ||
686 | 863 | } | ||
687 | 864 | } | ||
688 | 865 | |||
689 | 866 | /** Set the large flag on selected arcs */ | ||
690 | 867 | void PathManipulator::setArcSegmentLarge(bool large){ | ||
691 | 868 | if (_num_selected == 0) return; | ||
692 | 869 | for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { | ||
693 | 870 | for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { | ||
694 | 871 | NodeList::iterator k = j.next(); | ||
695 | 872 | if (!(k && j->selected() && k->selected())) continue; | ||
696 | 873 | // This code executes for every selected segment | ||
697 | 874 | if (!j->arc_rx()->isDegenerate() || !j->arc_ry()->isDegenerate()){ | ||
698 | 875 | // This code executes for every selected ARC segment | ||
699 | 876 | *(j->arc_large()) = large; | ||
700 | 877 | } | ||
701 | 878 | } | ||
702 | 879 | } | ||
703 | 880 | } | ||
704 | 881 | |||
705 | 882 | /** Set the sweep flag on selected arcs */ | ||
706 | 883 | void PathManipulator::toggleArcSegmentSweep(){ | ||
707 | 884 | if (_num_selected == 0) return; | ||
708 | 885 | for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { | ||
709 | 886 | for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { | ||
710 | 887 | NodeList::iterator k = j.next(); | ||
711 | 888 | if (!(k && j->selected() && k->selected())) continue; | ||
712 | 889 | // This code executes for every selected segment | ||
713 | 890 | if (!j->arc_rx()->isDegenerate() || !j->arc_ry()->isDegenerate()){ | ||
714 | 891 | // This code executes for every selected ARC segment | ||
715 | 892 | *(j->arc_sweep()) = !*j->arc_sweep(); | ||
716 | 826 | } | 893 | } |
717 | 827 | } | 894 | } |
718 | 828 | } | 895 | } |
719 | @@ -1094,6 +1161,75 @@ | |||
720 | 1094 | return match; | 1161 | return match; |
721 | 1095 | } | 1162 | } |
722 | 1096 | 1163 | ||
723 | 1164 | /** Replace a segment with a path. | ||
724 | 1165 | * @param segment The segment to replace | ||
725 | 1166 | * @param newPath The path to insert in place of 'segment' | ||
726 | 1167 | * | ||
727 | 1168 | * Currently, newPath must be composed only of cubic beziers, the caller must | ||
728 | 1169 | * ensure that the path only contains cubic beziers (until other segments are | ||
729 | 1170 | * implemented in this function) */ | ||
730 | 1171 | void PathManipulator::replaceSegmentWithPath(NodeList::iterator segment, Geom::Path newPath) | ||
731 | 1172 | { | ||
732 | 1173 | if (!segment) throw std::invalid_argument("Invalid iterator for replacement"); | ||
733 | 1174 | NodeList &list = NodeList::get(segment); | ||
734 | 1175 | NodeList::iterator second = segment.next(); | ||
735 | 1176 | if (!second) throw std::invalid_argument("Replace after last node in open path"); | ||
736 | 1177 | |||
737 | 1178 | // Retract all handles relating to this segment | ||
738 | 1179 | segment->retractArcHandles(); | ||
739 | 1180 | segment->front()->retract(); | ||
740 | 1181 | |||
741 | 1182 | // get the insertion point | ||
742 | 1183 | NodeList::iterator insert_at = segment; | ||
743 | 1184 | ++insert_at; | ||
744 | 1185 | |||
745 | 1186 | // Keep the previous node handy to update its handles when needed | ||
746 | 1187 | Node *prevNode = &(*insert_at); | ||
747 | 1188 | |||
748 | 1189 | // Path is to be inserted in reverse order | ||
749 | 1190 | Geom::Path reversedPath = newPath.reversed(); | ||
750 | 1191 | |||
751 | 1192 | // Iterate over the path | ||
752 | 1193 | for (Geom::Path::iterator i = reversedPath.begin(); i != reversedPath.end(); ++i){ | ||
753 | 1194 | const Geom::Curve & thisCurve = *i; | ||
754 | 1195 | |||
755 | 1196 | // Try converting to a bezier | ||
756 | 1197 | const Geom::BezierCurve * bezier = dynamic_cast<const Geom::BezierCurve*>(&thisCurve); | ||
757 | 1198 | if (bezier) { | ||
758 | 1199 | // Check order of bezier (currently only cubic beziers are supported) | ||
759 | 1200 | if (bezier->order() == 3) | ||
760 | 1201 | { | ||
761 | 1202 | // Create one new node | ||
762 | 1203 | Node *newNode = new Node(_multi_path_manipulator._path_data.node_data, bezier->finalPoint()); | ||
763 | 1204 | // Set the control points for this node and the previous node | ||
764 | 1205 | newNode->front() ->setPosition((*bezier)[2]); | ||
765 | 1206 | prevNode->back()->setPosition((*bezier)[1]); | ||
766 | 1207 | // All new nodes are smooth | ||
767 | 1208 | newNode->setType(NODE_SMOOTH, false); | ||
768 | 1209 | |||
769 | 1210 | // Insert new node | ||
770 | 1211 | list.insert(insert_at, newNode); | ||
771 | 1212 | // Move along to next node | ||
772 | 1213 | prevNode = newNode; | ||
773 | 1214 | insert_at--; | ||
774 | 1215 | } | ||
775 | 1216 | else{ | ||
776 | 1217 | // TODO, Is there a better exception to raise here? | ||
777 | 1218 | // TODO, implement this if needed in future | ||
778 | 1219 | throw std::invalid_argument("Only cubic bezier curves are implemented in PathManipulator::replaceSegment." | ||
779 | 1220 | " newPath contains beziers with order!=3."); | ||
780 | 1221 | } | ||
781 | 1222 | } | ||
782 | 1223 | else{ | ||
783 | 1224 | // Not a bezier | ||
784 | 1225 | // TODO, Is there a better exception to raise here? | ||
785 | 1226 | // TODO, implement this if needed in future | ||
786 | 1227 | throw std::invalid_argument("Only cubic bezier curves are implemented in PathManipulator::replaceSegment." | ||
787 | 1228 | " newPath contains non-bezier segments."); | ||
788 | 1229 | } | ||
789 | 1230 | } | ||
790 | 1231 | } | ||
791 | 1232 | |||
792 | 1097 | /** Called by the XML observer when something else than us modifies the path. */ | 1233 | /** Called by the XML observer when something else than us modifies the path. */ |
793 | 1098 | void PathManipulator::_externalChange(unsigned type) | 1234 | void PathManipulator::_externalChange(unsigned type) |
794 | 1099 | { | 1235 | { |
795 | @@ -1149,7 +1285,8 @@ | |||
796 | 1149 | 1285 | ||
797 | 1150 | // sanitize pathvector and store it in SPCurve, | 1286 | // sanitize pathvector and store it in SPCurve, |
798 | 1151 | // so that _updateDragPoint doesn't crash on paths with naked movetos | 1287 | // so that _updateDragPoint doesn't crash on paths with naked movetos |
800 | 1152 | Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers(_spcurve->get_pathvector()); | 1288 | Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers_and_arcs(_spcurve->get_pathvector()); |
801 | 1289 | |||
802 | 1153 | for (Geom::PathVector::iterator i = pathv.begin(); i != pathv.end(); ) { | 1290 | for (Geom::PathVector::iterator i = pathv.begin(); i != pathv.end(); ) { |
803 | 1154 | // NOTE: this utilizes the fact that Geom::PathVector is an std::vector. | 1291 | // NOTE: this utilizes the fact that Geom::PathVector is an std::vector. |
804 | 1155 | // When we erase an element, the next one slides into position, | 1292 | // When we erase an element, the next one slides into position, |
805 | @@ -1199,6 +1336,15 @@ | |||
806 | 1199 | previous_node->front()->setPosition((*bezier)[1]); | 1336 | previous_node->front()->setPosition((*bezier)[1]); |
807 | 1200 | current_node ->back() ->setPosition((*bezier)[2]); | 1337 | current_node ->back() ->setPosition((*bezier)[2]); |
808 | 1201 | } | 1338 | } |
809 | 1339 | Geom::EllipticalArc const *arc = dynamic_cast<Geom::EllipticalArc const*>(&*cit); | ||
810 | 1340 | if (arc) | ||
811 | 1341 | { | ||
812 | 1342 | Geom::Coord angleX = arc->rotationAngle(); | ||
813 | 1343 | Geom::Coord angleY = angleX + Geom::rad_from_deg(90); | ||
814 | 1344 | previous_node->moveArcHandles(current_node->position() - previous_node->position(), arc->ray(Geom::X), arc->ray(Geom::Y), angleX, angleY); | ||
815 | 1345 | *(previous_node->arc_large()) = arc->largeArc(); | ||
816 | 1346 | *(previous_node->arc_sweep()) = arc->sweep(); | ||
817 | 1347 | } | ||
818 | 1202 | previous_node = current_node; | 1348 | previous_node = current_node; |
819 | 1203 | } | 1349 | } |
820 | 1204 | // If the path is closed, make the list cyclic | 1350 | // If the path is closed, make the list cyclic |
821 | @@ -1355,8 +1501,9 @@ | |||
822 | 1355 | } | 1501 | } |
823 | 1356 | if (subpath->closed()) { | 1502 | if (subpath->closed()) { |
824 | 1357 | // Here we link the last and first node if the path is closed. | 1503 | // Here we link the last and first node if the path is closed. |
827 | 1358 | // If the last segment is Bezier, we add it. | 1504 | // If the last segment is Bezier or arc, we add it. |
828 | 1359 | if (!prev->front()->isDegenerate() || !subpath->begin()->back()->isDegenerate()) { | 1505 | if (!prev->front()->isDegenerate() || !subpath->begin()->back()->isDegenerate() |
829 | 1506 | || !prev->arc_rx()->isDegenerate() || !prev->arc_ry()->isDegenerate()) { | ||
830 | 1360 | build_segment(builder, prev.ptr(), subpath->begin().ptr()); | 1507 | build_segment(builder, prev.ptr(), subpath->begin().ptr()); |
831 | 1361 | } | 1508 | } |
832 | 1362 | // if that segment is linear, we just call closePath(). | 1509 | // if that segment is linear, we just call closePath(). |
833 | @@ -1397,16 +1544,33 @@ | |||
834 | 1397 | * @relates PathManipulator */ | 1544 | * @relates PathManipulator */ |
835 | 1398 | void build_segment(Geom::PathBuilder &builder, Node *prev_node, Node *cur_node) | 1545 | void build_segment(Geom::PathBuilder &builder, Node *prev_node, Node *cur_node) |
836 | 1399 | { | 1546 | { |
847 | 1400 | if (cur_node->back()->isDegenerate() && prev_node->front()->isDegenerate()) | 1547 | if (prev_node->arc_rx()->isDegenerate() || prev_node->arc_ry()->isDegenerate() ){ |
848 | 1401 | { | 1548 | // This is not an eliptical arc, check if it is a straight line or bezier |
849 | 1402 | // NOTE: It seems like the renderer cannot correctly handle vline / hline segments, | 1549 | if (cur_node->back()->isDegenerate() && prev_node->front()->isDegenerate()) |
850 | 1403 | // and trying to display a path using them results in funny artifacts. | 1550 | { |
851 | 1404 | builder.lineTo(cur_node->position()); | 1551 | // NOTE: It seems like the renderer cannot correctly handle vline / hline segments, |
852 | 1405 | } else { | 1552 | // and trying to display a path using them results in funny artifacts. |
853 | 1406 | // this is a bezier segment | 1553 | builder.lineTo(cur_node->position()); |
854 | 1407 | builder.curveTo( | 1554 | } else { |
855 | 1408 | prev_node->front()->position(), | 1555 | // this is a bezier segment |
856 | 1409 | cur_node->back()->position(), | 1556 | builder.curveTo( |
857 | 1557 | prev_node->front()->position(), | ||
858 | 1558 | cur_node->back()->position(), | ||
859 | 1559 | cur_node->position()); | ||
860 | 1560 | } | ||
861 | 1561 | } | ||
862 | 1562 | else{ | ||
863 | 1563 | // This is an eliptical arc, get the x and y set by the xy handle | ||
864 | 1564 | Geom::Coord rx = prev_node->arc_rx()->length(); | ||
865 | 1565 | Geom::Coord ry = prev_node->arc_ry()->length(); | ||
866 | 1566 | Geom::Angle rot = prev_node->arc_rx()->angle(); | ||
867 | 1567 | |||
868 | 1568 | builder.arcTo( | ||
869 | 1569 | rx, | ||
870 | 1570 | ry, | ||
871 | 1571 | rot, | ||
872 | 1572 | *prev_node->arc_large(), | ||
873 | 1573 | *prev_node->arc_sweep(), | ||
874 | 1410 | cur_node->position()); | 1574 | cur_node->position()); |
875 | 1411 | } | 1575 | } |
876 | 1412 | } | 1576 | } |
877 | 1413 | 1577 | ||
878 | === modified file 'src/ui/tool/path-manipulator.h' | |||
879 | --- src/ui/tool/path-manipulator.h 2015-08-13 23:23:05 +0000 | |||
880 | +++ src/ui/tool/path-manipulator.h 2016-07-09 15:52:46 +0000 | |||
881 | @@ -15,6 +15,7 @@ | |||
882 | 15 | #include <memory> | 15 | #include <memory> |
883 | 16 | #include <2geom/pathvector.h> | 16 | #include <2geom/pathvector.h> |
884 | 17 | #include <2geom/affine.h> | 17 | #include <2geom/affine.h> |
885 | 18 | #include <2geom/sbasis-to-bezier.h> | ||
886 | 18 | #include <boost/shared_ptr.hpp> | 19 | #include <boost/shared_ptr.hpp> |
887 | 19 | #include <boost/weak_ptr.hpp> | 20 | #include <boost/weak_ptr.hpp> |
888 | 20 | #include "ui/tool/node.h" | 21 | #include "ui/tool/node.h" |
889 | @@ -81,6 +82,9 @@ | |||
890 | 81 | void reverseSubpaths(bool selected_only); | 82 | void reverseSubpaths(bool selected_only); |
891 | 82 | void setSegmentType(SegmentType); | 83 | void setSegmentType(SegmentType); |
892 | 83 | 84 | ||
893 | 85 | void setArcSegmentLarge(bool large); | ||
894 | 86 | void toggleArcSegmentSweep(); | ||
895 | 87 | |||
896 | 84 | void scaleHandle(Node *n, int which, int dir, bool pixel); | 88 | void scaleHandle(Node *n, int which, int dir, bool pixel); |
897 | 85 | void rotateHandle(Node *n, int which, int dir, bool pixel); | 89 | void rotateHandle(Node *n, int which, int dir, bool pixel); |
898 | 86 | 90 | ||
899 | @@ -97,6 +101,7 @@ | |||
900 | 97 | NodeList::iterator subdivideSegment(NodeList::iterator after, double t); | 101 | NodeList::iterator subdivideSegment(NodeList::iterator after, double t); |
901 | 98 | NodeList::iterator extremeNode(NodeList::iterator origin, bool search_selected, | 102 | NodeList::iterator extremeNode(NodeList::iterator origin, bool search_selected, |
902 | 99 | bool search_unselected, bool closest); | 103 | bool search_unselected, bool closest); |
903 | 104 | void replaceSegmentWithPath(NodeList::iterator segment, Geom::Path newPath); | ||
904 | 100 | 105 | ||
905 | 101 | int _bsplineGetSteps() const; | 106 | int _bsplineGetSteps() const; |
906 | 102 | // this is necessary for Tab-selection in MultiPathManipulator | 107 | // this is necessary for Tab-selection in MultiPathManipulator |
907 | 103 | 108 | ||
908 | === modified file 'src/widgets/node-toolbar.cpp' | |||
909 | --- src/widgets/node-toolbar.cpp 2014-12-21 21:58:32 +0000 | |||
910 | +++ src/widgets/node-toolbar.cpp 2016-07-09 15:52:46 +0000 | |||
911 | @@ -171,6 +171,38 @@ | |||
912 | 171 | } | 171 | } |
913 | 172 | } | 172 | } |
914 | 173 | 173 | ||
915 | 174 | static void sp_node_path_edit_toarc(void) | ||
916 | 175 | { | ||
917 | 176 | NodeTool *nt = get_node_tool(); | ||
918 | 177 | if (nt) { | ||
919 | 178 | nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_ELIPTICAL_ARC); | ||
920 | 179 | } | ||
921 | 180 | } | ||
922 | 181 | |||
923 | 182 | static void sp_node_path_edit_arc_shallow(void) | ||
924 | 183 | { | ||
925 | 184 | NodeTool *nt = get_node_tool(); | ||
926 | 185 | if (nt) { | ||
927 | 186 | nt->_multipath->setArcSegmentLarge(false); | ||
928 | 187 | } | ||
929 | 188 | } | ||
930 | 189 | |||
931 | 190 | static void sp_node_path_edit_arc_bulge(void) | ||
932 | 191 | { | ||
933 | 192 | NodeTool *nt = get_node_tool(); | ||
934 | 193 | if (nt) { | ||
935 | 194 | nt->_multipath->setArcSegmentLarge(true); | ||
936 | 195 | } | ||
937 | 196 | } | ||
938 | 197 | |||
939 | 198 | static void sp_node_path_edit_arc_flip(void) | ||
940 | 199 | { | ||
941 | 200 | NodeTool *nt = get_node_tool(); | ||
942 | 201 | if (nt) { | ||
943 | 202 | nt->_multipath->toggleArcSegmentSweep(); | ||
944 | 203 | } | ||
945 | 204 | } | ||
946 | 205 | |||
947 | 174 | static void sp_node_path_edit_cusp(void) | 206 | static void sp_node_path_edit_cusp(void) |
948 | 175 | { | 207 | { |
949 | 176 | NodeTool *nt = get_node_tool(); | 208 | NodeTool *nt = get_node_tool(); |
950 | @@ -486,6 +518,36 @@ | |||
951 | 486 | } | 518 | } |
952 | 487 | 519 | ||
953 | 488 | { | 520 | { |
954 | 521 | InkAction* inky = ink_action_new( "NodeArcShallowAction", | ||
955 | 522 | _("Node Arc Shallow"), | ||
956 | 523 | _("Make selected arc segments shallow"), | ||
957 | 524 | INKSCAPE_ICON("node-segment-elliptical-arc-shallow"), | ||
958 | 525 | secondarySize ); | ||
959 | 526 | g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_arc_shallow), 0 ); | ||
960 | 527 | gtk_action_group_add_action( mainActions, GTK_ACTION(inky) ); | ||
961 | 528 | } | ||
962 | 529 | |||
963 | 530 | { | ||
964 | 531 | InkAction* inky = ink_action_new( "NodeArcBulgeAction", | ||
965 | 532 | _("Node Arc Bulge"), | ||
966 | 533 | _("Make selected arc segments bulge"), | ||
967 | 534 | INKSCAPE_ICON("node-segment-elliptical-arc-bulge"), | ||
968 | 535 | secondarySize ); | ||
969 | 536 | g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_arc_bulge), 0 ); | ||
970 | 537 | gtk_action_group_add_action( mainActions, GTK_ACTION(inky) ); | ||
971 | 538 | } | ||
972 | 539 | |||
973 | 540 | { | ||
974 | 541 | InkAction* inky = ink_action_new( "NodeArcFlipAction", | ||
975 | 542 | _("Node Arc Flip"), | ||
976 | 543 | _("Flip selected arc segments"), | ||
977 | 544 | INKSCAPE_ICON("node-segment-elliptical-arc-flip"), | ||
978 | 545 | secondarySize ); | ||
979 | 546 | g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_arc_flip), 0 ); | ||
980 | 547 | gtk_action_group_add_action( mainActions, GTK_ACTION(inky) ); | ||
981 | 548 | } | ||
982 | 549 | |||
983 | 550 | { | ||
984 | 489 | InkAction* inky = ink_action_new( "NodeLineAction", | 551 | InkAction* inky = ink_action_new( "NodeLineAction", |
985 | 490 | _("Node Line"), | 552 | _("Node Line"), |
986 | 491 | _("Make selected segments lines"), | 553 | _("Make selected segments lines"), |
987 | @@ -506,6 +568,16 @@ | |||
988 | 506 | } | 568 | } |
989 | 507 | 569 | ||
990 | 508 | { | 570 | { |
991 | 571 | InkAction* inky = ink_action_new( "NodeArcAction", | ||
992 | 572 | _("Node Arc"), | ||
993 | 573 | _("Make selected segments arcs"), | ||
994 | 574 | INKSCAPE_ICON("node-segment-elliptical-arc"), | ||
995 | 575 | secondarySize ); | ||
996 | 576 | g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toarc), 0 ); | ||
997 | 577 | gtk_action_group_add_action( mainActions, GTK_ACTION(inky) ); | ||
998 | 578 | } | ||
999 | 579 | |||
1000 | 580 | { | ||
1001 | 509 | InkToggleAction* act = ink_toggle_action_new( "NodesShowTransformHandlesAction", | 581 | InkToggleAction* act = ink_toggle_action_new( "NodesShowTransformHandlesAction", |
1002 | 510 | _("Show Transform Handles"), | 582 | _("Show Transform Handles"), |
1003 | 511 | _("Show transformation handles for selected nodes"), | 583 | _("Show transformation handles for selected nodes"), |
1004 | 512 | 584 | ||
1005 | === modified file 'src/widgets/toolbox.cpp' | |||
1006 | --- src/widgets/toolbox.cpp 2016-05-22 00:49:33 +0000 | |||
1007 | +++ src/widgets/toolbox.cpp 2016-07-09 15:52:46 +0000 | |||
1008 | @@ -282,6 +282,11 @@ | |||
1009 | 282 | " <separator />" | 282 | " <separator />" |
1010 | 283 | " <toolitem action='NodeLineAction' />" | 283 | " <toolitem action='NodeLineAction' />" |
1011 | 284 | " <toolitem action='NodeCurveAction' />" | 284 | " <toolitem action='NodeCurveAction' />" |
1012 | 285 | " <toolitem action='NodeArcAction' />" | ||
1013 | 286 | " <separator />" | ||
1014 | 287 | " <toolitem action='NodeArcShallowAction' />" | ||
1015 | 288 | " <toolitem action='NodeArcBulgeAction' />" | ||
1016 | 289 | " <toolitem action='NodeArcFlipAction' />" | ||
1017 | 285 | " <separator />" | 290 | " <separator />" |
1018 | 286 | " <toolitem action='ObjectToPath' />" | 291 | " <toolitem action='ObjectToPath' />" |
1019 | 287 | " <toolitem action='StrokeToPath' />" | 292 | " <toolitem action='StrokeToPath' />" |
Added some comments about coding style, also start compiling to see the UX and give a better review.
Great work!