Merge lp:~liampwhite/inkscape/inkscape into lp:inkscape/experimental
- inkscape
- Merge into experimental
Proposed by
Liam P. White
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 13545 | ||||||||
Proposed branch: | lp:~liampwhite/inkscape/inkscape | ||||||||
Merge into: | lp:inkscape/experimental | ||||||||
Diff against target: |
12144 lines (+10546/-196) 97 files modified
po/POTFILES.in (+15/-0) src/Makefile_insert (+3/-0) src/attributes.cpp (+2/-0) src/attributes.h (+2/-0) src/display/cairo-utils.cpp (+1/-3) src/display/cairo-utils.h (+3/-0) src/display/drawing-item.cpp (+3/-2) src/display/sp-canvas.cpp (+1/-1) src/interface.cpp (+11/-0) src/interface.h (+1/-0) src/knotholder.cpp (+25/-11) src/live_effects/CMakeLists.txt (+13/-0) src/live_effects/Makefile_insert (+16/-0) src/live_effects/effect-enum.h (+7/-0) src/live_effects/effect.cpp (+71/-6) src/live_effects/effect.h (+11/-0) src/live_effects/lpe-attach-path.cpp (+198/-0) src/live_effects/lpe-attach-path.h (+52/-0) src/live_effects/lpe-bounding-box.cpp (+67/-0) src/live_effects/lpe-bounding-box.h (+37/-0) src/live_effects/lpe-ellipse_5pts.cpp (+214/-0) src/live_effects/lpe-ellipse_5pts.h (+50/-0) src/live_effects/lpe-fill-between-many.cpp (+78/-0) src/live_effects/lpe-fill-between-many.h (+36/-0) src/live_effects/lpe-fill-between-strokes.cpp (+116/-0) src/live_effects/lpe-fill-between-strokes.h (+38/-0) src/live_effects/lpe-jointype.cpp (+184/-0) src/live_effects/lpe-jointype.h (+45/-0) src/live_effects/lpe-knot.cpp (+4/-0) src/live_effects/lpe-knot.h (+2/-1) src/live_effects/lpe-powerstroke-interpolators.h (+40/-0) src/live_effects/lpe-powerstroke.cpp (+89/-11) src/live_effects/lpe-powerstroke.h (+2/-0) src/live_effects/lpe-tangent_to_curve.cpp (+8/-11) src/live_effects/lpe-tangent_to_curve.h (+0/-1) src/live_effects/lpe-taperstroke.cpp (+630/-0) src/live_effects/lpe-taperstroke.h (+72/-0) src/live_effects/parameter/Makefile_insert (+4/-0) src/live_effects/parameter/filletchamferpointarray.cpp (+3/-0) src/live_effects/parameter/originalpatharray.cpp (+497/-0) src/live_effects/parameter/originalpatharray.h (+123/-0) src/live_effects/parameter/powerstrokepointarray.cpp (+36/-7) src/live_effects/parameter/powerstrokepointarray.h (+21/-2) src/live_effects/parameter/transformedpoint.cpp (+182/-0) src/live_effects/parameter/transformedpoint.h (+87/-0) src/live_effects/pathoutlineprovider.cpp (+795/-0) src/live_effects/pathoutlineprovider.h (+55/-0) src/menus-skeleton.h (+3/-0) src/path-chemistry.cpp (+8/-0) src/selection-chemistry.cpp (+159/-48) src/selection-chemistry.h (+1/-0) src/snap.cpp (+1/-1) src/sp-item-group.cpp (+12/-0) src/sp-item-group.h (+8/-0) src/sp-item.cpp (+40/-0) src/sp-item.h (+15/-0) src/sp-lpe-item.cpp (+8/-2) src/sp-object.h (+1/-0) src/sp-tag-use-reference.cpp (+156/-0) src/sp-tag-use-reference.h (+78/-0) src/sp-tag-use.cpp (+206/-0) src/sp-tag-use.h (+55/-0) src/sp-tag.cpp (+154/-0) src/sp-tag.h (+57/-0) src/ui/dialog/Makefile_insert (+6/-0) src/ui/dialog/calligraphic-profile-rename.h (+1/-1) src/ui/dialog/color-item.cpp (+2/-0) src/ui/dialog/dialog-manager.cpp (+6/-0) src/ui/dialog/filedialog.h (+1/-0) src/ui/dialog/lpe-powerstroke-properties.cpp (+211/-0) src/ui/dialog/lpe-powerstroke-properties.h (+99/-0) src/ui/dialog/objects.cpp (+2144/-0) src/ui/dialog/objects.h (+263/-0) src/ui/dialog/swatches.cpp (+32/-75) src/ui/dialog/swatches.h (+0/-2) src/ui/dialog/tags.cpp (+1165/-0) src/ui/dialog/tags.h (+181/-0) src/ui/tool/multi-path-manipulator.cpp (+3/-3) src/ui/tool/multi-path-manipulator.h (+1/-1) src/ui/tools/node-tool.cpp (+2/-1) src/ui/tools/pen-tool.cpp (+3/-1) src/ui/widget/Makefile_insert (+11/-2) src/ui/widget/addtoicon.cpp (+157/-0) src/ui/widget/addtoicon.h (+98/-0) src/ui/widget/clipmaskicon.cpp (+184/-0) src/ui/widget/clipmaskicon.h (+102/-0) src/ui/widget/filter-effect-chooser.cpp (+2/-0) src/ui/widget/filter-effect-chooser.h (+3/-1) src/ui/widget/highlight-picker.cpp (+214/-0) src/ui/widget/highlight-picker.h (+90/-0) src/ui/widget/insertordericon.cpp (+173/-0) src/ui/widget/insertordericon.h (+100/-0) src/ui/widget/layertypeicon.cpp (+174/-0) src/ui/widget/layertypeicon.h (+108/-0) src/verbs.cpp (+92/-1) src/verbs.h (+5/-0) src/widgets/desktop-widget.cpp (+1/-1) |
||||||||
To merge this branch: | bzr merge lp:~liampwhite/inkscape/inkscape | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Owens | Approve | ||
Johan Engelen | Pending | ||
Review via email: mp+225765@code.launchpad.net |
Commit message
Description of the change
Proposing to merge to watch progression of diff.
To post a comment you must log in.
lp:~liampwhite/inkscape/inkscape
updated
- 13179. By Liam P. White <inkscapebrony at-sign gmail dot com>
-
Update to experimental r13428
- 13180. By Liam P. White <inkscapebrony at-sign gmail dot com>
-
Update to experimental r13429
- 13181. By Liam P. White <inkscapebrony at-sign gmail dot com>
-
Duplicate LPE entries
- 13182. By Liam P. White <inkscapebrony at-sign gmail dot com>
-
Update to experimental r13436
- 13183. By Liam P. White <inkscapebrony at-sign gmail dot com>
-
This file does not need executable permissions
- 13184. By Liam P. White <inkscapebrony at-sign gmail dot com>
-
Refactoring of linejoin code
- 13185. By Liam P. White
-
Update to experimental r13452
- 13186. By Liam P. White
-
Fix make check
- 13187. By Liam P. White
-
I'm an idiot
- 13188. By Liam P. White
-
Update to experimental r13460
- 13189. By Liam P. White
-
Update to experimental r13464
- 13190. By Liam P. White
-
Messed up German translation (??)
- 13191. By Liam P. White
-
Ponyscape feature: finish pen drawing on context switch
- 13192. By Liam P. White
-
Update to experimental r13465
- 13193. By Liam P. White
-
Update to experimental r13479
- 13194. By Liam P. White
-
Clone Original -> Fill Between Many
- 13195. By Liam P. White
-
Update to experimental r13483
- 13196. By Liam P. White
-
Update to experimental r13531
- 13197. By Liam P. White
-
Fix gtk3 build
- 13198. By Liam P. White
-
Update to experimental r13543
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'po/POTFILES.in' |
2 | --- po/POTFILES.in 2014-08-30 15:50:55 +0000 |
3 | +++ po/POTFILES.in 2014-09-07 17:02:32 +0000 |
4 | @@ -153,7 +153,18 @@ |
5 | src/live_effects/lpe-skeleton.cpp |
6 | src/live_effects/lpe-sketch.cpp |
7 | src/live_effects/lpe-vonkoch.cpp |
8 | +src/live_effects/lpe-envelope-perspective.cpp |
9 | +src/live_effects/lpe-attach-path.cpp |
10 | +src/live_effects/lpe-bounding-box.cpp |
11 | +src/live_effects/lpe-ellipse_5pts.cpp |
12 | +src/live_effects/lpe-fill-between-many.cpp |
13 | +src/live_effects/lpe-fill-between-strokes.cpp |
14 | +src/live_effects/lpe-jointype.cpp |
15 | +src/live_effects/lpe-taperstroke.cpp |
16 | +src/live_effects/lpe-fillet-chamfer.cpp |
17 | src/live_effects/parameter/filletchamferpointarray.cpp |
18 | +src/live_effects/parameter/originalpatharray.cpp |
19 | +src/live_effects/parameter/transformedpoint.cpp |
20 | src/live_effects/parameter/pointreseteable.cpp |
21 | src/live_effects/parameter/togglebutton.cpp |
22 | src/live_effects/parameter/bool.cpp |
23 | @@ -260,6 +271,10 @@ |
24 | src/ui/dialog/tracedialog.cpp |
25 | src/ui/dialog/transformation.cpp |
26 | src/ui/dialog/xml-tree.cpp |
27 | +src/ui/dialog/lpe-fillet-chamfer-properties.cpp |
28 | +src/ui/dialog/lpe-powerstroke-properties.cpp |
29 | +src/ui/dialog/objects.cpp |
30 | +src/ui/dialog/tags.cpp |
31 | src/ui/tool/curve-drag-point.cpp |
32 | src/ui/tool/multi-path-manipulator.cpp |
33 | src/ui/tool/node.cpp |
34 | |
35 | === modified file 'src/Makefile_insert' |
36 | --- src/Makefile_insert 2014-08-31 18:17:26 +0000 |
37 | +++ src/Makefile_insert 2014-09-07 17:02:32 +0000 |
38 | @@ -199,6 +199,9 @@ |
39 | sp-style-elem.cpp sp-style-elem.h \ |
40 | sp-switch.cpp sp-switch.h \ |
41 | sp-symbol.cpp sp-symbol.h \ |
42 | + sp-tag.cpp sp-tag.h \ |
43 | + sp-tag-use.cpp sp-tag-use.h \ |
44 | + sp-tag-use-reference.cpp sp-tag-use-reference.h \ |
45 | sp-text.cpp sp-text.h \ |
46 | sp-textpath.h \ |
47 | sp-title.cpp sp-title.h \ |
48 | |
49 | === modified file 'src/attributes.cpp' |
50 | --- src/attributes.cpp 2014-08-18 20:19:55 +0000 |
51 | +++ src/attributes.cpp 2014-09-07 17:02:32 +0000 |
52 | @@ -40,6 +40,7 @@ |
53 | {SP_ATTR_TRANSFORM_CENTER_X, "inkscape:transform-center-x"}, |
54 | {SP_ATTR_TRANSFORM_CENTER_Y, "inkscape:transform-center-y"}, |
55 | {SP_ATTR_INKSCAPE_PATH_EFFECT, "inkscape:path-effect"}, |
56 | + {SP_ATTR_INKSCAPE_HIGHLIGHT_COLOR, "inkscape:highlight-color"}, |
57 | /* SPAnchor */ |
58 | {SP_ATTR_XLINK_HREF, "xlink:href"}, |
59 | {SP_ATTR_XLINK_TYPE, "xlink:type"}, |
60 | @@ -50,6 +51,7 @@ |
61 | {SP_ATTR_XLINK_ACTUATE, "xlink:actuate"}, |
62 | {SP_ATTR_TARGET, "target"}, |
63 | {SP_ATTR_INKSCAPE_GROUPMODE, "inkscape:groupmode"}, |
64 | + {SP_ATTR_INKSCAPE_EXPANDED, "inkscape:expanded"}, |
65 | /* SPRoot */ |
66 | {SP_ATTR_VERSION, "version"}, |
67 | {SP_ATTR_WIDTH, "width"}, |
68 | |
69 | === modified file 'src/attributes.h' |
70 | --- src/attributes.h 2014-08-04 16:19:41 +0000 |
71 | +++ src/attributes.h 2014-09-07 17:02:32 +0000 |
72 | @@ -40,6 +40,7 @@ |
73 | SP_ATTR_TRANSFORM_CENTER_X, |
74 | SP_ATTR_TRANSFORM_CENTER_Y, |
75 | SP_ATTR_INKSCAPE_PATH_EFFECT, |
76 | + SP_ATTR_INKSCAPE_HIGHLIGHT_COLOR, |
77 | /* SPAnchor */ |
78 | SP_ATTR_XLINK_HREF, |
79 | SP_ATTR_XLINK_TYPE, |
80 | @@ -51,6 +52,7 @@ |
81 | SP_ATTR_TARGET, |
82 | /* SPGroup */ |
83 | SP_ATTR_INKSCAPE_GROUPMODE, |
84 | + SP_ATTR_INKSCAPE_EXPANDED, |
85 | /* SPRoot */ |
86 | SP_ATTR_VERSION, |
87 | SP_ATTR_WIDTH, |
88 | |
89 | === modified file 'src/display/cairo-utils.cpp' |
90 | --- src/display/cairo-utils.cpp 2014-09-06 15:25:51 +0000 |
91 | +++ src/display/cairo-utils.cpp 2014-09-07 17:02:32 +0000 |
92 | @@ -33,8 +33,6 @@ |
93 | #include "helper/geom-curves.h" |
94 | #include "display/cairo-templates.h" |
95 | |
96 | -static void ink_cairo_pixbuf_cleanup(guchar *, void *); |
97 | - |
98 | /** |
99 | * Key for cairo_surface_t to keep track of current color interpolation value |
100 | * Only the address of the structure is used, it is never initialized. See: |
101 | @@ -1172,7 +1170,7 @@ |
102 | * to gdk_pixbuf_new_from_data when creating a GdkPixbuf backed by |
103 | * a Cairo surface. |
104 | */ |
105 | -static void ink_cairo_pixbuf_cleanup(guchar * /*pixels*/, void *data) |
106 | +void ink_cairo_pixbuf_cleanup(guchar * /*pixels*/, void *data) |
107 | { |
108 | cairo_surface_t *surface = static_cast<cairo_surface_t*>(data); |
109 | cairo_surface_destroy(surface); |
110 | |
111 | === modified file 'src/display/cairo-utils.h' |
112 | --- src/display/cairo-utils.h 2014-08-30 17:23:17 +0000 |
113 | +++ src/display/cairo-utils.h 2014-09-07 17:02:32 +0000 |
114 | @@ -20,6 +20,9 @@ |
115 | struct SPColor; |
116 | typedef struct _GdkPixbuf GdkPixbuf; |
117 | |
118 | +void ink_cairo_pixbuf_cleanup(unsigned char *, void *); |
119 | +void convert_pixbuf_argb32_to_normal(GdkPixbuf *pb); |
120 | + |
121 | namespace Inkscape { |
122 | |
123 | /** |
124 | |
125 | === modified file 'src/display/drawing-item.cpp' |
126 | --- src/display/drawing-item.cpp 2014-08-18 21:18:05 +0000 |
127 | +++ src/display/drawing-item.cpp 2014-09-07 17:02:32 +0000 |
128 | @@ -825,9 +825,10 @@ |
129 | { |
130 | // Sometimes there's no BBOX in state, reason unknown (bug 992817) |
131 | // I made this not an assert to remove the warning |
132 | + // This warning clutters the console output, so commented out |
133 | if (!(_state & STATE_BBOX) || !(_state & STATE_PICK)) { |
134 | - g_warning("Invalid state when picking: STATE_BBOX = %d, STATE_PICK = %d", |
135 | - _state & STATE_BBOX, _state & STATE_PICK); |
136 | + /*g_warning("Invalid state when picking: STATE_BBOX = %d, STATE_PICK = %d", |
137 | + _state & STATE_BBOX, _state & STATE_PICK);*/ |
138 | return NULL; |
139 | } |
140 | // ignore invisible and insensitive items unless sticky |
141 | |
142 | === modified file 'src/display/sp-canvas.cpp' |
143 | --- src/display/sp-canvas.cpp 2014-08-26 11:14:18 +0000 |
144 | +++ src/display/sp-canvas.cpp 2014-09-07 17:02:32 +0000 |
145 | @@ -1146,7 +1146,7 @@ |
146 | sp_canvas_init(SPCanvas *canvas) |
147 | { |
148 | gtk_widget_set_has_window (GTK_WIDGET (canvas), TRUE); |
149 | - //gtk_widget_set_double_buffered (GTK_WIDGET (canvas), TRUE); |
150 | + gtk_widget_set_double_buffered (GTK_WIDGET (canvas), FALSE); |
151 | gtk_widget_set_can_focus (GTK_WIDGET (canvas), TRUE); |
152 | |
153 | canvas->pick_event.type = GDK_LEAVE_NOTIFY; |
154 | |
155 | === modified file 'src/interface.cpp' |
156 | --- src/interface.cpp 2014-09-02 21:14:55 +0000 |
157 | +++ src/interface.cpp 2014-09-07 17:02:32 +0000 |
158 | @@ -1756,6 +1756,13 @@ |
159 | } |
160 | mi->show(); |
161 | append(*mi); |
162 | + |
163 | + /*SSet Clip Group */ |
164 | + mi = Gtk::manage(new Gtk::MenuItem(_("Create Clip G_roup"),1)); |
165 | + mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::CreateGroupClip)); |
166 | + mi->set_sensitive(TRUE); |
167 | + mi->show(); |
168 | + append(*mi); |
169 | |
170 | /* Set Clip */ |
171 | mi = Gtk::manage(new Gtk::MenuItem(_("Set Cl_ip"), 1)); |
172 | @@ -1867,6 +1874,10 @@ |
173 | sp_selection_unset_mask(_desktop, false); |
174 | } |
175 | |
176 | +void ContextMenu::CreateGroupClip(void) |
177 | +{ |
178 | + sp_selection_set_clipgroup(_desktop); |
179 | +} |
180 | |
181 | void ContextMenu::SetClip(void) |
182 | { |
183 | |
184 | === modified file 'src/interface.h' |
185 | --- src/interface.h 2014-08-31 18:17:26 +0000 |
186 | +++ src/interface.h 2014-09-07 17:02:32 +0000 |
187 | @@ -183,6 +183,7 @@ |
188 | void SelectSameStrokeStyle(void); |
189 | void SelectSameObjectType(void); |
190 | void ItemCreateLink(void); |
191 | + void CreateGroupClip(void); |
192 | void SetMask(void); |
193 | void ReleaseMask(void); |
194 | void SetClip(void); |
195 | |
196 | === modified file 'src/knotholder.cpp' |
197 | --- src/knotholder.cpp 2014-08-04 16:10:37 +0000 |
198 | +++ src/knotholder.cpp 2014-09-07 17:02:32 +0000 |
199 | @@ -154,8 +154,15 @@ |
200 | } |
201 | |
202 | // for drag, this is done by ungrabbed_handler, but for click we must do it here |
203 | - DocumentUndo::done(saved_item->document, object_verb, |
204 | - _("Change handle")); |
205 | + |
206 | + if (saved_item) { //increasingly aggressive sanity checks |
207 | + if (saved_item->document) { |
208 | + if (object_verb <= SP_VERB_LAST && object_verb >= SP_VERB_INVALID) { |
209 | + DocumentUndo::done(saved_item->document, object_verb, |
210 | + _("Change handle")); |
211 | + } |
212 | + } |
213 | + } // else { abort(); } |
214 | } |
215 | |
216 | void |
217 | @@ -203,14 +210,16 @@ |
218 | /* do cleanup tasks (e.g., for LPE items write the parameter values |
219 | * that were changed by dragging the handle to SVG) |
220 | */ |
221 | - if (SP_IS_LPE_ITEM(object)) { |
222 | + if (dynamic_cast<SPLPEItem*> (object)) { |
223 | // This writes all parameters to SVG. Is this sufficiently efficient or should we only |
224 | // write the ones that were changed? |
225 | - |
226 | - Inkscape::LivePathEffect::Effect *lpe = SP_LPE_ITEM(object)->getCurrentLPE(); |
227 | - if (lpe) { |
228 | - LivePathEffectObject *lpeobj = lpe->getLPEObj(); |
229 | - lpeobj->updateRepr(); |
230 | + SPLPEItem * lpeitem = SP_LPE_ITEM(object); |
231 | + if (lpeitem) { |
232 | + Inkscape::LivePathEffect::Effect *lpe = lpeitem->getCurrentLPE(); |
233 | + if (lpe) { |
234 | + LivePathEffectObject *lpeobj = lpe->getLPEObj(); |
235 | + lpeobj->updateRepr(); |
236 | + } |
237 | } |
238 | } |
239 | |
240 | @@ -232,9 +241,14 @@ |
241 | else |
242 | object_verb = SP_VERB_SELECTION_DYNAMIC_OFFSET; |
243 | } |
244 | - |
245 | - DocumentUndo::done(object->document, object_verb, |
246 | - _("Move handle")); |
247 | + if (object) { //increasingly aggressive sanity checks |
248 | + if (object->document) { |
249 | + if (object_verb <= SP_VERB_LAST && object_verb >= SP_VERB_INVALID) { |
250 | + DocumentUndo::done(object->document, object_verb, |
251 | + _("Move handle")); |
252 | + } |
253 | + } |
254 | + } //else { abort(); } |
255 | } |
256 | } |
257 | |
258 | |
259 | === modified file 'src/live_effects/CMakeLists.txt' |
260 | --- src/live_effects/CMakeLists.txt 2014-08-12 22:30:34 +0000 |
261 | +++ src/live_effects/CMakeLists.txt 2014-09-07 17:02:32 +0000 |
262 | @@ -2,8 +2,10 @@ |
263 | set(live_effects_SRC |
264 | effect.cpp |
265 | lpe-angle_bisector.cpp |
266 | + lpe-attach-path.cpp |
267 | lpe-bendpath.cpp |
268 | lpe-boolops.cpp |
269 | + lpe-bounding-box.cpp |
270 | lpe-circle_3pts.cpp |
271 | lpe-circle_with_radius.cpp |
272 | lpe-clone-original.cpp |
273 | @@ -11,9 +13,12 @@ |
274 | lpe-copy_rotate.cpp |
275 | lpe-curvestitch.cpp |
276 | lpe-dynastroke.cpp |
277 | + lpe-ellipse-5pts.cpp |
278 | lpe-envelope.cpp |
279 | lpe-envelope-perspective.cpp |
280 | lpe-extrude.cpp |
281 | + lpe-fill-between-many.cpp |
282 | + lpe-fill-between-strokes.cpp |
283 | lpe-fillet-chamfer.cpp |
284 | lpe-gears.cpp |
285 | lpe-interpolate.cpp |
286 | @@ -55,11 +60,13 @@ |
287 | parameter/parameter.cpp |
288 | parameter/path.cpp |
289 | parameter/originalpath.cpp |
290 | + parameter/originalpatharray.cpp |
291 | parameter/path-reference.cpp |
292 | parameter/point.cpp |
293 | parameter/powerstrokepointarray.cpp |
294 | parameter/random.cpp |
295 | parameter/text.cpp |
296 | + paramter/transformedpoint.cpp |
297 | parameter/togglebutton.cpp |
298 | parameter/unit.cpp |
299 | parameter/vector.cpp |
300 | @@ -70,8 +77,10 @@ |
301 | effect-enum.h |
302 | effect.h |
303 | lpe-angle_bisector.h |
304 | + lpe-attach-path.h |
305 | lpe-bendpath.h |
306 | lpe-boolops.h |
307 | + lpe-bounding-box.h |
308 | lpe-circle_3pts.h |
309 | lpe-circle_with_radius.h |
310 | lpe-clone-original.h |
311 | @@ -79,8 +88,11 @@ |
312 | lpe-copy_rotate.h |
313 | lpe-curvestitch.h |
314 | lpe-dynastroke.h |
315 | + lpe-ellipse-5pts.h |
316 | lpe-envelope.h |
317 | lpe-extrude.h |
318 | + lpe-fill-between-many.h |
319 | + lpe-fill-between-strokes.h |
320 | lpe-fillet-chamfer.h |
321 | lpe-gears.h |
322 | lpe-interpolate.h |
323 | @@ -125,6 +137,7 @@ |
324 | parameter/path-reference.h |
325 | parameter/path.h |
326 | parameter/originalpath.h |
327 | + parameter/originalpatharray.h |
328 | parameter/point.h |
329 | parameter/powerstrokepointarray.h |
330 | parameter/random.h |
331 | |
332 | === modified file 'src/live_effects/Makefile_insert' |
333 | --- src/live_effects/Makefile_insert 2014-08-23 16:39:36 +0000 |
334 | +++ src/live_effects/Makefile_insert 2014-09-07 17:02:32 +0000 |
335 | @@ -97,5 +97,21 @@ |
336 | live_effects/lpe-path_length.h \ |
337 | live_effects/lpe-line_segment.cpp \ |
338 | live_effects/lpe-line_segment.h \ |
339 | + live_effects/lpe-bounding-box.cpp \ |
340 | + live_effects/lpe-bounding-box.h \ |
341 | + live_effects/lpe-attach-path.cpp \ |
342 | + live_effects/lpe-attach-path.h \ |
343 | + live_effects/lpe-fill-between-strokes.cpp \ |
344 | + live_effects/lpe-fill-between-strokes.h \ |
345 | + live_effects/lpe-fill-between-many.cpp \ |
346 | + live_effects/lpe-fill-between-many.h \ |
347 | + live_effects/lpe-ellipse_5pts.cpp \ |
348 | + live_effects/lpe-ellipse_5pts.h \ |
349 | + live_effects/pathoutlineprovider.cpp \ |
350 | + live_effects/pathoutlineprovider.h \ |
351 | + live_effects/lpe-jointype.cpp \ |
352 | + live_effects/lpe-jointype.h \ |
353 | + live_effects/lpe-taperstroke.cpp \ |
354 | + live_effects/lpe-taperstroke.h \ |
355 | live_effects/lpe-envelope-perspective.cpp \ |
356 | live_effects/lpe-envelope-perspective.h |
357 | |
358 | === modified file 'src/live_effects/effect-enum.h' |
359 | --- src/live_effects/effect-enum.h 2014-08-23 16:39:36 +0000 |
360 | +++ src/live_effects/effect-enum.h 2014-09-07 17:02:32 +0000 |
361 | @@ -56,6 +56,13 @@ |
362 | EXTRUDE, |
363 | POWERSTROKE, |
364 | CLONE_ORIGINAL, |
365 | + ATTACH_PATH, |
366 | + FILL_BETWEEN_STROKES, |
367 | + FILL_BETWEEN_MANY, |
368 | + ELLIPSE_5PTS, |
369 | + BOUNDING_BOX, |
370 | + JOIN_TYPE, |
371 | + TAPER_STROKE, |
372 | ENVELOPE_PERSPECTIVE, |
373 | FILLET_CHAMFER, |
374 | INVALID_LPE // This must be last (I made it such that it is not needed anymore I think..., Don't trust on it being last. - johan) |
375 | |
376 | === modified file 'src/live_effects/effect.cpp' |
377 | --- src/live_effects/effect.cpp 2014-08-23 16:39:36 +0000 |
378 | +++ src/live_effects/effect.cpp 2014-09-07 17:02:32 +0000 |
379 | @@ -5,6 +5,8 @@ |
380 | * Released under GNU GPL, read the file 'COPYING' for more information |
381 | */ |
382 | |
383 | +//#define LPE_ENABLE_TEST_EFFECTS //uncomment for toy effects |
384 | + |
385 | #ifdef HAVE_CONFIG_H |
386 | # include "config.h" |
387 | #endif |
388 | @@ -19,7 +21,6 @@ |
389 | #include "live_effects/lpe-rough-hatches.h" |
390 | #include "live_effects/lpe-dynastroke.h" |
391 | #include "live_effects/lpe-test-doEffect-stack.h" |
392 | -#include "live_effects/lpe-bspline.h" |
393 | #include "live_effects/lpe-gears.h" |
394 | #include "live_effects/lpe-curvestitch.h" |
395 | #include "live_effects/lpe-circle_with_radius.h" |
396 | @@ -51,6 +52,14 @@ |
397 | #include "live_effects/lpe-extrude.h" |
398 | #include "live_effects/lpe-powerstroke.h" |
399 | #include "live_effects/lpe-clone-original.h" |
400 | +#include "live_effects/lpe-bspline.h" |
401 | +#include "live_effects/lpe-attach-path.h" |
402 | +#include "live_effects/lpe-fill-between-strokes.h" |
403 | +#include "live_effects/lpe-fill-between-many.h" |
404 | +#include "live_effects/lpe-ellipse_5pts.h" |
405 | +#include "live_effects/lpe-bounding-box.h" |
406 | +#include "live_effects/lpe-jointype.h" |
407 | +#include "live_effects/lpe-taperstroke.h" |
408 | #include "live_effects/lpe-envelope-perspective.h" |
409 | #include "live_effects/lpe-fillet-chamfer.h" |
410 | |
411 | @@ -132,10 +141,18 @@ |
412 | {SHOW_HANDLES, N_("Show handles"), "show_handles"}, |
413 | {ROUGHEN, N_("Roughen"), "roughen"}, |
414 | {BSPLINE, N_("BSpline"), "bspline"}, |
415 | - {SIMPLIFY, N_("Simplify"), "simplify"}, |
416 | - {LATTICE2, N_("Lattice Deformation 2"), "lattice2"}, |
417 | - // TRANSLATORS: "Envelope Perspective" should be equivalent to "perspective transformation" |
418 | - {ENVELOPE_PERSPECTIVE, N_("Envelope Perspective"), "envelope-perspective"}, |
419 | + {JOIN_TYPE, N_("Join type"), "join_type"}, |
420 | + {TAPER_STROKE, N_("Taper stroke"), "taper_stroke"}, |
421 | +/* Ponyscape */ |
422 | + {ATTACH_PATH, N_("Attach path"), "attach_path"}, |
423 | + {FILL_BETWEEN_STROKES, N_("Fill between strokes"), "fill_between_strokes"}, |
424 | + {FILL_BETWEEN_MANY, N_("Fill between many"), "fill_between_many"}, |
425 | + {ELLIPSE_5PTS, N_("Ellipse by 5 points"), "ellipse_5pts"}, |
426 | + {BOUNDING_BOX, N_("Bounding Box"), "bounding_box"}, |
427 | +/* 0.91 */ |
428 | + {SIMPLIFY, N_("Simplify"), "simplify"}, |
429 | + {LATTICE2, N_("Lattice Deformation 2"), "lattice2"}, |
430 | + {ENVELOPE_PERSPECTIVE, N_("Envelope-Perspective"), "envelope-perspective"}, |
431 | {FILLET_CHAMFER, N_("Fillet/Chamfer"), "fillet-chamfer"}, |
432 | {INTERPOLATE_POINTS, N_("Interpolate points"), "interpolate_points"}, |
433 | }; |
434 | @@ -267,6 +284,27 @@ |
435 | case CLONE_ORIGINAL: |
436 | neweffect = static_cast<Effect*> ( new LPECloneOriginal(lpeobj) ); |
437 | break; |
438 | + case ATTACH_PATH: |
439 | + neweffect = static_cast<Effect*> ( new LPEAttachPath(lpeobj) ); |
440 | + break; |
441 | + case FILL_BETWEEN_STROKES: |
442 | + neweffect = static_cast<Effect*> ( new LPEFillBetweenStrokes(lpeobj) ); |
443 | + break; |
444 | + case FILL_BETWEEN_MANY: |
445 | + neweffect = static_cast<Effect*> ( new LPEFillBetweenMany(lpeobj) ); |
446 | + break; |
447 | + case ELLIPSE_5PTS: |
448 | + neweffect = static_cast<Effect*> ( new LPEEllipse5Pts(lpeobj) ); |
449 | + break; |
450 | + case BOUNDING_BOX: |
451 | + neweffect = static_cast<Effect*> ( new LPEBoundingBox(lpeobj) ); |
452 | + break; |
453 | + case JOIN_TYPE: |
454 | + neweffect = static_cast<Effect*> ( new LPEJoinType(lpeobj) ); |
455 | + break; |
456 | + case TAPER_STROKE: |
457 | + neweffect = static_cast<Effect*> ( new LPETaperStroke(lpeobj) ); |
458 | + break; |
459 | case SIMPLIFY: |
460 | neweffect = static_cast<Effect*> ( new LPESimplify(lpeobj) ); |
461 | break; |
462 | @@ -286,7 +324,7 @@ |
463 | neweffect = static_cast<Effect*> ( new LPEShowHandles(lpeobj) ); |
464 | break; |
465 | default: |
466 | - g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr); |
467 | + g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr); |
468 | neweffect = NULL; |
469 | break; |
470 | } |
471 | @@ -369,6 +407,33 @@ |
472 | //Do nothing for simple effects |
473 | } |
474 | |
475 | +void Effect::doAfterEffect (SPLPEItem const* lpeitem) |
476 | +{ |
477 | +} |
478 | + |
479 | +void Effect::doOnRemove (SPLPEItem const* lpeitem) |
480 | +{ |
481 | +} |
482 | + |
483 | +//secret impl methods (shhhh!) |
484 | +void Effect::doOnApply_impl(SPLPEItem const* lpeitem) |
485 | +{ |
486 | + sp_lpe_item = const_cast<SPLPEItem *>(lpeitem); |
487 | + /*sp_curve = SP_SHAPE(sp_lpe_item)->getCurve(); |
488 | + pathvector_before_effect = sp_curve->get_pathvector();*/ |
489 | + doOnApply(lpeitem); |
490 | +} |
491 | + |
492 | +void Effect::doBeforeEffect_impl(SPLPEItem const* lpeitem) |
493 | +{ |
494 | + sp_lpe_item = const_cast<SPLPEItem *>(lpeitem); |
495 | + //printf("(SPLPEITEM*) %p\n", sp_lpe_item); |
496 | + sp_curve = SP_SHAPE(sp_lpe_item)->getCurve(); |
497 | + pathvector_before_effect = sp_curve->get_pathvector(); |
498 | + |
499 | + doBeforeEffect(lpeitem); |
500 | +} |
501 | + |
502 | /** |
503 | * Effects can have a parameter path set before they are applied by accepting a nonzero number of |
504 | * mouse clicks. This method activates the pen context, which waits for the specified number of |
505 | |
506 | === modified file 'src/live_effects/effect.h' |
507 | --- src/live_effects/effect.h 2014-05-05 07:13:35 +0000 |
508 | +++ src/live_effects/effect.h 2014-09-07 17:02:32 +0000 |
509 | @@ -53,8 +53,16 @@ |
510 | |
511 | EffectType effectType() const; |
512 | |
513 | + //basically, to get this method called before the derived classes, a bit |
514 | + //of indirection is needed. We first call these methods, then the below. |
515 | + void doOnApply_impl(SPLPEItem const* lpeitem); |
516 | + void doBeforeEffect_impl(SPLPEItem const* lpeitem); |
517 | + |
518 | virtual void doOnApply (SPLPEItem const* lpeitem); |
519 | virtual void doBeforeEffect (SPLPEItem const* lpeitem); |
520 | + |
521 | + virtual void doAfterEffect (SPLPEItem const* lpeitem); |
522 | + virtual void doOnRemove (SPLPEItem const* lpeitem); |
523 | |
524 | void writeParamsToSVG(); |
525 | |
526 | @@ -147,6 +155,9 @@ |
527 | // instead of normally 'splitting' the path into continuous pwd2 paths and calling doEffect_pwd2 for each. |
528 | bool concatenate_before_pwd2; |
529 | |
530 | + SPLPEItem * sp_lpe_item; // these get stored in doBeforeEffect_impl, and derived classes may do as they please with them. |
531 | + SPCurve * sp_curve; |
532 | + std::vector<Geom::Path> pathvector_before_effect; |
533 | private: |
534 | bool provides_own_flash_paths; // if true, the standard flash path is suppressed |
535 | |
536 | |
537 | === added file 'src/live_effects/lpe-attach-path.cpp' |
538 | --- src/live_effects/lpe-attach-path.cpp 1970-01-01 00:00:00 +0000 |
539 | +++ src/live_effects/lpe-attach-path.cpp 2014-09-07 17:02:32 +0000 |
540 | @@ -0,0 +1,198 @@ |
541 | +/* |
542 | + * Copyright (C) Johan Engelen 2012 <j.b.c.engelen@alumnus.utwente.nl> |
543 | + * |
544 | + * Released under GNU GPL, read the file 'COPYING' for more information |
545 | + */ |
546 | + |
547 | +#include <glibmm/i18n.h> |
548 | +#include <math.h> |
549 | + |
550 | +#include "live_effects/lpe-attach-path.h" |
551 | + |
552 | +#include "display/curve.h" |
553 | +#include "sp-item.h" |
554 | +#include "2geom/path.h" |
555 | +#include "sp-shape.h" |
556 | +#include "sp-text.h" |
557 | +#include "2geom/bezier-curve.h" |
558 | +#include "2geom/path-sink.h" |
559 | +#include "parameter/parameter.h" |
560 | +#include "live_effects/parameter/point.h" |
561 | +#include "parameter/originalpath.h" |
562 | +#include "2geom/affine.h" |
563 | + |
564 | +namespace Inkscape { |
565 | +namespace LivePathEffect { |
566 | + |
567 | +LPEAttachPath::LPEAttachPath(LivePathEffectObject *lpeobject) : |
568 | + Effect(lpeobject), |
569 | + start_path(_("Start path:"), _("Path to attach to the start of this path"), "startpath", &wr, this), |
570 | + start_path_position(_("Start path position:"), _("Position to attach path start to"), "startposition", &wr, this, 0.0), |
571 | + start_path_curve_start(_("Start path curve start:"), _("Starting curve"), "startcurvestart", &wr, this, Geom::Point(20,0)/*, true*/), |
572 | + start_path_curve_end(_("Start path curve end:"), _("Ending curve"), "startcurveend", &wr, this, Geom::Point(20,0)/*, true*/), |
573 | + end_path(_("End path:"), _("Path to attach to the end of this path"), "endpath", &wr, this), |
574 | + end_path_position(_("End path position:"), _("Position to attach path end to"), "endposition", &wr, this, 0.0), |
575 | + end_path_curve_start(_("End path curve start:"), _("Starting curve"), "endcurvestart", &wr, this, Geom::Point(20,0)/*, true*/), |
576 | + end_path_curve_end(_("End path curve end:"), _("Ending curve"), "endcurveend", &wr, this, Geom::Point(20,0)/*, true*/) |
577 | +{ |
578 | + registerParameter( dynamic_cast<Parameter *>(&start_path) ); |
579 | + registerParameter( dynamic_cast<Parameter *>(&start_path_position) ); |
580 | + registerParameter( dynamic_cast<Parameter *>(&start_path_curve_start) ); |
581 | + registerParameter( dynamic_cast<Parameter *>(&start_path_curve_end) ); |
582 | + |
583 | + registerParameter( dynamic_cast<Parameter *>(&end_path) ); |
584 | + registerParameter( dynamic_cast<Parameter *>(&end_path_position) ); |
585 | + registerParameter( dynamic_cast<Parameter *>(&end_path_curve_start) ); |
586 | + registerParameter( dynamic_cast<Parameter *>(&end_path_curve_end) ); |
587 | + |
588 | + //perceived_path = true; |
589 | + show_orig_path = true; |
590 | + curve_start_previous_origin = start_path_curve_end.getOrigin(); |
591 | + curve_end_previous_origin = end_path_curve_end.getOrigin(); |
592 | +} |
593 | + |
594 | +LPEAttachPath::~LPEAttachPath() |
595 | +{ |
596 | + |
597 | +} |
598 | + |
599 | +void LPEAttachPath::resetDefaults(SPItem const * item) |
600 | +{ |
601 | + curve_start_previous_origin = start_path_curve_end.getOrigin(); |
602 | + curve_end_previous_origin = end_path_curve_end.getOrigin(); |
603 | +} |
604 | + |
605 | +void LPEAttachPath::doEffect (SPCurve * curve) |
606 | +{ |
607 | + std::vector<Geom::Path> this_pathv = curve->get_pathvector(); |
608 | + if (sp_lpe_item && !this_pathv.empty()) { |
609 | + Geom::Path p = Geom::Path(this_pathv.front().initialPoint()); |
610 | + |
611 | + bool set_start_end = start_path_curve_end.getOrigin() != curve_start_previous_origin; |
612 | + bool set_end_end = end_path_curve_end.getOrigin() != curve_end_previous_origin; |
613 | + |
614 | + if (start_path.linksToPath()) { |
615 | + |
616 | + std::vector<Geom::Path> linked_pathv = start_path.get_pathvector(); |
617 | + Geom::Affine linkedtransform = start_path.getObject()->getRelativeTransform(sp_lpe_item); |
618 | + |
619 | + if ( !linked_pathv.empty() ) |
620 | + { |
621 | + Geom::Path transformedpath = linked_pathv.front() * linkedtransform; |
622 | + start_path_curve_start.setOrigin(this_pathv.front().initialPoint()); |
623 | + |
624 | + std::vector<Geom::Point> derivs = this_pathv.front().front().pointAndDerivatives(0, 3); |
625 | + |
626 | + for (unsigned deriv_n = 1; deriv_n < derivs.size(); deriv_n++) { |
627 | + Geom::Coord length = derivs[deriv_n].length(); |
628 | + if ( ! Geom::are_near(length, 0) ) { |
629 | + if (set_start_end) { |
630 | + start_path_position.param_set_value(transformedpath.nearestPoint(start_path_curve_end.getOrigin())); |
631 | + } |
632 | + |
633 | + if (start_path_position > transformedpath.size()) { |
634 | + start_path_position.param_set_value(transformedpath.size()); |
635 | + } else if (start_path_position < 0) { |
636 | + start_path_position.param_set_value(0); |
637 | + } |
638 | + const Geom::Curve *c = start_path_position >= transformedpath.size() ? &transformedpath.back() : &transformedpath.at_index((int)start_path_position); |
639 | + |
640 | + std::vector<Geom::Point> derivs_2 = c->pointAndDerivatives(start_path_position >= transformedpath.size() ? 1 : (start_path_position - (int)start_path_position), 3); |
641 | + for (unsigned deriv_n_2 = 1; deriv_n_2 < derivs_2.size(); deriv_n_2++) { |
642 | + Geom::Coord length_2 = derivs[deriv_n_2].length(); |
643 | + if ( ! Geom::are_near(length_2, 0) ) { |
644 | + start_path_curve_end.setOrigin(derivs_2[0]); |
645 | + curve_start_previous_origin = start_path_curve_end.getOrigin(); |
646 | + |
647 | + double startangle = atan2(start_path_curve_start.getVector().y(), start_path_curve_start.getVector().x()); |
648 | + double endangle = atan2(start_path_curve_end.getVector().y(), start_path_curve_end.getVector().x()); |
649 | + double startderiv = atan2(derivs[deriv_n].y(), derivs[deriv_n].x()); |
650 | + double endderiv = atan2(derivs_2[deriv_n_2].y(), derivs_2[deriv_n_2].x()); |
651 | + Geom::Point pt1 = Geom::Point(start_path_curve_start.getVector().length() * cos(startangle + startderiv), start_path_curve_start.getVector().length() * sin(startangle + startderiv)); |
652 | + Geom::Point pt2 = Geom::Point(start_path_curve_end.getVector().length() * cos(endangle + endderiv), start_path_curve_end.getVector().length() * sin(endangle + endderiv)); |
653 | + p = Geom::Path(derivs_2[0]); |
654 | + p.appendNew<Geom::CubicBezier>(-pt2 + derivs_2[0], -pt1 + this_pathv.front().initialPoint(), this_pathv.front().initialPoint()); |
655 | + break; |
656 | + |
657 | + } |
658 | + } |
659 | + break; |
660 | + } |
661 | + } |
662 | + } |
663 | + } |
664 | + |
665 | + p.append(this_pathv.front()); |
666 | + |
667 | + if (end_path.linksToPath()) { |
668 | + |
669 | + std::vector<Geom::Path> linked_pathv = end_path.get_pathvector(); |
670 | + Geom::Affine linkedtransform = end_path.getObject()->getRelativeTransform(sp_lpe_item); |
671 | + |
672 | + if ( !linked_pathv.empty() ) |
673 | + { |
674 | + Geom::Path transformedpath = linked_pathv.front() * linkedtransform; |
675 | + Geom::Curve * last_seg_reverse = this_pathv.front().back().reverse(); |
676 | + |
677 | + end_path_curve_start.setOrigin(last_seg_reverse->initialPoint()); |
678 | + |
679 | + std::vector<Geom::Point> derivs = last_seg_reverse->pointAndDerivatives(0, 3); |
680 | + for (unsigned deriv_n = 1; deriv_n < derivs.size(); deriv_n++) { |
681 | + Geom::Coord length = derivs[deriv_n].length(); |
682 | + if ( ! Geom::are_near(length, 0) ) { |
683 | + if (set_end_end) { |
684 | + end_path_position.param_set_value(transformedpath.nearestPoint(end_path_curve_end.getOrigin())); |
685 | + } |
686 | + |
687 | + if (end_path_position > transformedpath.size()) { |
688 | + end_path_position.param_set_value(transformedpath.size()); |
689 | + } else if (end_path_position < 0) { |
690 | + end_path_position.param_set_value(0); |
691 | + } |
692 | + const Geom::Curve *c = end_path_position >= transformedpath.size() ? &transformedpath.back() : &transformedpath.at_index((int)end_path_position); |
693 | + |
694 | + std::vector<Geom::Point> derivs_2 = c->pointAndDerivatives(end_path_position >= transformedpath.size() ? 1 : (end_path_position - (int)end_path_position), 3); |
695 | + for (unsigned deriv_n_2 = 1; deriv_n_2 < derivs_2.size(); deriv_n_2++) { |
696 | + Geom::Coord length_2 = derivs[deriv_n_2].length(); |
697 | + if ( ! Geom::are_near(length_2, 0) ) { |
698 | + |
699 | + end_path_curve_end.setOrigin(derivs_2[0]); |
700 | + curve_end_previous_origin = end_path_curve_end.getOrigin(); |
701 | + |
702 | + double startangle = atan2(end_path_curve_start.getVector().y(), end_path_curve_start.getVector().x()); |
703 | + double endangle = atan2(end_path_curve_end.getVector().y(), end_path_curve_end.getVector().x()); |
704 | + double startderiv = atan2(derivs[deriv_n].y(), derivs[deriv_n].x()); |
705 | + double endderiv = atan2(derivs_2[deriv_n_2].y(), derivs_2[deriv_n_2].x()); |
706 | + Geom::Point pt1 = Geom::Point(end_path_curve_start.getVector().length() * cos(startangle + startderiv), end_path_curve_start.getVector().length() * sin(startangle + startderiv)); |
707 | + Geom::Point pt2 = Geom::Point(end_path_curve_end.getVector().length() * cos(endangle + endderiv), end_path_curve_end.getVector().length() * sin(endangle + endderiv)); |
708 | + p.appendNew<Geom::CubicBezier>(-pt1 + this_pathv.front().finalPoint(), -pt2 + derivs_2[0], derivs_2[0]); |
709 | + |
710 | + break; |
711 | + |
712 | + } |
713 | + } |
714 | + break; |
715 | + } |
716 | + } |
717 | + delete last_seg_reverse; |
718 | + } |
719 | + } |
720 | + Geom::PathVector outvector; |
721 | + outvector.push_back(p); |
722 | + curve->set_pathvector(outvector); |
723 | + } |
724 | +} |
725 | + |
726 | +} // namespace LivePathEffect |
727 | +} /* namespace Inkscape */ |
728 | + |
729 | +/* |
730 | + Local Variables: |
731 | + mode:c++ |
732 | + c-file-style:"stroustrup" |
733 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
734 | + indent-tabs-mode:nil |
735 | + fill-column:99 |
736 | + End: |
737 | +*/ |
738 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
739 | |
740 | === added file 'src/live_effects/lpe-attach-path.h' |
741 | --- src/live_effects/lpe-attach-path.h 1970-01-01 00:00:00 +0000 |
742 | +++ src/live_effects/lpe-attach-path.h 2014-09-07 17:02:32 +0000 |
743 | @@ -0,0 +1,52 @@ |
744 | +#ifndef INKSCAPE_LPE_ATTACH_PATH_H |
745 | +#define INKSCAPE_LPE_ATTACH_PATH_H |
746 | + |
747 | +/* |
748 | + * Inkscape::LPEAttachPath |
749 | + * |
750 | + * Copyright (C) Ted Janeczko 2012 <flutterguy317@gmail.com> |
751 | + * |
752 | + * Released under GNU GPL, read the file 'COPYING' for more information |
753 | + */ |
754 | + |
755 | +#include "live_effects/effect.h" |
756 | +#include "live_effects/parameter/parameter.h" |
757 | +#include "live_effects/parameter/point.h" |
758 | +#include "live_effects/parameter/originalpath.h" |
759 | +#include "live_effects/parameter/vector.h" |
760 | +#include "live_effects/parameter/bool.h" |
761 | +#include "live_effects/parameter/transformedpoint.h" |
762 | + |
763 | +namespace Inkscape { |
764 | +namespace LivePathEffect { |
765 | + |
766 | +class LPEAttachPath : public Effect { |
767 | +public: |
768 | + LPEAttachPath(LivePathEffectObject *lpeobject); |
769 | + virtual ~LPEAttachPath(); |
770 | + |
771 | + virtual void doEffect (SPCurve * curve); |
772 | + virtual void resetDefaults(SPItem const * item); |
773 | + |
774 | +private: |
775 | + LPEAttachPath(const LPEAttachPath&); |
776 | + LPEAttachPath& operator=(const LPEAttachPath&); |
777 | + |
778 | + Geom::Point curve_start_previous_origin; |
779 | + Geom::Point curve_end_previous_origin; |
780 | + |
781 | + OriginalPathParam start_path; |
782 | + ScalarParam start_path_position; |
783 | + TransformedPointParam start_path_curve_start; |
784 | + VectorParam start_path_curve_end; |
785 | + |
786 | + OriginalPathParam end_path; |
787 | + ScalarParam end_path_position; |
788 | + TransformedPointParam end_path_curve_start; |
789 | + VectorParam end_path_curve_end; |
790 | +}; |
791 | + |
792 | +}; //namespace LivePathEffect |
793 | +}; //namespace Inkscape |
794 | + |
795 | +#endif |
796 | |
797 | === added file 'src/live_effects/lpe-bounding-box.cpp' |
798 | --- src/live_effects/lpe-bounding-box.cpp 1970-01-01 00:00:00 +0000 |
799 | +++ src/live_effects/lpe-bounding-box.cpp 2014-09-07 17:02:32 +0000 |
800 | @@ -0,0 +1,67 @@ |
801 | +/* |
802 | + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> |
803 | + * |
804 | + * Released under GNU GPL, read the file 'COPYING' for more information |
805 | + */ |
806 | + |
807 | +#include <glibmm/i18n.h> |
808 | + |
809 | +#include "live_effects/lpe-bounding-box.h" |
810 | + |
811 | +#include "display/curve.h" |
812 | +#include "sp-item.h" |
813 | +#include "2geom/path.h" |
814 | +#include "sp-shape.h" |
815 | +#include "sp-text.h" |
816 | +#include "2geom/bezier-curve.h" |
817 | +#include "lpe-bounding-box.h" |
818 | + |
819 | +namespace Inkscape { |
820 | +namespace LivePathEffect { |
821 | + |
822 | +LPEBoundingBox::LPEBoundingBox(LivePathEffectObject *lpeobject) : |
823 | + Effect(lpeobject), |
824 | + linked_path(_("Linked path:"), _("Path from which to take the original path data"), "linkedpath", &wr, this), |
825 | + visual_bounds(_("Visual Bounds"), _("Uses the visual bounding box"), "visualbounds", &wr, this) |
826 | +{ |
827 | + registerParameter( dynamic_cast<Parameter *>(&linked_path) ); |
828 | + registerParameter( dynamic_cast<Parameter *>(&visual_bounds) ); |
829 | + //perceived_path = true; |
830 | +} |
831 | + |
832 | +LPEBoundingBox::~LPEBoundingBox() |
833 | +{ |
834 | + |
835 | +} |
836 | + |
837 | +void LPEBoundingBox::doEffect (SPCurve * curve) |
838 | +{ |
839 | + if (curve) { |
840 | + if ( linked_path.linksToPath() && linked_path.getObject() ) { |
841 | + SPItem * item = linked_path.getObject(); |
842 | + Geom::OptRect bbox = visual_bounds.get_value() ? item->visualBounds() : item->geometricBounds(); |
843 | + Geom::Path p(Geom::Point(bbox->left(), bbox->top())); |
844 | + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->top())); |
845 | + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->bottom())); |
846 | + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->bottom())); |
847 | + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->top())); |
848 | + std::vector<Geom::Path> out; |
849 | + out.push_back(p); |
850 | + curve->set_pathvector(out); |
851 | + } |
852 | + } |
853 | +} |
854 | + |
855 | +} // namespace LivePathEffect |
856 | +} /* namespace Inkscape */ |
857 | + |
858 | +/* |
859 | + Local Variables: |
860 | + mode:c++ |
861 | + c-file-style:"stroustrup" |
862 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
863 | + indent-tabs-mode:nil |
864 | + fill-column:99 |
865 | + End: |
866 | +*/ |
867 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
868 | |
869 | === added file 'src/live_effects/lpe-bounding-box.h' |
870 | --- src/live_effects/lpe-bounding-box.h 1970-01-01 00:00:00 +0000 |
871 | +++ src/live_effects/lpe-bounding-box.h 2014-09-07 17:02:32 +0000 |
872 | @@ -0,0 +1,37 @@ |
873 | +#ifndef INKSCAPE_LPE_BOUNDING_BOX_H |
874 | +#define INKSCAPE_LPE_BOUNDING_BOX_H |
875 | + |
876 | +/* |
877 | + * Inkscape::LPEFillBetweenStrokes |
878 | + * |
879 | + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> |
880 | + * |
881 | + * Released under GNU GPL, read the file 'COPYING' for more information |
882 | + */ |
883 | + |
884 | +#include "live_effects/effect.h" |
885 | +#include "live_effects/parameter/originalpath.h" |
886 | + |
887 | +namespace Inkscape { |
888 | +namespace LivePathEffect { |
889 | + |
890 | +class LPEBoundingBox : public Effect { |
891 | +public: |
892 | + LPEBoundingBox(LivePathEffectObject *lpeobject); |
893 | + virtual ~LPEBoundingBox(); |
894 | + |
895 | + virtual void doEffect (SPCurve * curve); |
896 | + |
897 | +private: |
898 | + OriginalPathParam linked_path; |
899 | + BoolParam visual_bounds; |
900 | + |
901 | +private: |
902 | + LPEBoundingBox(const LPEBoundingBox&); |
903 | + LPEBoundingBox& operator=(const LPEBoundingBox&); |
904 | +}; |
905 | + |
906 | +}; //namespace LivePathEffect |
907 | +}; //namespace Inkscape |
908 | + |
909 | +#endif |
910 | |
911 | === added file 'src/live_effects/lpe-ellipse_5pts.cpp' |
912 | --- src/live_effects/lpe-ellipse_5pts.cpp 1970-01-01 00:00:00 +0000 |
913 | +++ src/live_effects/lpe-ellipse_5pts.cpp 2014-09-07 17:02:32 +0000 |
914 | @@ -0,0 +1,214 @@ |
915 | +/** \file |
916 | + * LPE "Ellipse through 5 points" implementation |
917 | + */ |
918 | + |
919 | +/* |
920 | + * Authors: |
921 | + * Theodore Janeczko |
922 | + * |
923 | + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> |
924 | + * |
925 | + * Released under GNU GPL, read the file 'COPYING' for more information |
926 | + */ |
927 | + |
928 | +#include "live_effects/lpe-ellipse_5pts.h" |
929 | + |
930 | +// You might need to include other 2geom files. You can add them here: |
931 | +#include <glibmm/i18n.h> |
932 | +#include <2geom/path.h> |
933 | +#include <2geom/circle.h> |
934 | +#include <2geom/ellipse.h> |
935 | +#include <2geom/path-sink.h> |
936 | +#include "inkscape.h" |
937 | +#include "desktop.h" |
938 | +#include "message-stack.h" |
939 | + |
940 | +namespace Inkscape { |
941 | +namespace LivePathEffect { |
942 | + |
943 | +LPEEllipse5Pts::LPEEllipse5Pts(LivePathEffectObject *lpeobject) : |
944 | + Effect(lpeobject) |
945 | +{ |
946 | + //perceived_path = true; |
947 | +} |
948 | + |
949 | +LPEEllipse5Pts::~LPEEllipse5Pts() |
950 | +{ |
951 | +} |
952 | + |
953 | +static double _det3(double (*mat)[3]) |
954 | +{ |
955 | + for (int i = 0; i < 2; i++) |
956 | + { |
957 | + for (int j = i + 1; j < 3; j++) |
958 | + { |
959 | + for (int k = i + 1; k < 3; k++) |
960 | + { |
961 | + mat[j][k] = (mat[j][k] * mat[i][i] - mat[j][i] * mat[i][k]); |
962 | + if (i) mat[j][k] /= mat[i-1][i-1]; |
963 | + } |
964 | + } |
965 | + } |
966 | + return mat[2][2]; |
967 | +} |
968 | +static double _det5(double (*mat)[5]) |
969 | +{ |
970 | + for (int i = 0; i < 4; i++) |
971 | + { |
972 | + for (int j = i + 1; j < 5; j++) |
973 | + { |
974 | + for (int k = i + 1; k < 5; k++) |
975 | + { |
976 | + mat[j][k] = (mat[j][k] * mat[i][i] - mat[j][i] * mat[i][k]); |
977 | + if (i) mat[j][k] /= mat[i-1][i-1]; |
978 | + } |
979 | + } |
980 | + } |
981 | + return mat[4][4]; |
982 | +} |
983 | + |
984 | +std::vector<Geom::Path> |
985 | +LPEEllipse5Pts::doEffect_path (std::vector<Geom::Path> const & path_in) |
986 | +{ |
987 | + std::vector<Geom::Path> path_out = std::vector<Geom::Path>(); |
988 | + |
989 | + if (path_in[0].size() < 4) { |
990 | + |
991 | + SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Five points required for constructing an ellipse")); |
992 | + return path_in; |
993 | + } |
994 | + // we assume that the path has >= 3 nodes |
995 | + Geom::Point A = path_in[0].initialPoint(); |
996 | + Geom::Point B = path_in[0].pointAt(1); |
997 | + Geom::Point C = path_in[0].pointAt(2); |
998 | + Geom::Point D = path_in[0].pointAt(3); |
999 | + Geom::Point E = path_in[0].pointAt(4); |
1000 | + |
1001 | + using namespace Geom; |
1002 | + |
1003 | + double rowmajor_matrix[5][6] = |
1004 | + { |
1005 | + {A.x()*A.x(), A.x()*A.y(), A.y()*A.y(), A.x(), A.y(), 1}, |
1006 | + {B.x()*B.x(), B.x()*B.y(), B.y()*B.y(), B.x(), B.y(), 1}, |
1007 | + {C.x()*C.x(), C.x()*C.y(), C.y()*C.y(), C.x(), C.y(), 1}, |
1008 | + {D.x()*D.x(), D.x()*D.y(), D.y()*D.y(), D.x(), D.y(), 1}, |
1009 | + {E.x()*E.x(), E.x()*E.y(), E.y()*E.y(), E.x(), E.y(), 1} |
1010 | + }; |
1011 | + |
1012 | + double mat_a[5][5] = |
1013 | + { |
1014 | + {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]}, |
1015 | + {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]}, |
1016 | + {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]}, |
1017 | + {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]}, |
1018 | + {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]} |
1019 | + }; |
1020 | + double mat_b[5][5] = |
1021 | + { |
1022 | + {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]}, |
1023 | + {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]}, |
1024 | + {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]}, |
1025 | + {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]}, |
1026 | + {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]} |
1027 | + }; |
1028 | + double mat_c[5][5] = |
1029 | + { |
1030 | + {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]}, |
1031 | + {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]}, |
1032 | + {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]}, |
1033 | + {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]}, |
1034 | + {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]} |
1035 | + }; |
1036 | + double mat_d[5][5] = |
1037 | + { |
1038 | + {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]}, |
1039 | + {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]}, |
1040 | + {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]}, |
1041 | + {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]}, |
1042 | + {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]} |
1043 | + }; |
1044 | + double mat_e[5][5] = |
1045 | + { |
1046 | + {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]}, |
1047 | + {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]}, |
1048 | + {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]}, |
1049 | + {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]}, |
1050 | + {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]} |
1051 | + }; |
1052 | + double mat_f[5][5] = |
1053 | + { |
1054 | + {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]}, |
1055 | + {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]}, |
1056 | + {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]}, |
1057 | + {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]}, |
1058 | + {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]} |
1059 | + }; |
1060 | + |
1061 | + double a1 = _det5(mat_a); |
1062 | + double b1 = -_det5(mat_b); |
1063 | + double c1 = _det5(mat_c); |
1064 | + double d1 = -_det5(mat_d); |
1065 | + double e1 = _det5(mat_e); |
1066 | + double f1 = -_det5(mat_f); |
1067 | + |
1068 | + double mat_check[][3] = |
1069 | + { |
1070 | + {a1, b1/2, d1/2}, |
1071 | + {b1/2, c1, e1/2}, |
1072 | + {d1/2, e1/2, f1} |
1073 | + }; |
1074 | + |
1075 | + if (_det3(mat_check) == 0 || a1*c1 - b1*b1/4 <= 0) { |
1076 | + SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No ellipse found for specified points")); |
1077 | + return path_in; |
1078 | + } |
1079 | + |
1080 | + Geom::Ellipse el(a1, b1, c1, d1, e1, f1); |
1081 | + |
1082 | + double s, e; |
1083 | + double x0, y0, x1, y1, x2, y2, x3, y3; |
1084 | + double len; |
1085 | + |
1086 | + // figure out if we have a slice, guarding against rounding errors |
1087 | + |
1088 | + Path p(Geom::Point(cos(0), sin(0))); |
1089 | + |
1090 | + double end = 2 * M_PI; |
1091 | + for (s = 0; s < end; s += M_PI_2) { |
1092 | + e = s + M_PI_2; |
1093 | + if (e > end) |
1094 | + e = end; |
1095 | + len = 4*tan((e - s)/4)/3; |
1096 | + x0 = cos(s); |
1097 | + y0 = sin(s); |
1098 | + x1 = x0 + len * cos(s + M_PI_2); |
1099 | + y1 = y0 + len * sin(s + M_PI_2); |
1100 | + x3 = cos(e); |
1101 | + y3 = sin(e); |
1102 | + x2 = x3 + len * cos(e - M_PI_2); |
1103 | + y2 = y3 + len * sin(e - M_PI_2); |
1104 | + p.appendNew<Geom::CubicBezier>(Geom::Point(x1,y1), Geom::Point(x2,y2), Geom::Point(x3,y3)); |
1105 | + } |
1106 | + |
1107 | + Geom::Affine aff = Geom::Scale(el.ray(Geom::X), el.ray(Geom::Y)) * Geom::Rotate(el.rot_angle()) * Geom::Translate(el.center()); |
1108 | + |
1109 | + path_out.push_back(p * aff); |
1110 | + |
1111 | + return path_out; |
1112 | +} |
1113 | + |
1114 | +/* ######################## */ |
1115 | + |
1116 | +} //namespace LivePathEffect |
1117 | +} /* namespace Inkscape */ |
1118 | + |
1119 | +/* |
1120 | + Local Variables: |
1121 | + mode:c++ |
1122 | + c-file-style:"stroustrup" |
1123 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
1124 | + indent-tabs-mode:nil |
1125 | + fill-column:99 |
1126 | + End: |
1127 | +*/ |
1128 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
1129 | |
1130 | === added file 'src/live_effects/lpe-ellipse_5pts.h' |
1131 | --- src/live_effects/lpe-ellipse_5pts.h 1970-01-01 00:00:00 +0000 |
1132 | +++ src/live_effects/lpe-ellipse_5pts.h 2014-09-07 17:02:32 +0000 |
1133 | @@ -0,0 +1,50 @@ |
1134 | +#ifndef INKSCAPE_LPE_ELLIPSE_5PTS_H |
1135 | +#define INKSCAPE_LPE_ELLIPSE_5PTS_H |
1136 | + |
1137 | +/** \file |
1138 | + * LPE "Ellipse through 5 points" implementation |
1139 | + */ |
1140 | + |
1141 | +/* |
1142 | + * Authors: |
1143 | + * Theodore Janeczko |
1144 | + * |
1145 | + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> |
1146 | + * |
1147 | + * Released under GNU GPL, read the file 'COPYING' for more information |
1148 | + */ |
1149 | + |
1150 | +#include "live_effects/effect.h" |
1151 | +#include "live_effects/parameter/parameter.h" |
1152 | +#include "live_effects/parameter/point.h" |
1153 | + |
1154 | +namespace Inkscape { |
1155 | +namespace LivePathEffect { |
1156 | + |
1157 | +class LPEEllipse5Pts : public Effect { |
1158 | +public: |
1159 | + LPEEllipse5Pts(LivePathEffectObject *lpeobject); |
1160 | + virtual ~LPEEllipse5Pts(); |
1161 | + |
1162 | + virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); |
1163 | + |
1164 | +private: |
1165 | + LPEEllipse5Pts(const LPEEllipse5Pts&); |
1166 | + LPEEllipse5Pts& operator=(const LPEEllipse5Pts&); |
1167 | +}; |
1168 | + |
1169 | +} //namespace LivePathEffect |
1170 | +} //namespace Inkscape |
1171 | + |
1172 | +#endif |
1173 | + |
1174 | +/* |
1175 | + Local Variables: |
1176 | + mode:c++ |
1177 | + c-file-style:"stroustrup" |
1178 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
1179 | + indent-tabs-mode:nil |
1180 | + fill-column:99 |
1181 | + End: |
1182 | +*/ |
1183 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
1184 | |
1185 | === added file 'src/live_effects/lpe-fill-between-many.cpp' |
1186 | --- src/live_effects/lpe-fill-between-many.cpp 1970-01-01 00:00:00 +0000 |
1187 | +++ src/live_effects/lpe-fill-between-many.cpp 2014-09-07 17:02:32 +0000 |
1188 | @@ -0,0 +1,78 @@ |
1189 | +/* |
1190 | + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> |
1191 | + * |
1192 | + * Released under GNU GPL, read the file 'COPYING' for more information |
1193 | + */ |
1194 | + |
1195 | +#include <gtkmm/box.h> |
1196 | + |
1197 | +#include "live_effects/lpe-fill-between-many.h" |
1198 | + |
1199 | +#include "display/curve.h" |
1200 | +#include "sp-item.h" |
1201 | +#include "2geom/path.h" |
1202 | +#include "sp-shape.h" |
1203 | +#include "sp-text.h" |
1204 | +#include "2geom/bezier-curve.h" |
1205 | + |
1206 | +#include <glibmm/i18n.h> |
1207 | + |
1208 | +namespace Inkscape { |
1209 | +namespace LivePathEffect { |
1210 | + |
1211 | +LPEFillBetweenMany::LPEFillBetweenMany(LivePathEffectObject *lpeobject) : |
1212 | + Effect(lpeobject), |
1213 | + linked_paths(_("Linked path:"), _("Paths from which to take the original path data"), "linkedpaths", &wr, this) |
1214 | +{ |
1215 | + registerParameter( dynamic_cast<Parameter *>(&linked_paths) ); |
1216 | + //perceived_path = true; |
1217 | +} |
1218 | + |
1219 | +LPEFillBetweenMany::~LPEFillBetweenMany() |
1220 | +{ |
1221 | + |
1222 | +} |
1223 | + |
1224 | +void LPEFillBetweenMany::doEffect (SPCurve * curve) |
1225 | +{ |
1226 | + std::vector<Geom::Path> res_pathv; |
1227 | + SPItem * firstObj = NULL; |
1228 | + for (std::vector<PathAndDirection*>::iterator iter = linked_paths._vector.begin(); iter != linked_paths._vector.end(); iter++) { |
1229 | + SPObject *obj; |
1230 | + if ((*iter)->ref.isAttached() && (obj = (*iter)->ref.getObject()) && SP_IS_ITEM(obj) && !(*iter)->_pathvector.empty()) { |
1231 | + Geom::Path linked_path; |
1232 | + if ((*iter)->reversed) { |
1233 | + linked_path = (*iter)->_pathvector.front().reverse(); |
1234 | + } else { |
1235 | + linked_path = (*iter)->_pathvector.front(); |
1236 | + } |
1237 | + |
1238 | + if (!res_pathv.empty()) { |
1239 | + linked_path = linked_path * SP_ITEM(obj)->getRelativeTransform(firstObj); |
1240 | + res_pathv.front().appendNew<Geom::LineSegment>(linked_path.initialPoint()); |
1241 | + res_pathv.front().append(linked_path); |
1242 | + } else { |
1243 | + firstObj = SP_ITEM(obj); |
1244 | + res_pathv.push_back(linked_path); |
1245 | + } |
1246 | + } |
1247 | + } |
1248 | + if (!res_pathv.empty()) { |
1249 | + res_pathv.front().close(); |
1250 | + } |
1251 | + curve->set_pathvector(res_pathv); |
1252 | +} |
1253 | + |
1254 | +} // namespace LivePathEffect |
1255 | +} /* namespace Inkscape */ |
1256 | + |
1257 | +/* |
1258 | + Local Variables: |
1259 | + mode:c++ |
1260 | + c-file-style:"stroustrup" |
1261 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
1262 | + indent-tabs-mode:nil |
1263 | + fill-column:99 |
1264 | + End: |
1265 | +*/ |
1266 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
1267 | |
1268 | === added file 'src/live_effects/lpe-fill-between-many.h' |
1269 | --- src/live_effects/lpe-fill-between-many.h 1970-01-01 00:00:00 +0000 |
1270 | +++ src/live_effects/lpe-fill-between-many.h 2014-09-07 17:02:32 +0000 |
1271 | @@ -0,0 +1,36 @@ |
1272 | +#ifndef INKSCAPE_LPE_FILL_BETWEEN_MANY_H |
1273 | +#define INKSCAPE_LPE_FILL_BETWEEN_MANY_H |
1274 | + |
1275 | +/* |
1276 | + * Inkscape::LPEFillBetweenStrokes |
1277 | + * |
1278 | + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> |
1279 | + * |
1280 | + * Released under GNU GPL, read the file 'COPYING' for more information |
1281 | + */ |
1282 | + |
1283 | +#include "live_effects/effect.h" |
1284 | +#include "live_effects/parameter/originalpatharray.h" |
1285 | + |
1286 | +namespace Inkscape { |
1287 | +namespace LivePathEffect { |
1288 | + |
1289 | +class LPEFillBetweenMany : public Effect { |
1290 | +public: |
1291 | + LPEFillBetweenMany(LivePathEffectObject *lpeobject); |
1292 | + virtual ~LPEFillBetweenMany(); |
1293 | + |
1294 | + virtual void doEffect (SPCurve * curve); |
1295 | + |
1296 | +private: |
1297 | + OriginalPathArrayParam linked_paths; |
1298 | + |
1299 | +private: |
1300 | + LPEFillBetweenMany(const LPEFillBetweenMany&); |
1301 | + LPEFillBetweenMany& operator=(const LPEFillBetweenMany&); |
1302 | +}; |
1303 | + |
1304 | +}; //namespace LivePathEffect |
1305 | +}; //namespace Inkscape |
1306 | + |
1307 | +#endif |
1308 | |
1309 | === added file 'src/live_effects/lpe-fill-between-strokes.cpp' |
1310 | --- src/live_effects/lpe-fill-between-strokes.cpp 1970-01-01 00:00:00 +0000 |
1311 | +++ src/live_effects/lpe-fill-between-strokes.cpp 2014-09-07 17:02:32 +0000 |
1312 | @@ -0,0 +1,116 @@ |
1313 | +/* |
1314 | + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> |
1315 | + * |
1316 | + * Released under GNU GPL, read the file 'COPYING' for more information |
1317 | + */ |
1318 | + |
1319 | +#include <glibmm/i18n.h> |
1320 | + |
1321 | +#include "live_effects/lpe-fill-between-strokes.h" |
1322 | + |
1323 | +#include "display/curve.h" |
1324 | +#include "sp-item.h" |
1325 | +#include "2geom/path.h" |
1326 | +#include "sp-shape.h" |
1327 | +#include "sp-text.h" |
1328 | +#include "2geom/bezier-curve.h" |
1329 | + |
1330 | +namespace Inkscape { |
1331 | +namespace LivePathEffect { |
1332 | + |
1333 | +LPEFillBetweenStrokes::LPEFillBetweenStrokes(LivePathEffectObject *lpeobject) : |
1334 | + Effect(lpeobject), |
1335 | + linked_path(_("Linked path:"), _("Path from which to take the original path data"), "linkedpath", &wr, this), |
1336 | + second_path(_("Second path:"), _("Second path from which to take the original path data"), "secondpath", &wr, this), |
1337 | + reverse_second(_("Reverse Second"), _("Reverses the second path order"), "reversesecond", &wr, this) |
1338 | +{ |
1339 | + registerParameter( dynamic_cast<Parameter *>(&linked_path) ); |
1340 | + registerParameter( dynamic_cast<Parameter *>(&second_path) ); |
1341 | + registerParameter( dynamic_cast<Parameter *>(&reverse_second) ); |
1342 | + //perceived_path = true; |
1343 | +} |
1344 | + |
1345 | +LPEFillBetweenStrokes::~LPEFillBetweenStrokes() |
1346 | +{ |
1347 | + |
1348 | +} |
1349 | + |
1350 | +void LPEFillBetweenStrokes::doEffect (SPCurve * curve) |
1351 | +{ |
1352 | + if (curve) { |
1353 | + if ( linked_path.linksToPath() && second_path.linksToPath() && linked_path.getObject() && second_path.getObject() ) { |
1354 | + std::vector<Geom::Path> linked_pathv = linked_path.get_pathvector(); |
1355 | + std::vector<Geom::Path> second_pathv = second_path.get_pathvector(); |
1356 | + std::vector<Geom::Path> result_linked_pathv; |
1357 | + std::vector<Geom::Path> result_second_pathv; |
1358 | + Geom::Affine second_transform = second_path.getObject()->getRelativeTransform(linked_path.getObject()); |
1359 | + |
1360 | + for (std::vector<Geom::Path>::iterator iter = linked_pathv.begin(); iter != linked_pathv.end(); ++iter) |
1361 | + { |
1362 | + result_linked_pathv.push_back((*iter)); |
1363 | + } |
1364 | + for (std::vector<Geom::Path>::iterator iter = second_pathv.begin(); iter != second_pathv.end(); ++iter) |
1365 | + { |
1366 | + result_second_pathv.push_back((*iter) * second_transform); |
1367 | + } |
1368 | + |
1369 | + if ( !result_linked_pathv.empty() && !result_second_pathv.empty() && !result_linked_pathv.front().closed() ) { |
1370 | + if (reverse_second.get_value()) |
1371 | + { |
1372 | + result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().finalPoint()); |
1373 | + result_linked_pathv.front().append(result_second_pathv.front().reverse()); |
1374 | + } |
1375 | + else |
1376 | + { |
1377 | + result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().initialPoint()); |
1378 | + result_linked_pathv.front().append(result_second_pathv.front()); |
1379 | + } |
1380 | + curve->set_pathvector(result_linked_pathv); |
1381 | + } |
1382 | + else if ( !result_linked_pathv.empty() ) { |
1383 | + curve->set_pathvector(result_linked_pathv); |
1384 | + } |
1385 | + else if ( !result_second_pathv.empty() ) { |
1386 | + curve->set_pathvector(result_second_pathv); |
1387 | + } |
1388 | + } |
1389 | + else if ( linked_path.linksToPath() && linked_path.getObject() ) { |
1390 | + std::vector<Geom::Path> linked_pathv = linked_path.get_pathvector(); |
1391 | + std::vector<Geom::Path> result_pathv; |
1392 | + |
1393 | + for (std::vector<Geom::Path>::iterator iter = linked_pathv.begin(); iter != linked_pathv.end(); ++iter) |
1394 | + { |
1395 | + result_pathv.push_back((*iter)); |
1396 | + } |
1397 | + if ( !result_pathv.empty() ) { |
1398 | + curve->set_pathvector(result_pathv); |
1399 | + } |
1400 | + } |
1401 | + else if ( second_path.linksToPath() && second_path.getObject() ) { |
1402 | + std::vector<Geom::Path> second_pathv = second_path.get_pathvector(); |
1403 | + std::vector<Geom::Path> result_pathv; |
1404 | + |
1405 | + for (std::vector<Geom::Path>::iterator iter = second_pathv.begin(); iter != second_pathv.end(); ++iter) |
1406 | + { |
1407 | + result_pathv.push_back((*iter)); |
1408 | + } |
1409 | + if ( !result_pathv.empty() ) { |
1410 | + curve->set_pathvector(result_pathv); |
1411 | + } |
1412 | + } |
1413 | + } |
1414 | +} |
1415 | + |
1416 | +} // namespace LivePathEffect |
1417 | +} /* namespace Inkscape */ |
1418 | + |
1419 | +/* |
1420 | + Local Variables: |
1421 | + mode:c++ |
1422 | + c-file-style:"stroustrup" |
1423 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
1424 | + indent-tabs-mode:nil |
1425 | + fill-column:99 |
1426 | + End: |
1427 | +*/ |
1428 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
1429 | |
1430 | === added file 'src/live_effects/lpe-fill-between-strokes.h' |
1431 | --- src/live_effects/lpe-fill-between-strokes.h 1970-01-01 00:00:00 +0000 |
1432 | +++ src/live_effects/lpe-fill-between-strokes.h 2014-09-07 17:02:32 +0000 |
1433 | @@ -0,0 +1,38 @@ |
1434 | +#ifndef INKSCAPE_LPE_FILL_BETWEEN_STROKES_H |
1435 | +#define INKSCAPE_LPE_FILL_BETWEEN_STROKES_H |
1436 | + |
1437 | +/* |
1438 | + * Inkscape::LPEFillBetweenStrokes |
1439 | + * |
1440 | + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> |
1441 | + * |
1442 | + * Released under GNU GPL, read the file 'COPYING' for more information |
1443 | + */ |
1444 | + |
1445 | +#include "live_effects/effect.h" |
1446 | +#include "live_effects/parameter/originalpath.h" |
1447 | + |
1448 | +namespace Inkscape { |
1449 | +namespace LivePathEffect { |
1450 | + |
1451 | +class LPEFillBetweenStrokes : public Effect { |
1452 | +public: |
1453 | + LPEFillBetweenStrokes(LivePathEffectObject *lpeobject); |
1454 | + virtual ~LPEFillBetweenStrokes(); |
1455 | + |
1456 | + virtual void doEffect (SPCurve * curve); |
1457 | + |
1458 | +private: |
1459 | + OriginalPathParam linked_path; |
1460 | + OriginalPathParam second_path; |
1461 | + BoolParam reverse_second; |
1462 | + |
1463 | +private: |
1464 | + LPEFillBetweenStrokes(const LPEFillBetweenStrokes&); |
1465 | + LPEFillBetweenStrokes& operator=(const LPEFillBetweenStrokes&); |
1466 | +}; |
1467 | + |
1468 | +}; //namespace LivePathEffect |
1469 | +}; //namespace Inkscape |
1470 | + |
1471 | +#endif |
1472 | |
1473 | === added file 'src/live_effects/lpe-jointype.cpp' |
1474 | --- src/live_effects/lpe-jointype.cpp 1970-01-01 00:00:00 +0000 |
1475 | +++ src/live_effects/lpe-jointype.cpp 2014-09-07 17:02:32 +0000 |
1476 | @@ -0,0 +1,184 @@ |
1477 | +/** \file |
1478 | + * LPE "Join Type" implementation |
1479 | + */ |
1480 | + /* Authors: |
1481 | + * |
1482 | + * Liam P White |
1483 | + * |
1484 | + * Copyright (C) 2014 Authors |
1485 | + * |
1486 | + * Released under GNU GPL v2, read the file 'COPYING' for more information |
1487 | + */ |
1488 | + |
1489 | +#include <math.h> |
1490 | + |
1491 | +#include "live_effects/parameter/enum.h" |
1492 | +#include "live_effects/pathoutlineprovider.h" |
1493 | + |
1494 | +#include "sp-shape.h" |
1495 | +#include "style.h" |
1496 | +#include "xml/repr.h" |
1497 | +#include "sp-paint-server.h" |
1498 | +#include "svg/svg-color.h" |
1499 | +#include "desktop-style.h" |
1500 | +#include "svg/css-ostringstream.h" |
1501 | +#include "display/curve.h" |
1502 | + |
1503 | +#include <2geom/path.h> |
1504 | +#include <2geom/svg-elliptical-arc.h> |
1505 | + |
1506 | +#include "lpe-jointype.h" |
1507 | + |
1508 | +namespace Inkscape { |
1509 | +namespace LivePathEffect { |
1510 | + |
1511 | +static const Util::EnumData<unsigned> JoinTypeData[] = { |
1512 | + {LINEJOIN_STRAIGHT, N_("Beveled"), "bevel"}, |
1513 | + {LINEJOIN_ROUND, N_("Rounded"), "round"}, |
1514 | + {LINEJOIN_POINTY, N_("Miter"), "miter"}, |
1515 | + {LINEJOIN_REFLECTED, N_("Reflected"), "extrapolated"}, |
1516 | + {LINEJOIN_EXTRAPOLATED, N_("Extrapolated arc"), "extrp_arc"} |
1517 | +}; |
1518 | + |
1519 | +static const Util::EnumData<unsigned> CapTypeData[] = { |
1520 | + {BUTT_STRAIGHT, N_("Butt"), "butt"}, |
1521 | + {BUTT_ROUND, N_("Rounded"), "round"}, |
1522 | + {BUTT_SQUARE, N_("Square"), "square"}, |
1523 | + {BUTT_POINTY, N_("Peak"), "peak"}, |
1524 | + {BUTT_LEANED, N_("Leaned"), "leaned"} |
1525 | +}; |
1526 | + |
1527 | +static const Util::EnumDataConverter<unsigned> CapTypeConverter(CapTypeData, sizeof(CapTypeData)/sizeof(*CapTypeData)); |
1528 | +static const Util::EnumDataConverter<unsigned> JoinTypeConverter(JoinTypeData, sizeof(JoinTypeData)/sizeof(*JoinTypeData)); |
1529 | + |
1530 | +LPEJoinType::LPEJoinType(LivePathEffectObject *lpeobject) : |
1531 | + Effect(lpeobject), |
1532 | + line_width(_("Line width"), _("Thickness of the stroke"), "line_width", &wr, this, 1.), |
1533 | + linecap_type(_("Line cap"), _("The end shape of the stroke"), "linecap_type", CapTypeConverter, &wr, this, butt_straight), |
1534 | + linejoin_type(_("Join:"), _("Determines the shape of the path's corners"), "linejoin_type", JoinTypeConverter, &wr, this, LINEJOIN_EXTRAPOLATED), |
1535 | + start_lean(_("Start path lean"), _("Start path lean"), "start_lean", &wr, this, 0.), |
1536 | + end_lean(_("End path lean"), _("End path lean"), "end_lean", &wr, this, 0.), |
1537 | + miter_limit(_("Miter limit:"), _("Maximum length of the miter join (in units of stroke width)"), "miter_limit", &wr, this, 100.), |
1538 | + attempt_force_join(_("Force miter"), _("Overrides the miter limit and forces a join."), "attempt_force_join", &wr, this, true) |
1539 | +{ |
1540 | + show_orig_path = true; |
1541 | + registerParameter( dynamic_cast<Parameter *>(&linecap_type) ); |
1542 | + registerParameter( dynamic_cast<Parameter *>(&line_width) ); |
1543 | + registerParameter( dynamic_cast<Parameter *>(&linejoin_type) ); |
1544 | + registerParameter( dynamic_cast<Parameter *>(&start_lean) ); |
1545 | + registerParameter( dynamic_cast<Parameter *>(&end_lean) ); |
1546 | + registerParameter( dynamic_cast<Parameter *>(&miter_limit) ); |
1547 | + registerParameter( dynamic_cast<Parameter *>(&attempt_force_join) ); |
1548 | + was_initialized = false; |
1549 | + start_lean.param_set_range(-1,1); |
1550 | + start_lean.param_set_increments(0.1, 0.1); |
1551 | + start_lean.param_set_digits(4); |
1552 | + end_lean.param_set_range(-1,1); |
1553 | + end_lean.param_set_increments(0.1, 0.1); |
1554 | + end_lean.param_set_digits(4); |
1555 | +} |
1556 | + |
1557 | +LPEJoinType::~LPEJoinType() |
1558 | +{ |
1559 | +} |
1560 | + |
1561 | +//from LPEPowerStroke -- sets fill if stroke color because we will |
1562 | +//be converting to a fill to make the new join. |
1563 | + |
1564 | +void LPEJoinType::doOnApply(SPLPEItem const* lpeitem) |
1565 | +{ |
1566 | + if (SP_IS_SHAPE(lpeitem)) { |
1567 | + SPLPEItem* item = const_cast<SPLPEItem*>(lpeitem); |
1568 | + double width = (lpeitem && lpeitem->style) ? lpeitem->style->stroke_width.computed : 1.; |
1569 | + |
1570 | + SPCSSAttr *css = sp_repr_css_attr_new (); |
1571 | + if (true) { |
1572 | + if (lpeitem->style->stroke.isPaintserver()) { |
1573 | + SPPaintServer * server = lpeitem->style->getStrokePaintServer(); |
1574 | + if (server) { |
1575 | + Glib::ustring str; |
1576 | + str += "url(#"; |
1577 | + str += server->getId(); |
1578 | + str += ")"; |
1579 | + sp_repr_css_set_property (css, "fill", str.c_str()); |
1580 | + } |
1581 | + } else if (lpeitem->style->stroke.isColor()) { |
1582 | + gchar c[64]; |
1583 | + sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value))); |
1584 | + sp_repr_css_set_property (css, "fill", c); |
1585 | + } else { |
1586 | + sp_repr_css_set_property (css, "fill", "none"); |
1587 | + } |
1588 | + } else { |
1589 | + sp_repr_css_unset_property (css, "fill"); |
1590 | + } |
1591 | + |
1592 | + sp_repr_css_set_property(css, "stroke", "none"); |
1593 | + |
1594 | + sp_desktop_apply_css_recursive(item, css, true); |
1595 | + sp_repr_css_attr_unref (css); |
1596 | + if (!was_initialized) |
1597 | + { |
1598 | + was_initialized = true; |
1599 | + line_width.param_set_value(width); |
1600 | + } |
1601 | + } else { |
1602 | + g_warning("LPE Join Type can only be applied to paths (not groups)."); |
1603 | + } |
1604 | +} |
1605 | + |
1606 | +//from LPEPowerStroke -- sets stroke color from existing fill color |
1607 | + |
1608 | +void LPEJoinType::doOnRemove(SPLPEItem const* lpeitem) |
1609 | +{ |
1610 | + |
1611 | + if (SP_IS_SHAPE(lpeitem)) { |
1612 | + SPLPEItem *item = const_cast<SPLPEItem*>(lpeitem); |
1613 | + |
1614 | + SPCSSAttr *css = sp_repr_css_attr_new (); |
1615 | + if (true) { |
1616 | + if (lpeitem->style->fill.isPaintserver()) { |
1617 | + SPPaintServer * server = lpeitem->style->getFillPaintServer(); |
1618 | + if (server) { |
1619 | + Glib::ustring str; |
1620 | + str += "url(#"; |
1621 | + str += server->getId(); |
1622 | + str += ")"; |
1623 | + sp_repr_css_set_property (css, "stroke", str.c_str()); |
1624 | + } |
1625 | + } else if (lpeitem->style->fill.isColor()) { |
1626 | + gchar c[64]; |
1627 | + sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value))); |
1628 | + sp_repr_css_set_property (css, "stroke", c); |
1629 | + } else { |
1630 | + sp_repr_css_set_property (css, "stroke", "none"); |
1631 | + } |
1632 | + } else { |
1633 | + sp_repr_css_unset_property (css, "stroke"); |
1634 | + } |
1635 | + |
1636 | + Inkscape::CSSOStringStream os; |
1637 | + os << fabs(line_width); |
1638 | + sp_repr_css_set_property (css, "stroke-width", os.str().c_str()); |
1639 | + |
1640 | + sp_repr_css_set_property(css, "fill", "none"); |
1641 | + |
1642 | + sp_desktop_apply_css_recursive(item, css, true); |
1643 | + sp_repr_css_attr_unref (css); |
1644 | + item->updateRepr(); |
1645 | + } |
1646 | +} |
1647 | + |
1648 | +//NOTE: I originally had all the outliner functions defined in here, but they were actually useful |
1649 | +//enough for other LPEs so I moved them all into pathoutlineprovider.cpp. The code here is just a |
1650 | +//wrapper around it. |
1651 | +std::vector<Geom::Path> LPEJoinType::doEffect_path(std::vector<Geom::Path> const & path_in) |
1652 | +{ |
1653 | + return Outline::PathVectorOutline(path_in, line_width, static_cast<ButtTypeMod>(linecap_type.get_value()), |
1654 | + static_cast<LineJoinType>(linejoin_type.get_value()), |
1655 | + (attempt_force_join ? std::numeric_limits<double>::max() : miter_limit), |
1656 | + start_lean/2 ,end_lean/2); |
1657 | +} |
1658 | + |
1659 | +} //namespace LivePathEffect |
1660 | +} //namespace Inkscape |
1661 | |
1662 | === added file 'src/live_effects/lpe-jointype.h' |
1663 | --- src/live_effects/lpe-jointype.h 1970-01-01 00:00:00 +0000 |
1664 | +++ src/live_effects/lpe-jointype.h 2014-09-07 17:02:32 +0000 |
1665 | @@ -0,0 +1,45 @@ |
1666 | +/* Authors: |
1667 | + * Liam P White |
1668 | + * |
1669 | + * Copyright (C) 2014 Authors |
1670 | + * |
1671 | + * Released under GNU GPL v2, read the file COPYING for more information |
1672 | + */ |
1673 | +#ifndef INKSCAPE_LPE_JOINTYPE_H |
1674 | +#define INKSCAPE_LPE_JOINTYPE_H |
1675 | + |
1676 | +#include "live_effects/effect.h" |
1677 | +#include "live_effects/parameter/parameter.h" |
1678 | +#include "live_effects/parameter/point.h" |
1679 | +#include "live_effects/parameter/enum.h" |
1680 | + |
1681 | +namespace Inkscape { |
1682 | +namespace LivePathEffect { |
1683 | + |
1684 | +class LPEJoinType : public Effect { |
1685 | +public: |
1686 | + LPEJoinType(LivePathEffectObject *lpeobject); |
1687 | + virtual ~LPEJoinType(); |
1688 | + |
1689 | + virtual void doOnApply(SPLPEItem const* lpeitem); |
1690 | + virtual void doOnRemove(SPLPEItem const* lpeitem); |
1691 | + virtual std::vector <Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); |
1692 | + |
1693 | +private: |
1694 | + LPEJoinType(const LPEJoinType&); |
1695 | + LPEJoinType& operator=(const LPEJoinType&); |
1696 | + |
1697 | + ScalarParam line_width; |
1698 | + EnumParam<unsigned> linecap_type; |
1699 | + EnumParam<unsigned> linejoin_type; |
1700 | + ScalarParam start_lean; |
1701 | + ScalarParam end_lean; |
1702 | + ScalarParam miter_limit; |
1703 | + BoolParam attempt_force_join; |
1704 | + bool was_initialized; |
1705 | +}; |
1706 | + |
1707 | +} //namespace LivePathEffect |
1708 | +} //namespace Inkscape |
1709 | + |
1710 | +#endif |
1711 | |
1712 | === modified file 'src/live_effects/lpe-knot.cpp' |
1713 | --- src/live_effects/lpe-knot.cpp 2014-08-31 18:17:26 +0000 |
1714 | +++ src/live_effects/lpe-knot.cpp 2014-09-07 17:02:32 +0000 |
1715 | @@ -537,6 +537,10 @@ |
1716 | { |
1717 | using namespace Geom; |
1718 | original_bbox(lpeitem); |
1719 | + |
1720 | + if (SP_IS_PATH(lpeitem)) { |
1721 | + supplied_path = SP_PATH(lpeitem)->getCurve()->get_pathvector(); |
1722 | + } |
1723 | |
1724 | gpaths.clear(); |
1725 | gstroke_widths.clear(); |
1726 | |
1727 | === modified file 'src/live_effects/lpe-knot.h' |
1728 | --- src/live_effects/lpe-knot.h 2014-06-16 21:34:16 +0000 |
1729 | +++ src/live_effects/lpe-knot.h 2014-09-07 17:02:32 +0000 |
1730 | @@ -64,7 +64,8 @@ |
1731 | void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); |
1732 | |
1733 | protected: |
1734 | - virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec); |
1735 | + virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec); |
1736 | + std::vector<Geom::Path> supplied_path; //for knotholder business |
1737 | |
1738 | private: |
1739 | void updateSwitcher(); |
1740 | |
1741 | === modified file 'src/live_effects/lpe-powerstroke-interpolators.h' |
1742 | --- src/live_effects/lpe-powerstroke-interpolators.h 2014-08-23 17:14:02 +0000 |
1743 | +++ src/live_effects/lpe-powerstroke-interpolators.h 2014-09-07 17:02:32 +0000 |
1744 | @@ -28,6 +28,7 @@ |
1745 | INTERP_CUBICBEZIER, |
1746 | INTERP_CUBICBEZIER_JOHAN, |
1747 | INTERP_SPIRO, |
1748 | + INTERP_CUBICBEZIER_SMOOTH, |
1749 | INTERP_CENTRIPETAL_CATMULLROM |
1750 | }; |
1751 | |
1752 | @@ -134,6 +135,43 @@ |
1753 | CubicBezierJohan& operator=(const CubicBezierJohan&); |
1754 | }; |
1755 | |
1756 | +/// @todo invent name for this class |
1757 | +class CubicBezierSmooth : public Interpolator { |
1758 | +public: |
1759 | + CubicBezierSmooth(double beta = 0.2) { |
1760 | + _beta = beta; |
1761 | + }; |
1762 | + virtual ~CubicBezierSmooth() {}; |
1763 | + |
1764 | + virtual Path interpolateToPath(std::vector<Point> const &points) const { |
1765 | + Path fit; |
1766 | + fit.start(points.at(0)); |
1767 | + unsigned int num_points = points.size(); |
1768 | + for (unsigned int i = 1; i < num_points; ++i) { |
1769 | + Point p0 = points.at(i-1); |
1770 | + Point p1 = points.at(i); |
1771 | + Point dx = Point(p1[X] - p0[X], 0); |
1772 | + if (i == 1) { |
1773 | + fit.appendNew<CubicBezier>(p0, p1-0.75*dx, p1); |
1774 | + } else if (i == points.size() - 1) { |
1775 | + fit.appendNew<CubicBezier>(p0+0.75*dx, p1, p1); |
1776 | + } else { |
1777 | + fit.appendNew<CubicBezier>(p0+_beta*dx, p1-_beta*dx, p1); |
1778 | + } |
1779 | + } |
1780 | + return fit; |
1781 | + }; |
1782 | + |
1783 | + void setBeta(double beta) { |
1784 | + _beta = beta; |
1785 | + } |
1786 | + |
1787 | + double _beta; |
1788 | + |
1789 | +private: |
1790 | + CubicBezierSmooth(const CubicBezierSmooth&); |
1791 | + CubicBezierSmooth& operator=(const CubicBezierSmooth&); |
1792 | +}; |
1793 | |
1794 | class SpiroInterpolator : public Interpolator { |
1795 | public: |
1796 | @@ -261,6 +299,8 @@ |
1797 | return new Geom::Interpolate::CubicBezierJohan(); |
1798 | case INTERP_SPIRO: |
1799 | return new Geom::Interpolate::SpiroInterpolator(); |
1800 | + case INTERP_CUBICBEZIER_SMOOTH: |
1801 | + return new Geom::Interpolate::CubicBezierSmooth(); |
1802 | case INTERP_CENTRIPETAL_CATMULLROM: |
1803 | return new Geom::Interpolate::CentripetalCatmullRomInterpolator(); |
1804 | default: |
1805 | |
1806 | === modified file 'src/live_effects/lpe-powerstroke.cpp' |
1807 | --- src/live_effects/lpe-powerstroke.cpp 2014-08-23 16:39:36 +0000 |
1808 | +++ src/live_effects/lpe-powerstroke.cpp 2014-09-07 17:02:32 +0000 |
1809 | @@ -15,6 +15,11 @@ |
1810 | |
1811 | #include "sp-shape.h" |
1812 | #include "style.h" |
1813 | +#include "xml/repr.h" |
1814 | +#include "sp-paint-server.h" |
1815 | +#include "svg/svg-color.h" |
1816 | +#include "desktop-style.h" |
1817 | +#include "svg/css-ostringstream.h" |
1818 | #include "display/curve.h" |
1819 | |
1820 | #include <2geom/path.h> |
1821 | @@ -185,6 +190,7 @@ |
1822 | namespace LivePathEffect { |
1823 | |
1824 | static const Util::EnumData<unsigned> InterpolatorTypeData[] = { |
1825 | + {Geom::Interpolate::INTERP_CUBICBEZIER_SMOOTH, N_("CubicBezierSmooth"), "CubicBezierSmooth"}, |
1826 | {Geom::Interpolate::INTERP_LINEAR , N_("Linear"), "Linear"}, |
1827 | {Geom::Interpolate::INTERP_CUBICBEZIER , N_("CubicBezierFit"), "CubicBezierFit"}, |
1828 | {Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN , N_("CubicBezierJohan"), "CubicBezierJohan"}, |
1829 | @@ -223,9 +229,7 @@ |
1830 | {LINEJOIN_EXTRP_MITER, N_("Extrapolated"), "extrapolated"}, |
1831 | {LINEJOIN_MITER, N_("Miter"), "miter"}, |
1832 | {LINEJOIN_SPIRO, N_("Spiro"), "spiro"}, |
1833 | -#ifdef LPE_ENABLE_TEST_EFFECTS |
1834 | {LINEJOIN_EXTRP_MITER_ARC, N_("Extrapolated arc"), "extrp_arc"}, |
1835 | -#endif |
1836 | }; |
1837 | static const Util::EnumDataConverter<unsigned> LineJoinTypeConverter(LineJoinTypeData, sizeof(LineJoinTypeData)/sizeof(*LineJoinTypeData)); |
1838 | |
1839 | @@ -233,12 +237,12 @@ |
1840 | Effect(lpeobject), |
1841 | offset_points(_("Offset points"), _("Offset points"), "offset_points", &wr, this), |
1842 | sort_points(_("Sort points"), _("Sort offset points according to their time value along the curve"), "sort_points", &wr, this, true), |
1843 | - interpolator_type(_("Interpolator type:"), _("Determines which kind of interpolator will be used to interpolate between stroke width along the path"), "interpolator_type", InterpolatorTypeConverter, &wr, this, Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN), |
1844 | + interpolator_type(_("Interpolator type:"), _("Determines which kind of interpolator will be used to interpolate between stroke width along the path"), "interpolator_type", InterpolatorTypeConverter, &wr, this, Geom::Interpolate::INTERP_CUBICBEZIER), |
1845 | interpolator_beta(_("Smoothness:"), _("Sets the smoothness for the CubicBezierJohan interpolator; 0 = linear interpolation, 1 = smooth"), "interpolator_beta", &wr, this, 0.2), |
1846 | - start_linecap_type(_("Start cap:"), _("Determines the shape of the path's start"), "start_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ROUND), |
1847 | - linejoin_type(_("Join:"), _("Determines the shape of the path's corners"), "linejoin_type", LineJoinTypeConverter, &wr, this, LINEJOIN_ROUND), |
1848 | + start_linecap_type(_("Start cap:"), _("Determines the shape of the path's start"), "start_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_BUTT), |
1849 | + linejoin_type(_("Join:"), _("Determines the shape of the path's corners"), "linejoin_type", LineJoinTypeConverter, &wr, this, LINEJOIN_EXTRP_MITER_ARC), |
1850 | miter_limit(_("Miter limit:"), _("Maximum length of the miter (in units of stroke width)"), "miter_limit", &wr, this, 4.), |
1851 | - end_linecap_type(_("End cap:"), _("Determines the shape of the path's end"), "end_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ROUND) |
1852 | + end_linecap_type(_("End cap:"), _("Determines the shape of the path's end"), "end_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_BUTT) |
1853 | { |
1854 | show_orig_path = true; |
1855 | |
1856 | @@ -267,20 +271,52 @@ |
1857 | LPEPowerStroke::doOnApply(SPLPEItem const* lpeitem) |
1858 | { |
1859 | if (SP_IS_SHAPE(lpeitem)) { |
1860 | + SPLPEItem* item = const_cast<SPLPEItem*>(lpeitem); |
1861 | std::vector<Geom::Point> points; |
1862 | Geom::PathVector const &pathv = SP_SHAPE(lpeitem)->_curve->get_pathvector(); |
1863 | - double width = (lpeitem && lpeitem->style) ? lpeitem->style->stroke_width.computed : 1.; |
1864 | + double width = (lpeitem && lpeitem->style) ? lpeitem->style->stroke_width.computed / 2 : 1.; |
1865 | + |
1866 | + SPCSSAttr *css = sp_repr_css_attr_new (); |
1867 | + if (true) { |
1868 | + if (lpeitem->style->stroke.isPaintserver()) { |
1869 | + SPPaintServer * server = lpeitem->style->getStrokePaintServer(); |
1870 | + if (server) { |
1871 | + Glib::ustring str; |
1872 | + str += "url(#"; |
1873 | + str += server->getId(); |
1874 | + str += ")"; |
1875 | + sp_repr_css_set_property (css, "fill", str.c_str()); |
1876 | + } |
1877 | + } else if (lpeitem->style->stroke.isColor()) { |
1878 | + gchar c[64]; |
1879 | + sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value))); |
1880 | + sp_repr_css_set_property (css, "fill", c); |
1881 | + } else { |
1882 | + sp_repr_css_set_property (css, "fill", "none"); |
1883 | + } |
1884 | + } else { |
1885 | + sp_repr_css_unset_property (css, "fill"); |
1886 | + } |
1887 | + |
1888 | + sp_repr_css_set_property(css, "stroke", "none"); |
1889 | + |
1890 | + sp_desktop_apply_css_recursive(item, css, true); |
1891 | + sp_repr_css_attr_unref (css); |
1892 | + |
1893 | + item->updateRepr(); |
1894 | if (pathv.empty()) { |
1895 | - points.push_back( Geom::Point(0.,width) ); |
1896 | + points.push_back( Geom::Point(0.2,width) ); |
1897 | points.push_back( Geom::Point(0.5,width) ); |
1898 | - points.push_back( Geom::Point(1.,width) ); |
1899 | + points.push_back( Geom::Point(0.8,width) ); |
1900 | } else { |
1901 | Geom::Path const &path = pathv.front(); |
1902 | Geom::Path::size_type const size = path.size_default(); |
1903 | - points.push_back( Geom::Point(0.,width) ); |
1904 | + if (!path.closed()) { |
1905 | + points.push_back( Geom::Point(0.2,width) ); |
1906 | + } |
1907 | points.push_back( Geom::Point(0.5*size,width) ); |
1908 | if (!path.closed()) { |
1909 | - points.push_back( Geom::Point(size,width) ); |
1910 | + points.push_back( Geom::Point(size - 0.2,width) ); |
1911 | } |
1912 | } |
1913 | offset_points.param_set_and_write_new_value(points); |
1914 | @@ -289,6 +325,45 @@ |
1915 | } |
1916 | } |
1917 | |
1918 | +void LPEPowerStroke::doOnRemove(SPLPEItem const* lpeitem) |
1919 | +{ |
1920 | + if (SP_IS_SHAPE(lpeitem)) { |
1921 | + SPLPEItem *item = const_cast<SPLPEItem*>(lpeitem); |
1922 | + SPCSSAttr *css = sp_repr_css_attr_new (); |
1923 | + if (true) { |
1924 | + if (lpeitem->style->fill.isPaintserver()) { |
1925 | + SPPaintServer * server = lpeitem->style->getFillPaintServer(); |
1926 | + if (server) { |
1927 | + Glib::ustring str; |
1928 | + str += "url(#"; |
1929 | + str += server->getId(); |
1930 | + str += ")"; |
1931 | + sp_repr_css_set_property (css, "stroke", str.c_str()); |
1932 | + } |
1933 | + } else if (lpeitem->style->fill.isColor()) { |
1934 | + gchar c[64]; |
1935 | + sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value))); |
1936 | + sp_repr_css_set_property (css, "stroke", c); |
1937 | + } else { |
1938 | + sp_repr_css_set_property (css, "stroke", "none"); |
1939 | + } |
1940 | + } else { |
1941 | + sp_repr_css_unset_property (css, "stroke"); |
1942 | + } |
1943 | + |
1944 | + Inkscape::CSSOStringStream os; |
1945 | + os << offset_points.median_width() * 2; |
1946 | + sp_repr_css_set_property (css, "stroke-width", os.str().c_str()); |
1947 | + |
1948 | + sp_repr_css_set_property(css, "fill", "none"); |
1949 | + |
1950 | + sp_desktop_apply_css_recursive(item, css, true); |
1951 | + sp_repr_css_attr_unref (css); |
1952 | + |
1953 | + item->updateRepr(); |
1954 | + } |
1955 | +} |
1956 | + |
1957 | void |
1958 | LPEPowerStroke::adjustForNewPath(std::vector<Geom::Path> const & path_in) |
1959 | { |
1960 | @@ -588,6 +663,9 @@ |
1961 | if (Geom::Interpolate::CubicBezierJohan *johan = dynamic_cast<Geom::Interpolate::CubicBezierJohan*>(interpolator)) { |
1962 | johan->setBeta(interpolator_beta); |
1963 | } |
1964 | + if (Geom::Interpolate::CubicBezierSmooth *smooth = dynamic_cast<Geom::Interpolate::CubicBezierSmooth*>(interpolator)) { |
1965 | + smooth->setBeta(interpolator_beta); |
1966 | + } |
1967 | Geom::Path strokepath = interpolator->interpolateToPath(ts); |
1968 | delete interpolator; |
1969 | |
1970 | |
1971 | === modified file 'src/live_effects/lpe-powerstroke.h' |
1972 | --- src/live_effects/lpe-powerstroke.h 2014-03-27 01:33:44 +0000 |
1973 | +++ src/live_effects/lpe-powerstroke.h 2014-09-07 17:02:32 +0000 |
1974 | @@ -25,9 +25,11 @@ |
1975 | LPEPowerStroke(LivePathEffectObject *lpeobject); |
1976 | virtual ~LPEPowerStroke(); |
1977 | |
1978 | + |
1979 | virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); |
1980 | |
1981 | virtual void doOnApply(SPLPEItem const* lpeitem); |
1982 | + virtual void doOnRemove(SPLPEItem const* lpeitem); |
1983 | |
1984 | // methods called by path-manipulator upon edits |
1985 | void adjustForNewPath(std::vector<Geom::Path> const & path_in); |
1986 | |
1987 | === modified file 'src/live_effects/lpe-tangent_to_curve.cpp' |
1988 | --- src/live_effects/lpe-tangent_to_curve.cpp 2014-03-27 01:33:44 +0000 |
1989 | +++ src/live_effects/lpe-tangent_to_curve.cpp 2014-09-07 17:02:32 +0000 |
1990 | @@ -16,8 +16,6 @@ |
1991 | #include <glibmm/i18n.h> |
1992 | |
1993 | #include "live_effects/lpe-tangent_to_curve.h" |
1994 | -// FIXME: The following are only needed to convert the path's SPCurve* to pwd2. |
1995 | -// There must be a more convenient way to achieve this. |
1996 | #include "sp-path.h" |
1997 | #include "display/curve.h" |
1998 | |
1999 | @@ -108,13 +106,13 @@ |
2000 | { |
2001 | KnotHolderEntity *e = new TtC::KnotHolderEntityLeftEnd(this); |
2002 | e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, |
2003 | - _("Adjust the \"left\" end of the tangent") ); |
2004 | + _("Adjust the <b>left</b> end of the tangent") ); |
2005 | knotholder->add(e); |
2006 | } |
2007 | { |
2008 | KnotHolderEntity *e = new TtC::KnotHolderEntityRightEnd(this); |
2009 | e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, |
2010 | - _("Adjust the \"right\" end of the tangent") ); |
2011 | + _("Adjust the <b>right</b> end of the tangent") ); |
2012 | knotholder->add(e); |
2013 | } |
2014 | }; |
2015 | @@ -130,14 +128,13 @@ |
2016 | |
2017 | Geom::Point const s = snap_knot_position(p, state); |
2018 | |
2019 | - // FIXME: There must be a better way of converting the path's SPCurve* to pwd2. |
2020 | - SPCurve *curve = SP_PATH(item)->get_curve_for_edit(); |
2021 | - Geom::PathVector pathv = curve->get_pathvector(); |
2022 | - Piecewise<D2<SBasis> > pwd2; |
2023 | - for (unsigned int i=0; i < pathv.size(); i++) { |
2024 | - pwd2.concat(pathv[i].toPwSb()); |
2025 | + if ( !SP_IS_SHAPE(lpe->sp_lpe_item) ) { |
2026 | + //lpe->t_attach.param_set_value(0); |
2027 | + g_warning("LPEItem is not a path! %s:%d\n", __FILE__, __LINE__); |
2028 | + return; |
2029 | } |
2030 | - |
2031 | + Piecewise<D2<SBasis> > pwd2 = paths_to_pw( lpe->pathvector_before_effect ); |
2032 | + |
2033 | double t0 = nearest_point(s, pwd2); |
2034 | lpe->t_attach.param_set_value(t0); |
2035 | |
2036 | |
2037 | === modified file 'src/live_effects/lpe-tangent_to_curve.h' |
2038 | --- src/live_effects/lpe-tangent_to_curve.h 2012-04-07 14:09:58 +0000 |
2039 | +++ src/live_effects/lpe-tangent_to_curve.h 2014-09-07 17:02:32 +0000 |
2040 | @@ -34,7 +34,6 @@ |
2041 | public: |
2042 | LPETangentToCurve(LivePathEffectObject *lpeobject); |
2043 | virtual ~LPETangentToCurve(); |
2044 | - |
2045 | virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > |
2046 | doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in); |
2047 | |
2048 | |
2049 | === added file 'src/live_effects/lpe-taperstroke.cpp' |
2050 | --- src/live_effects/lpe-taperstroke.cpp 1970-01-01 00:00:00 +0000 |
2051 | +++ src/live_effects/lpe-taperstroke.cpp 2014-09-07 17:02:32 +0000 |
2052 | @@ -0,0 +1,630 @@ |
2053 | +/** |
2054 | + * @file |
2055 | + * Taper Stroke path effect, provided as an alternative to Power Strokes |
2056 | + * for otherwise constant-width paths. |
2057 | + * |
2058 | + * Authors: |
2059 | + * Liam P White <inkscapebrony@gmail.com> |
2060 | + * |
2061 | + * Copyright (C) 2014 Authors |
2062 | + * |
2063 | + * Released under GNU GPL, read the file 'COPYING' for more information |
2064 | + */ |
2065 | + |
2066 | +#include "live_effects/lpe-taperstroke.h" |
2067 | + |
2068 | +#include <2geom/path.h> |
2069 | +#include <2geom/shape.h> |
2070 | +#include <2geom/path.h> |
2071 | +#include <2geom/circle.h> |
2072 | +#include <2geom/sbasis-to-bezier.h> |
2073 | +#include "pathoutlineprovider.h" |
2074 | +#include "display/curve.h" |
2075 | +#include "sp-shape.h" |
2076 | +#include "style.h" |
2077 | +#include "xml/repr.h" |
2078 | +#include "sp-paint-server.h" |
2079 | +#include "svg/svg-color.h" |
2080 | +#include "desktop-style.h" |
2081 | +#include "svg/css-ostringstream.h" |
2082 | +#include "svg/svg.h" |
2083 | + |
2084 | +//#include <glibmm/i18n.h> |
2085 | + |
2086 | +#include "knot-holder-entity.h" |
2087 | +#include "knotholder.h" |
2088 | + |
2089 | +template<typename T> |
2090 | +inline bool withinRange(T value, T low, T high) { |
2091 | + return (value > low && value < high); |
2092 | +} |
2093 | + |
2094 | +namespace Inkscape { |
2095 | +namespace LivePathEffect { |
2096 | + |
2097 | +namespace TpS { |
2098 | + class KnotHolderEntityAttachBegin : public LPEKnotHolderEntity { |
2099 | + public: |
2100 | + KnotHolderEntityAttachBegin(LPETaperStroke * effect) : LPEKnotHolderEntity(effect) {} |
2101 | + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); |
2102 | + virtual Geom::Point knot_get() const; |
2103 | + }; |
2104 | + |
2105 | + class KnotHolderEntityAttachEnd : public LPEKnotHolderEntity { |
2106 | + public: |
2107 | + KnotHolderEntityAttachEnd(LPETaperStroke * effect) : LPEKnotHolderEntity(effect) {} |
2108 | + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); |
2109 | + virtual Geom::Point knot_get() const; |
2110 | + }; |
2111 | +} // TpS |
2112 | + |
2113 | +static const Util::EnumData<unsigned> JoinType[] = { |
2114 | + {LINEJOIN_STRAIGHT, N_("Beveled"), "bevel"}, |
2115 | + {LINEJOIN_ROUND, N_("Rounded"), "round"}, |
2116 | + {LINEJOIN_REFLECTED, N_("Reflected"), "reflected"}, |
2117 | + {LINEJOIN_POINTY, N_("Miter"), "miter"}, |
2118 | + {LINEJOIN_EXTRAPOLATED, N_("Extrapolated"), "extrapolated"} |
2119 | +}; |
2120 | + |
2121 | +static const Util::EnumDataConverter<unsigned> JoinTypeConverter(JoinType, sizeof (JoinType)/sizeof(*JoinType)); |
2122 | + |
2123 | +LPETaperStroke::LPETaperStroke(LivePathEffectObject *lpeobject) : |
2124 | + Effect(lpeobject), |
2125 | + line_width(_("Stroke width"), _("The (non-tapered) width of the path"), "stroke_width", &wr, this, 1.), |
2126 | + attach_start(_("Start offset"), _("Taper distance from path start"), "attach_start", &wr, this, 0.2), |
2127 | + attach_end(_("End offset"), _("The ending position of the taper"), "end_offset", &wr, this, 0.2), |
2128 | + smoothing(_("Taper smoothing"), _("Amount of smoothing to apply to the tapers"), "smoothing", &wr, this, 0.5), |
2129 | + join_type(_("Join type"), _("Join type for non-smooth nodes"), "jointype", JoinTypeConverter, &wr, this, LINEJOIN_EXTRAPOLATED), |
2130 | + miter_limit(_("Miter limit"), _("Limit for miter joins"), "miter_limit", &wr, this, 100.) |
2131 | +{ |
2132 | + show_orig_path = true; |
2133 | + _provides_knotholder_entities = true; |
2134 | + |
2135 | + attach_start.param_set_digits(3); |
2136 | + attach_end.param_set_digits(3); |
2137 | + |
2138 | + |
2139 | + registerParameter( dynamic_cast<Parameter *>(&line_width) ); |
2140 | + registerParameter( dynamic_cast<Parameter *>(&attach_start) ); |
2141 | + registerParameter( dynamic_cast<Parameter *>(&attach_end) ); |
2142 | + registerParameter( dynamic_cast<Parameter *>(&smoothing) ); |
2143 | + registerParameter( dynamic_cast<Parameter *>(&join_type) ); |
2144 | + registerParameter( dynamic_cast<Parameter *>(&miter_limit) ); |
2145 | +} |
2146 | + |
2147 | +LPETaperStroke::~LPETaperStroke() |
2148 | +{ |
2149 | + |
2150 | +} |
2151 | + |
2152 | +//from LPEPowerStroke -- sets fill if stroke color because we will |
2153 | +//be converting to a fill to make the new join. |
2154 | + |
2155 | +void LPETaperStroke::doOnApply(SPLPEItem const* lpeitem) |
2156 | +{ |
2157 | + if (SP_IS_SHAPE(lpeitem)) { |
2158 | + SPLPEItem* item = const_cast<SPLPEItem*>(lpeitem); |
2159 | + double width = (lpeitem && lpeitem->style) ? lpeitem->style->stroke_width.computed : 1.; |
2160 | + |
2161 | + SPCSSAttr *css = sp_repr_css_attr_new (); |
2162 | + if (true) { |
2163 | + if (lpeitem->style->stroke.isPaintserver()) { |
2164 | + SPPaintServer * server = lpeitem->style->getStrokePaintServer(); |
2165 | + if (server) { |
2166 | + Glib::ustring str; |
2167 | + str += "url(#"; |
2168 | + str += server->getId(); |
2169 | + str += ")"; |
2170 | + sp_repr_css_set_property (css, "fill", str.c_str()); |
2171 | + } |
2172 | + } else if (lpeitem->style->stroke.isColor()) { |
2173 | + gchar c[64]; |
2174 | + sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value))); |
2175 | + sp_repr_css_set_property (css, "fill", c); |
2176 | + } else { |
2177 | + sp_repr_css_set_property (css, "fill", "none"); |
2178 | + } |
2179 | + } else { |
2180 | + sp_repr_css_unset_property (css, "fill"); |
2181 | + } |
2182 | + |
2183 | + sp_repr_css_set_property(css, "stroke", "none"); |
2184 | + |
2185 | + sp_desktop_apply_css_recursive(item, css, true); |
2186 | + sp_repr_css_attr_unref (css); |
2187 | + |
2188 | + line_width.param_set_value(width); |
2189 | + } else { |
2190 | + g_warning("LPE Join Type can only be applied to paths (not groups)."); |
2191 | + } |
2192 | +} |
2193 | + |
2194 | +//from LPEPowerStroke -- sets stroke color from existing fill color |
2195 | + |
2196 | +void LPETaperStroke::doOnRemove(SPLPEItem const* lpeitem) |
2197 | +{ |
2198 | + |
2199 | + if (SP_IS_SHAPE(lpeitem)) { |
2200 | + SPLPEItem *item = const_cast<SPLPEItem*>(lpeitem); |
2201 | + |
2202 | + SPCSSAttr *css = sp_repr_css_attr_new (); |
2203 | + if (true) { |
2204 | + if (lpeitem->style->fill.isPaintserver()) { |
2205 | + SPPaintServer * server = lpeitem->style->getFillPaintServer(); |
2206 | + if (server) { |
2207 | + Glib::ustring str; |
2208 | + str += "url(#"; |
2209 | + str += server->getId(); |
2210 | + str += ")"; |
2211 | + sp_repr_css_set_property (css, "stroke", str.c_str()); |
2212 | + } |
2213 | + } else if (lpeitem->style->fill.isColor()) { |
2214 | + gchar c[64]; |
2215 | + sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value))); |
2216 | + sp_repr_css_set_property (css, "stroke", c); |
2217 | + } else { |
2218 | + sp_repr_css_set_property (css, "stroke", "none"); |
2219 | + } |
2220 | + } else { |
2221 | + sp_repr_css_unset_property (css, "stroke"); |
2222 | + } |
2223 | + |
2224 | + Inkscape::CSSOStringStream os; |
2225 | + os << fabs(line_width); |
2226 | + sp_repr_css_set_property (css, "stroke-width", os.str().c_str()); |
2227 | + |
2228 | + sp_repr_css_set_property(css, "fill", "none"); |
2229 | + |
2230 | + sp_desktop_apply_css_recursive(item, css, true); |
2231 | + sp_repr_css_attr_unref (css); |
2232 | + item->updateRepr(); |
2233 | + } |
2234 | +} |
2235 | + |
2236 | +//actual effect impl here |
2237 | + |
2238 | +Geom::Path return_at_first_cusp (Geom::Path const & path_in, double /*smooth_tolerance*/ = 0.05) |
2239 | +{ |
2240 | + return Geom::split_at_cusps(path_in)[0]; |
2241 | +} |
2242 | + |
2243 | +Geom::Piecewise<Geom::D2<Geom::SBasis> > stretch_along(Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in, Geom::Path pattern, double width); |
2244 | + |
2245 | +//references to pointers, because magic |
2246 | +void subdivideCurve(Geom::Curve * curve_in, Geom::Coord t, Geom::Curve *& val_first, Geom::Curve *& val_second); |
2247 | + |
2248 | +Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in) |
2249 | +{ |
2250 | + Geom::Path first_cusp = return_at_first_cusp(path_in[0]); |
2251 | + Geom::Path last_cusp = return_at_first_cusp(path_in[0].reverse()); |
2252 | + |
2253 | + bool zeroStart = false; |
2254 | + bool zeroEnd = false; |
2255 | + bool metInMiddle = false; |
2256 | + |
2257 | + //there is a pretty good chance that people will try to drag the knots |
2258 | + //on top of each other, so block it |
2259 | + |
2260 | + unsigned size = path_in[0].size(); |
2261 | + if (size == first_cusp.size()) { |
2262 | + //check to see if the knots were dragged over each other |
2263 | + //if so, reset the end offset, but still allow the start offset. |
2264 | + if ( attach_start >= (size - attach_end) ) { |
2265 | + attach_end.param_set_value( size - attach_start ); |
2266 | + metInMiddle = true; |
2267 | + } |
2268 | + } |
2269 | + |
2270 | + if (attach_start == size - attach_end) { |
2271 | + metInMiddle = true; |
2272 | + } |
2273 | + if (attach_end == size - attach_start) { |
2274 | + metInMiddle = true; |
2275 | + } |
2276 | + |
2277 | + //don't let it be integer |
2278 | + { |
2279 | + if (double(unsigned(attach_start)) == attach_start) { |
2280 | + attach_start.param_set_value(attach_start - 0.00001); |
2281 | + } |
2282 | + if (double(unsigned(attach_end)) == attach_end) { |
2283 | + attach_end.param_set_value(attach_end - 0.00001); |
2284 | + } |
2285 | + } |
2286 | + |
2287 | + unsigned allowed_start = first_cusp.size(); |
2288 | + unsigned allowed_end = last_cusp.size(); |
2289 | + |
2290 | + //don't let the knots be farther than they are allowed to be |
2291 | + { |
2292 | + if ((unsigned)attach_start >= allowed_start) { |
2293 | + attach_start.param_set_value((double)allowed_start - 0.00001); |
2294 | + } |
2295 | + if ((unsigned)attach_end >= allowed_end) { |
2296 | + attach_end.param_set_value((double)allowed_end - 0.00001); |
2297 | + } |
2298 | + } |
2299 | + |
2300 | + //don't let it be zero |
2301 | + if (attach_start < 0.0000001 || withinRange(double(attach_start), 0.00000001, 0.000001)) { |
2302 | + attach_start.param_set_value( 0.0000001 ); |
2303 | + zeroStart = true; |
2304 | + } |
2305 | + if (attach_end < 0.0000001 || withinRange(double(attach_end), 0.00000001, 0.000001)) { |
2306 | + attach_end.param_set_value( 0.0000001 ); |
2307 | + zeroEnd = true; |
2308 | + } |
2309 | + |
2310 | + //remember, Path::operator () means get point at time t |
2311 | + start_attach_point = first_cusp(attach_start); |
2312 | + end_attach_point = last_cusp(attach_end); |
2313 | + Geom::PathVector pathv_out; |
2314 | + |
2315 | + //the following function just splits it up into three pieces. |
2316 | + pathv_out = doEffect_simplePath(path_in); |
2317 | + |
2318 | + //now for the actual tapering. We use the stretch_along method to get this done. |
2319 | + |
2320 | + Geom::PathVector real_pathv; |
2321 | + Geom::Path real_path; |
2322 | + Geom::PathVector pat_vec; |
2323 | + Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2; |
2324 | + Geom::Path throwaway_path; |
2325 | + |
2326 | + if (!zeroStart) { |
2327 | + //Construct the pattern (pat_str stands for pattern string) (and yes, this is easier, trust me) |
2328 | + std::stringstream pat_str; |
2329 | + pat_str << "M 1,0 C " << 1 - (double)smoothing << ",0 0,0.5 0,0.5 0,0.5 " << 1 - (double)smoothing << ",1 1,1"; |
2330 | + |
2331 | + pat_vec = sp_svg_read_pathv(pat_str.str().c_str()); |
2332 | + pwd2.concat(stretch_along(pathv_out[0].toPwSb(), pat_vec[0], -fabs(line_width))); |
2333 | + throwaway_path = Geom::path_from_piecewise(pwd2, LPE_CONVERSION_TOLERANCE)[0]; |
2334 | + |
2335 | + real_path.append(throwaway_path); |
2336 | + } |
2337 | + |
2338 | + if (!metInMiddle) { |
2339 | + //append the outside outline of the path (with direction) |
2340 | + throwaway_path = Outline::PathOutsideOutline(pathv_out[1], |
2341 | + -fabs(line_width), static_cast<LineJoinType>(join_type.get_value()), miter_limit); |
2342 | + if (!zeroStart && real_path.size() >= 1 && throwaway_path.size() >= 1) { |
2343 | + if (Geom::distance(real_path.finalPoint(), throwaway_path.initialPoint()) > 0.0000001) { |
2344 | + real_path.appendNew<Geom::LineSegment>(throwaway_path.initialPoint()); |
2345 | + } else { |
2346 | + real_path.setFinal(throwaway_path.initialPoint()); |
2347 | + } |
2348 | + } |
2349 | + real_path.append(throwaway_path); |
2350 | + } |
2351 | + |
2352 | + if (!zeroEnd) { |
2353 | + //append the ending taper |
2354 | + std::stringstream pat_str_1; |
2355 | + pat_str_1 << "M 0,1 C " << (double)smoothing << ",1 1,0.5 1,0.5 1,0.5 " << double(smoothing) << ",0 0,0"; |
2356 | + pat_vec = sp_svg_read_pathv(pat_str_1.str().c_str()); |
2357 | + |
2358 | + pwd2 = Geom::Piecewise<Geom::D2<Geom::SBasis> > (); |
2359 | + pwd2.concat(stretch_along(pathv_out[2].toPwSb(), pat_vec[0], -fabs(line_width))); |
2360 | + |
2361 | + throwaway_path = Geom::path_from_piecewise(pwd2, LPE_CONVERSION_TOLERANCE)[0]; |
2362 | + if (Geom::distance(real_path.finalPoint(), throwaway_path.initialPoint()) > 0.0000001 && real_path.size() >= 1) { |
2363 | + real_path.appendNew<Geom::LineSegment>(throwaway_path.initialPoint()); |
2364 | + } else { |
2365 | + real_path.setFinal(throwaway_path.initialPoint()); |
2366 | + } |
2367 | + real_path.append(throwaway_path); |
2368 | + } |
2369 | + |
2370 | + if (!metInMiddle) { |
2371 | + //append the inside outline of the path (against direction) |
2372 | + throwaway_path = Outline::PathOutsideOutline(pathv_out[1].reverse(), |
2373 | + -fabs(line_width), static_cast<LineJoinType>(join_type.get_value()), miter_limit); |
2374 | + |
2375 | + if (Geom::distance(real_path.finalPoint(), throwaway_path.initialPoint()) > 0.0000001 && real_path.size() >= 1) { |
2376 | + real_path.appendNew<Geom::LineSegment>(throwaway_path.initialPoint()); |
2377 | + } else { |
2378 | + real_path.setFinal(throwaway_path.initialPoint()); |
2379 | + } |
2380 | + real_path.append(throwaway_path); |
2381 | + } |
2382 | + |
2383 | + if (Geom::distance(real_path.finalPoint(), real_path.initialPoint()) > 0.0000001) { |
2384 | + real_path.appendNew<Geom::LineSegment>(real_path.initialPoint()); |
2385 | + } else { |
2386 | + real_path.setFinal(real_path.initialPoint()); |
2387 | + } |
2388 | + real_path.close(); |
2389 | + |
2390 | + real_pathv.push_back(real_path); |
2391 | + |
2392 | + return real_pathv; |
2393 | +} |
2394 | + |
2395 | +//in all cases, this should return a PathVector with three elements. |
2396 | +Geom::PathVector LPETaperStroke::doEffect_simplePath(Geom::PathVector const & path_in) |
2397 | +{ |
2398 | + unsigned size = path_in[0].size(); |
2399 | + |
2400 | + //do subdivision and get out |
2401 | + unsigned loc = (unsigned)attach_start; |
2402 | + Geom::Curve * curve_start = path_in[0] [loc].duplicate(); |
2403 | + |
2404 | + std::vector<Geom::Path> pathv_out; |
2405 | + Geom::Path path_out = Geom::Path(); |
2406 | + |
2407 | + Geom::Path trimmed_start = Geom::Path(); |
2408 | + Geom::Path trimmed_end = Geom::Path(); |
2409 | + |
2410 | + for (unsigned i = 0; i < loc; i++) { |
2411 | + trimmed_start.append(path_in[0] [i]); |
2412 | + } |
2413 | + |
2414 | + Geom::Curve * temp; |
2415 | + subdivideCurve(curve_start, attach_start - loc, temp, curve_start); |
2416 | + trimmed_start.append(*temp); |
2417 | + if (temp) delete temp; temp = 0; |
2418 | + |
2419 | + //special case: path is one segment long |
2420 | + //special case: what if the two knots occupy the same segment? |
2421 | + if ((size == 1) || ( size - unsigned(attach_end) - 1 == loc )) { |
2422 | + Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_start); |
2423 | + |
2424 | + //it is just a dumb segment |
2425 | + //we have to do some shifting here because the value changed when we reduced the length |
2426 | + //of the previous segment. |
2427 | + |
2428 | + subdivideCurve(curve_start, t, curve_start, temp); |
2429 | + trimmed_end.append(*temp); |
2430 | + if (temp) delete temp; temp = 0; |
2431 | + |
2432 | + for (unsigned j = (size - attach_end) + 1; j < size; j++) { |
2433 | + trimmed_end.append(path_in[0] [j]); |
2434 | + } |
2435 | + |
2436 | + path_out.append(*curve_start); |
2437 | + pathv_out.push_back(trimmed_start); |
2438 | + pathv_out.push_back(path_out); |
2439 | + pathv_out.push_back(trimmed_end); |
2440 | + return pathv_out; |
2441 | + } |
2442 | + |
2443 | + pathv_out.push_back(trimmed_start); |
2444 | + |
2445 | + //append almost all of the rest of the path, ignore the curves that the knot is past (we'll get to it in a minute) |
2446 | + path_out.append(*curve_start); |
2447 | + |
2448 | + for (unsigned k = loc + 1; k < (size - unsigned(attach_end)) - 1; k++) { |
2449 | + path_out.append(path_in[0] [k]); |
2450 | + } |
2451 | + |
2452 | + //deal with the last segment in a very similar fashion to the first |
2453 | + loc = size - attach_end; |
2454 | + |
2455 | + Geom::Curve * curve_end = path_in[0] [loc].duplicate(); |
2456 | + |
2457 | + Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_end); |
2458 | + |
2459 | + subdivideCurve(curve_end, t, curve_end, temp); |
2460 | + trimmed_end.append(*temp); |
2461 | + if (temp) delete temp; temp = 0; |
2462 | + |
2463 | + for (unsigned j = (size - attach_end) + 1; j < size; j++) { |
2464 | + trimmed_end.append(path_in[0] [j]); |
2465 | + } |
2466 | + |
2467 | + path_out.append(*curve_end); |
2468 | + pathv_out.push_back(path_out); |
2469 | + |
2470 | + pathv_out.push_back(trimmed_end); |
2471 | + |
2472 | + if (curve_end) delete curve_end; |
2473 | + if (curve_start) delete curve_start; |
2474 | + return pathv_out; |
2475 | +} |
2476 | + |
2477 | + |
2478 | +//most of the below code is verbatim from Pattern Along Path. However, it needed a little |
2479 | +//tweaking to get it to work right in this case. |
2480 | +Geom::Piecewise<Geom::D2<Geom::SBasis> > stretch_along(Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in, Geom::Path pattern, double prop_scale) |
2481 | +{ |
2482 | + using namespace Geom; |
2483 | + |
2484 | + // Don't allow empty path parameter: |
2485 | + if ( pattern.empty() ) { |
2486 | + return pwd2_in; |
2487 | + } |
2488 | + |
2489 | + /* Much credit should go to jfb and mgsloan of lib2geom development for the code below! */ |
2490 | + Piecewise<D2<SBasis> > output; |
2491 | + std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > pre_output; |
2492 | + |
2493 | + D2<Piecewise<SBasis> > patternd2 = make_cuts_independent(pattern.toPwSb()); |
2494 | + Piecewise<SBasis> x0 = Piecewise<SBasis>(patternd2[0]); |
2495 | + Piecewise<SBasis> y0 = Piecewise<SBasis>(patternd2[1]); |
2496 | + OptInterval pattBndsX = bounds_exact(x0); |
2497 | + OptInterval pattBndsY = bounds_exact(y0); |
2498 | + if (pattBndsX && pattBndsY) { |
2499 | + x0 -= pattBndsX->min(); |
2500 | + y0 -= pattBndsY->middle(); |
2501 | + |
2502 | + double xspace = 0; |
2503 | + double noffset = 0; |
2504 | + double toffset = 0; |
2505 | + //Prevent more than 90% overlap... |
2506 | + if (xspace < -pattBndsX->extent()*.9) { |
2507 | + xspace = -pattBndsX->extent()*.9; |
2508 | + } |
2509 | + |
2510 | + y0+=noffset; |
2511 | + |
2512 | + std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > paths_in; |
2513 | + paths_in = split_at_discontinuities(pwd2_in); |
2514 | + |
2515 | + for (unsigned idx = 0; idx < paths_in.size(); idx++) { |
2516 | + Geom::Piecewise<Geom::D2<Geom::SBasis> > path_i = paths_in[idx]; |
2517 | + Piecewise<SBasis> x = x0; |
2518 | + Piecewise<SBasis> y = y0; |
2519 | + Piecewise<D2<SBasis> > uskeleton = arc_length_parametrization(path_i,2,.1); |
2520 | + uskeleton = remove_short_cuts(uskeleton,.01); |
2521 | + Piecewise<D2<SBasis> > n = rot90(derivative(uskeleton)); |
2522 | + n = force_continuity(remove_short_cuts(n,.1)); |
2523 | + |
2524 | + int nbCopies = 0; |
2525 | + double scaling = 1; |
2526 | + nbCopies = 1; |
2527 | + scaling = (uskeleton.domain().extent() - toffset)/pattBndsX->extent(); |
2528 | + |
2529 | + double pattWidth = pattBndsX->extent() * scaling; |
2530 | + |
2531 | + if (scaling != 1.0) { |
2532 | + x*=scaling; |
2533 | + } |
2534 | + if ( false ) { |
2535 | + y*=(scaling*prop_scale); |
2536 | + } else { |
2537 | + if (prop_scale != 1.0) y *= prop_scale; |
2538 | + } |
2539 | + x += toffset; |
2540 | + |
2541 | + double offs = 0; |
2542 | + for (int i=0; i<nbCopies; i++) { |
2543 | + if (false) { |
2544 | + Geom::Piecewise<Geom::D2<Geom::SBasis> > output_piece = compose(uskeleton,x+offs)+y*compose(n,x+offs); |
2545 | + std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > splited_output_piece = split_at_discontinuities(output_piece); |
2546 | + pre_output.insert(pre_output.end(), splited_output_piece.begin(), splited_output_piece.end() ); |
2547 | + } else { |
2548 | + output.concat(compose(uskeleton,x+offs)+y*compose(n,x+offs)); |
2549 | + } |
2550 | + offs+=pattWidth; |
2551 | + } |
2552 | + } |
2553 | + return output; |
2554 | + } else { |
2555 | + return pwd2_in; |
2556 | + } |
2557 | +} |
2558 | + |
2559 | +void subdivideCurve(Geom::Curve * curve_in, Geom::Coord t, Geom::Curve *& val_first, Geom::Curve *& val_second) |
2560 | +{ |
2561 | + if (Geom::LineSegment* linear = dynamic_cast<Geom::LineSegment*>(curve_in)) { |
2562 | + //special case for line segments |
2563 | + std::pair<Geom::LineSegment, Geom::LineSegment> seg_pair = linear->subdivide(t); |
2564 | + val_first = seg_pair.first.duplicate(); |
2565 | + val_second = seg_pair.second.duplicate(); |
2566 | + } else { |
2567 | + //all other cases: |
2568 | + Geom::CubicBezier cubic = Geom::sbasis_to_cubicbezier(curve_in->toSBasis()); |
2569 | + std::pair<Geom::CubicBezier, Geom::CubicBezier> cubic_pair = cubic.subdivide(t); |
2570 | + val_first = cubic_pair.first.duplicate(); |
2571 | + val_second = cubic_pair.second.duplicate(); |
2572 | + } |
2573 | +} |
2574 | + |
2575 | + |
2576 | +void LPETaperStroke::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) |
2577 | +{ |
2578 | + { |
2579 | + KnotHolderEntity *e = new TpS::KnotHolderEntityAttachBegin(this); |
2580 | + e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, |
2581 | + _("Start point of the taper"), SP_KNOT_SHAPE_CIRCLE ); |
2582 | + knotholder->add(e); |
2583 | + } |
2584 | + { |
2585 | + KnotHolderEntity *e = new TpS::KnotHolderEntityAttachEnd(this); |
2586 | + e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, |
2587 | + _("End point of the taper"), SP_KNOT_SHAPE_CIRCLE ); |
2588 | + knotholder->add(e); |
2589 | + } |
2590 | +} |
2591 | + |
2592 | +namespace TpS { |
2593 | +void KnotHolderEntityAttachBegin::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) |
2594 | +{ |
2595 | + using namespace Geom; |
2596 | + |
2597 | + LPETaperStroke* lpe = dynamic_cast<LPETaperStroke *>(_effect); |
2598 | + |
2599 | + Geom::Point const s = snap_knot_position(p, state); |
2600 | + |
2601 | + if (!SP_IS_SHAPE(lpe->sp_lpe_item) ) { |
2602 | + g_warning("LPEItem is not a path! %s:%d\n", __FILE__, __LINE__); |
2603 | + return; |
2604 | + } |
2605 | + |
2606 | + SPCurve* curve; |
2607 | + if ( !(curve = SP_SHAPE(lpe->sp_lpe_item)->getCurve()) ) { |
2608 | + //oops |
2609 | + //lpe->attach_start.param_set_value(0); |
2610 | + return; |
2611 | + } |
2612 | + //in case you are wondering, the above are simply sanity checks. we never want to actually |
2613 | + //use that object. |
2614 | + |
2615 | + Geom::PathVector pathv = lpe->pathvector_before_effect; |
2616 | + |
2617 | + Piecewise<D2<SBasis> > pwd2; |
2618 | + Geom::Path p_in = return_at_first_cusp(pathv[0]); |
2619 | + pwd2.concat(p_in.toPwSb()); |
2620 | + |
2621 | + double t0 = nearest_point(s, pwd2); |
2622 | + lpe->attach_start.param_set_value(t0); |
2623 | + |
2624 | + // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. |
2625 | + sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); |
2626 | +} |
2627 | +void KnotHolderEntityAttachEnd::knot_set(Geom::Point const &p, Geom::Point const& /*origin*/, guint state) |
2628 | +{ |
2629 | + using namespace Geom; |
2630 | + |
2631 | + LPETaperStroke* lpe = dynamic_cast<LPETaperStroke *>(_effect); |
2632 | + |
2633 | + Geom::Point const s = snap_knot_position(p, state); |
2634 | + |
2635 | + if (!SP_IS_SHAPE(lpe->sp_lpe_item) ) { |
2636 | + g_warning("LPEItem is not a path! %s:%d\n", __FILE__, __LINE__); |
2637 | + return; |
2638 | + } |
2639 | + |
2640 | + SPCurve* curve; |
2641 | + if ( !(curve = SP_SHAPE(lpe->sp_lpe_item)->getCurve()) ) { |
2642 | + //oops |
2643 | + //lpe->attach_end.param_set_value(0); |
2644 | + return; |
2645 | + } |
2646 | + Geom::PathVector pathv = lpe->pathvector_before_effect; |
2647 | + Geom::Path p_in = return_at_first_cusp(pathv[0].reverse()); |
2648 | + Piecewise<D2<SBasis> > pwd2 = p_in.toPwSb(); |
2649 | + |
2650 | + double t0 = nearest_point(s, pwd2); |
2651 | + lpe->attach_end.param_set_value(t0); |
2652 | + |
2653 | + sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); |
2654 | +} |
2655 | +Geom::Point KnotHolderEntityAttachBegin::knot_get() const |
2656 | +{ |
2657 | + LPETaperStroke const * lpe = dynamic_cast<LPETaperStroke const*> (_effect); |
2658 | + return lpe->start_attach_point; |
2659 | +} |
2660 | +Geom::Point KnotHolderEntityAttachEnd::knot_get() const |
2661 | +{ |
2662 | + LPETaperStroke const * lpe = dynamic_cast<LPETaperStroke const*> (_effect); |
2663 | + return lpe->end_attach_point; |
2664 | +} |
2665 | +} |
2666 | + |
2667 | + |
2668 | +/* ######################## */ |
2669 | + |
2670 | +} //namespace LivePathEffect |
2671 | +} /* namespace Inkscape */ |
2672 | + |
2673 | +/* |
2674 | + Local Variables: |
2675 | + mode:c++ |
2676 | + c-file-style:"stroustrup" |
2677 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
2678 | + indent-tabs-mode:nil |
2679 | + fill-column:99 |
2680 | + End: |
2681 | +*/ |
2682 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : |
2683 | |
2684 | === added file 'src/live_effects/lpe-taperstroke.h' |
2685 | --- src/live_effects/lpe-taperstroke.h 1970-01-01 00:00:00 +0000 |
2686 | +++ src/live_effects/lpe-taperstroke.h 2014-09-07 17:02:32 +0000 |
2687 | @@ -0,0 +1,72 @@ |
2688 | +/** @file |
2689 | + * @brief Taper Stroke path effect (meant as a replacement for using Power Strokes for tapering) |
2690 | + */ |
2691 | +/* Authors: |
2692 | + * Liam P White <inkscapebrony@gmail.com> |
2693 | + * Copyright (C) 2014 Authors |
2694 | + * |
2695 | + * Released under GNU GPL, read the file 'COPYING' for more information |
2696 | + */ |
2697 | + |
2698 | +#ifndef INKSCAPE_LPE_TAPERSTROKE_H |
2699 | +#define INKSCAPE_LPE_TAPERSTROKE_H |
2700 | + |
2701 | +#include "live_effects/parameter/enum.h" |
2702 | +#include "live_effects/effect.h" |
2703 | +#include "live_effects/parameter/parameter.h" |
2704 | +#include "live_effects/parameter/vector.h" |
2705 | + |
2706 | +namespace Inkscape { |
2707 | +namespace LivePathEffect { |
2708 | + |
2709 | +namespace TpS { |
2710 | +// we need a separate namespace to avoid clashes with other LPEs |
2711 | +class KnotHolderEntityAttachBegin; |
2712 | +class KnotHolderEntityAttachEnd; |
2713 | +} |
2714 | + |
2715 | +class LPETaperStroke : public Effect { |
2716 | +public: |
2717 | + LPETaperStroke(LivePathEffectObject *lpeobject); |
2718 | + virtual ~LPETaperStroke(); |
2719 | + |
2720 | + virtual void doOnApply(SPLPEItem const* lpeitem); |
2721 | + virtual void doOnRemove(SPLPEItem const* lpeitem); |
2722 | + |
2723 | + virtual Geom::PathVector doEffect_path (Geom::PathVector const& path_in); |
2724 | + Geom::PathVector doEffect_simplePath(Geom::PathVector const& path_in); |
2725 | + |
2726 | + virtual void addKnotHolderEntities(KnotHolder * knotholder, SPDesktop * desktop, SPItem * item); |
2727 | + |
2728 | + friend class TpS::KnotHolderEntityAttachBegin; |
2729 | + friend class TpS::KnotHolderEntityAttachEnd; |
2730 | +private: |
2731 | + ScalarParam line_width; |
2732 | + ScalarParam attach_start; |
2733 | + ScalarParam attach_end; |
2734 | + ScalarParam smoothing; |
2735 | + EnumParam<unsigned> join_type; |
2736 | + ScalarParam miter_limit; |
2737 | + |
2738 | + Geom::Point start_attach_point; |
2739 | + Geom::Point end_attach_point; |
2740 | + |
2741 | + LPETaperStroke(const LPETaperStroke&); |
2742 | + LPETaperStroke& operator=(const LPETaperStroke&); |
2743 | +}; |
2744 | + |
2745 | +} //namespace LivePathEffect |
2746 | +} //namespace Inkscape |
2747 | + |
2748 | +#endif |
2749 | + |
2750 | +/* |
2751 | + Local Variables: |
2752 | + mode:c++ |
2753 | + c-file-style:"stroustrup" |
2754 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
2755 | + indent-tabs-mode:nil |
2756 | + fill-column:99 |
2757 | + End: |
2758 | +*/ |
2759 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : |
2760 | |
2761 | === modified file 'src/live_effects/parameter/Makefile_insert' |
2762 | --- src/live_effects/parameter/Makefile_insert 2014-07-02 11:14:35 +0000 |
2763 | +++ src/live_effects/parameter/Makefile_insert 2014-09-07 17:02:32 +0000 |
2764 | @@ -20,12 +20,16 @@ |
2765 | live_effects/parameter/path.h \ |
2766 | live_effects/parameter/originalpath.cpp \ |
2767 | live_effects/parameter/originalpath.h \ |
2768 | + live_effects/parameter/originalpatharray.cpp \ |
2769 | + live_effects/parameter/originalpatharray.h \ |
2770 | live_effects/parameter/powerstrokepointarray.cpp \ |
2771 | live_effects/parameter/powerstrokepointarray.h \ |
2772 | live_effects/parameter/filletchamferpointarray.cpp \ |
2773 | live_effects/parameter/filletchamferpointarray.h \ |
2774 | live_effects/parameter/text.cpp \ |
2775 | live_effects/parameter/text.h \ |
2776 | + live_effects/parameter/transformedpoint.cpp \ |
2777 | + live_effects/parameter/transformedpoint.h \ |
2778 | live_effects/parameter/togglebutton.cpp \ |
2779 | live_effects/parameter/togglebutton.h \ |
2780 | live_effects/parameter/unit.cpp \ |
2781 | |
2782 | === modified file 'src/live_effects/parameter/filletchamferpointarray.cpp' |
2783 | --- src/live_effects/parameter/filletchamferpointarray.cpp 2014-08-24 10:37:18 +0000 |
2784 | +++ src/live_effects/parameter/filletchamferpointarray.cpp 2014-09-07 17:02:32 +0000 |
2785 | @@ -8,11 +8,14 @@ |
2786 | * Released under GNU GPL, read the file 'COPYING' for more information |
2787 | */ |
2788 | |
2789 | +#include <glibmm.h> |
2790 | + |
2791 | #include "ui/dialog/lpe-fillet-chamfer-properties.h" |
2792 | #include "live_effects/parameter/filletchamferpointarray.h" |
2793 | #include <2geom/piecewise.h> |
2794 | #include <2geom/sbasis-to-bezier.h> |
2795 | #include <2geom/sbasis-geometric.h> |
2796 | +#include <gtkmm.h> |
2797 | |
2798 | #include "live_effects/effect.h" |
2799 | #include "svg/svg.h" |
2800 | |
2801 | === added file 'src/live_effects/parameter/originalpatharray.cpp' |
2802 | --- src/live_effects/parameter/originalpatharray.cpp 1970-01-01 00:00:00 +0000 |
2803 | +++ src/live_effects/parameter/originalpatharray.cpp 2014-09-07 17:02:32 +0000 |
2804 | @@ -0,0 +1,497 @@ |
2805 | +/* |
2806 | + * Copyright (C) Johan Engelen 2008 <j.b.c.engelen@utwente.nl> |
2807 | + * |
2808 | + * Released under GNU GPL, read the file 'COPYING' for more information |
2809 | + */ |
2810 | + |
2811 | +#ifdef HAVE_CONFIG_H |
2812 | +# include "config.h" |
2813 | +#endif |
2814 | + |
2815 | +#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H |
2816 | +#include <glibmm/threads.h> |
2817 | +#endif |
2818 | + |
2819 | +#include "live_effects/parameter/originalpatharray.h" |
2820 | + |
2821 | +#include <gtkmm/widget.h> |
2822 | +#include <gtkmm/icontheme.h> |
2823 | +#include <gtkmm/imagemenuitem.h> |
2824 | +#include <gtkmm/separatormenuitem.h> |
2825 | +#include <gtkmm/scrolledwindow.h> |
2826 | + |
2827 | +#include <glibmm/i18n.h> |
2828 | + |
2829 | +#include "inkscape.h" |
2830 | +#include "icon-size.h" |
2831 | +#include "widgets/icon.h" |
2832 | +#include "ui/clipboard.h" |
2833 | +#include "svg/svg.h" |
2834 | +#include "svg/stringstream.h" |
2835 | +#include "originalpath.h" |
2836 | +#include "uri.h" |
2837 | +#include "display/curve.h" |
2838 | + |
2839 | +#include <2geom/coord.h> |
2840 | +#include <2geom/point.h> |
2841 | +#include "sp-shape.h" |
2842 | +#include "sp-text.h" |
2843 | +#include "live_effects/effect.h" |
2844 | + |
2845 | +#include "verbs.h" |
2846 | +#include "document-undo.h" |
2847 | +#include "document.h" |
2848 | + |
2849 | +namespace Inkscape { |
2850 | + |
2851 | +namespace LivePathEffect { |
2852 | + |
2853 | +class OriginalPathArrayParam::ModelColumns : public Gtk::TreeModel::ColumnRecord |
2854 | +{ |
2855 | +public: |
2856 | + |
2857 | + ModelColumns() |
2858 | + { |
2859 | + add(_colObject); |
2860 | + add(_colLabel); |
2861 | + add(_colReverse); |
2862 | + } |
2863 | + virtual ~ModelColumns() {} |
2864 | + |
2865 | + Gtk::TreeModelColumn<PathAndDirection*> _colObject; |
2866 | + Gtk::TreeModelColumn<Glib::ustring> _colLabel; |
2867 | + Gtk::TreeModelColumn<bool> _colReverse; |
2868 | +}; |
2869 | + |
2870 | +OriginalPathArrayParam::OriginalPathArrayParam( const Glib::ustring& label, |
2871 | + const Glib::ustring& tip, |
2872 | + const Glib::ustring& key, |
2873 | + Inkscape::UI::Widget::Registry* wr, |
2874 | + Effect* effect ) |
2875 | +: Parameter(label, tip, key, wr, effect), |
2876 | + _vector(), |
2877 | + _tree(), |
2878 | + _text_renderer(), |
2879 | + _toggle_renderer(), |
2880 | + _scroller() |
2881 | +{ |
2882 | + _model = new ModelColumns(); |
2883 | + _store = Gtk::TreeStore::create(*_model); |
2884 | + _tree.set_model(_store); |
2885 | + |
2886 | + _tree.set_reorderable(true); |
2887 | + _tree.enable_model_drag_dest (Gdk::ACTION_MOVE); |
2888 | + |
2889 | + _text_renderer = manage(new Gtk::CellRendererText()); |
2890 | + int nameColNum = _tree.append_column(_("Name"), *_text_renderer) - 1; |
2891 | + _name_column = _tree.get_column(nameColNum); |
2892 | + _name_column->add_attribute(_text_renderer->property_text(), _model->_colLabel); |
2893 | + |
2894 | + _tree.set_expander_column( *_tree.get_column(nameColNum) ); |
2895 | + _tree.set_search_column(_model->_colLabel); |
2896 | + |
2897 | + Gtk::CellRendererToggle * _toggle_renderer = manage(new Gtk::CellRendererToggle()); |
2898 | + int toggleColNum = _tree.append_column(_("Reverse"), *_toggle_renderer) - 1; |
2899 | + Gtk::TreeViewColumn* col = _tree.get_column(toggleColNum); |
2900 | + _toggle_renderer->set_activatable(true); |
2901 | + _toggle_renderer->signal_toggled().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_reverse_toggled)); |
2902 | + col->add_attribute(_toggle_renderer->property_active(), _model->_colReverse); |
2903 | + |
2904 | + //quick little hack -- newer versions of gtk gave the item zero space allotment |
2905 | + _scroller.set_size_request(-1, 120); |
2906 | + |
2907 | + _scroller.add(_tree); |
2908 | + _scroller.set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC ); |
2909 | + //_scroller.set_shadow_type(Gtk::SHADOW_IN); |
2910 | + |
2911 | + oncanvas_editable = true; |
2912 | + |
2913 | +} |
2914 | + |
2915 | +OriginalPathArrayParam::~OriginalPathArrayParam() |
2916 | +{ |
2917 | + while (!_vector.empty()) { |
2918 | + PathAndDirection *w = _vector.back(); |
2919 | + _vector.pop_back(); |
2920 | + unlink(w); |
2921 | + delete w; |
2922 | + } |
2923 | + delete _model; |
2924 | +} |
2925 | + |
2926 | +void OriginalPathArrayParam::on_reverse_toggled(const Glib::ustring& path) |
2927 | +{ |
2928 | + Gtk::TreeModel::iterator iter = _store->get_iter(path); |
2929 | + Gtk::TreeModel::Row row = *iter; |
2930 | + PathAndDirection *w = row[_model->_colObject]; |
2931 | + row[_model->_colReverse] = !row[_model->_colReverse]; |
2932 | + w->reversed = row[_model->_colReverse]; |
2933 | + |
2934 | + gchar * full = param_getSVGValue(); |
2935 | + param_write_to_repr(full); |
2936 | + g_free(full); |
2937 | + DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, |
2938 | + _("Link path parameter to path")); |
2939 | +} |
2940 | + |
2941 | +void OriginalPathArrayParam::param_set_default() |
2942 | +{ |
2943 | + |
2944 | +} |
2945 | + |
2946 | +Gtk::Widget* OriginalPathArrayParam::param_newWidget() |
2947 | +{ |
2948 | + Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox()); |
2949 | + Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox()); |
2950 | + |
2951 | + vbox->pack_start(_scroller, Gtk::PACK_EXPAND_WIDGET); |
2952 | + |
2953 | + |
2954 | + { // Paste path to link button |
2955 | + Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( GTK_STOCK_PASTE, Inkscape::ICON_SIZE_BUTTON) ); |
2956 | + Gtk::Button *pButton = Gtk::manage(new Gtk::Button()); |
2957 | + pButton->set_relief(Gtk::RELIEF_NONE); |
2958 | + pIcon->show(); |
2959 | + pButton->add(*pIcon); |
2960 | + pButton->show(); |
2961 | + pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_link_button_click)); |
2962 | + hbox->pack_start(*pButton, Gtk::PACK_SHRINK); |
2963 | + pButton->set_tooltip_text(_("Link to path")); |
2964 | + } |
2965 | + |
2966 | + { // Remove linked path |
2967 | + Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( GTK_STOCK_REMOVE, Inkscape::ICON_SIZE_BUTTON) ); |
2968 | + Gtk::Button *pButton = Gtk::manage(new Gtk::Button()); |
2969 | + pButton->set_relief(Gtk::RELIEF_NONE); |
2970 | + pIcon->show(); |
2971 | + pButton->add(*pIcon); |
2972 | + pButton->show(); |
2973 | + pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_remove_button_click)); |
2974 | + hbox->pack_start(*pButton, Gtk::PACK_SHRINK); |
2975 | + pButton->set_tooltip_text(_("Remove Path")); |
2976 | + } |
2977 | + |
2978 | + { // Move Down |
2979 | + Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( GTK_STOCK_GO_DOWN, Inkscape::ICON_SIZE_BUTTON) ); |
2980 | + Gtk::Button *pButton = Gtk::manage(new Gtk::Button()); |
2981 | + pButton->set_relief(Gtk::RELIEF_NONE); |
2982 | + pIcon->show(); |
2983 | + pButton->add(*pIcon); |
2984 | + pButton->show(); |
2985 | + pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_down_button_click)); |
2986 | + hbox->pack_end(*pButton, Gtk::PACK_SHRINK); |
2987 | + pButton->set_tooltip_text(_("Move Down")); |
2988 | + } |
2989 | + |
2990 | + { // Move Down |
2991 | + Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( GTK_STOCK_GO_UP, Inkscape::ICON_SIZE_BUTTON) ); |
2992 | + Gtk::Button *pButton = Gtk::manage(new Gtk::Button()); |
2993 | + pButton->set_relief(Gtk::RELIEF_NONE); |
2994 | + pIcon->show(); |
2995 | + pButton->add(*pIcon); |
2996 | + pButton->show(); |
2997 | + pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_up_button_click)); |
2998 | + hbox->pack_end(*pButton, Gtk::PACK_SHRINK); |
2999 | + pButton->set_tooltip_text(_("Move Up")); |
3000 | + } |
3001 | + |
3002 | + vbox->pack_end(*hbox, Gtk::PACK_SHRINK); |
3003 | + |
3004 | + vbox->show_all_children(true); |
3005 | + |
3006 | + return vbox; |
3007 | +} |
3008 | + |
3009 | +bool OriginalPathArrayParam::_selectIndex(const Gtk::TreeIter& iter, int* i) |
3010 | +{ |
3011 | + if ((*i)-- <= 0) { |
3012 | + _tree.get_selection()->select(iter); |
3013 | + return true; |
3014 | + } |
3015 | + return false; |
3016 | +} |
3017 | + |
3018 | +void OriginalPathArrayParam::on_up_button_click() |
3019 | +{ |
3020 | + Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected(); |
3021 | + if (iter) { |
3022 | + Gtk::TreeModel::Row row = *iter; |
3023 | + |
3024 | + int i = -1; |
3025 | + std::vector<PathAndDirection*>::iterator piter = _vector.begin(); |
3026 | + for (std::vector<PathAndDirection*>::iterator iter = _vector.begin(); iter != _vector.end(); piter = iter, i++, iter++) { |
3027 | + if (*iter == row[_model->_colObject]) { |
3028 | + _vector.erase(iter); |
3029 | + _vector.insert(piter, row[_model->_colObject]); |
3030 | + break; |
3031 | + } |
3032 | + } |
3033 | + |
3034 | + gchar * full = param_getSVGValue(); |
3035 | + param_write_to_repr(full); |
3036 | + g_free(full); |
3037 | + |
3038 | + DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, |
3039 | + _("Move path up")); |
3040 | + |
3041 | + _store->foreach_iter(sigc::bind<int*>(sigc::mem_fun(*this, &OriginalPathArrayParam::_selectIndex), &i)); |
3042 | + } |
3043 | +} |
3044 | + |
3045 | +void OriginalPathArrayParam::on_down_button_click() |
3046 | +{ |
3047 | + Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected(); |
3048 | + if (iter) { |
3049 | + Gtk::TreeModel::Row row = *iter; |
3050 | + |
3051 | + int i = 0; |
3052 | + for (std::vector<PathAndDirection*>::iterator iter = _vector.begin(); iter != _vector.end(); i++, iter++) { |
3053 | + if (*iter == row[_model->_colObject]) { |
3054 | + std::vector<PathAndDirection*>::iterator niter = _vector.erase(iter); |
3055 | + if (niter != _vector.end()) { |
3056 | + niter++; |
3057 | + i++; |
3058 | + } |
3059 | + _vector.insert(niter, row[_model->_colObject]); |
3060 | + break; |
3061 | + } |
3062 | + } |
3063 | + |
3064 | + gchar * full = param_getSVGValue(); |
3065 | + param_write_to_repr(full); |
3066 | + g_free(full); |
3067 | + |
3068 | + DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, |
3069 | + _("Move path down")); |
3070 | + |
3071 | + _store->foreach_iter(sigc::bind<int*>(sigc::mem_fun(*this, &OriginalPathArrayParam::_selectIndex), &i)); |
3072 | + } |
3073 | +} |
3074 | + |
3075 | +void OriginalPathArrayParam::on_remove_button_click() |
3076 | +{ |
3077 | + Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected(); |
3078 | + if (iter) { |
3079 | + Gtk::TreeModel::Row row = *iter; |
3080 | + remove_link(row[_model->_colObject]); |
3081 | + |
3082 | + gchar * full = param_getSVGValue(); |
3083 | + param_write_to_repr(full); |
3084 | + g_free(full); |
3085 | + |
3086 | + DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, |
3087 | + _("Remove path")); |
3088 | + } |
3089 | + |
3090 | +} |
3091 | + |
3092 | +void |
3093 | +OriginalPathArrayParam::on_link_button_click() |
3094 | +{ |
3095 | + Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); |
3096 | + Glib::ustring pathid = cm->getShapeOrTextObjectId(SP_ACTIVE_DESKTOP); |
3097 | + |
3098 | + if (pathid == "") { |
3099 | + return; |
3100 | + } |
3101 | + // add '#' at start to make it an uri. |
3102 | + pathid.insert(pathid.begin(), '#'); |
3103 | + |
3104 | + Inkscape::SVGOStringStream os; |
3105 | + bool foundOne = false; |
3106 | + for (std::vector<PathAndDirection*>::const_iterator iter = _vector.begin(); iter != _vector.end(); iter++) { |
3107 | + if (foundOne) { |
3108 | + os << "|"; |
3109 | + } else { |
3110 | + foundOne = true; |
3111 | + } |
3112 | + os << (*iter)->href << "," << ((*iter)->reversed ? "1" : "0"); |
3113 | + } |
3114 | + |
3115 | + if (foundOne) { |
3116 | + os << "|"; |
3117 | + } |
3118 | + |
3119 | + os << pathid.c_str() << ",0"; |
3120 | + |
3121 | + param_write_to_repr(os.str().c_str()); |
3122 | + DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, |
3123 | + _("Link path parameter to path")); |
3124 | +} |
3125 | + |
3126 | +void OriginalPathArrayParam::unlink(PathAndDirection* to) |
3127 | +{ |
3128 | + to->linked_modified_connection.disconnect(); |
3129 | + to->linked_delete_connection.disconnect(); |
3130 | + to->ref.detach(); |
3131 | + to->_pathvector = Geom::PathVector(); |
3132 | + if (to->href) { |
3133 | + g_free(to->href); |
3134 | + to->href = NULL; |
3135 | + } |
3136 | +} |
3137 | + |
3138 | +void OriginalPathArrayParam::remove_link(PathAndDirection* to) |
3139 | +{ |
3140 | + unlink(to); |
3141 | + for (std::vector<PathAndDirection*>::iterator iter = _vector.begin(); iter != _vector.end(); iter++) { |
3142 | + if (*iter == to) { |
3143 | + PathAndDirection *w = *iter; |
3144 | + _vector.erase(iter); |
3145 | + delete w; |
3146 | + return; |
3147 | + } |
3148 | + } |
3149 | +} |
3150 | + |
3151 | +void OriginalPathArrayParam::linked_delete(SPObject */*deleted*/, PathAndDirection* to) |
3152 | +{ |
3153 | + //remove_link(to); |
3154 | + |
3155 | + gchar * full = param_getSVGValue(); |
3156 | + param_write_to_repr(full); |
3157 | + g_free(full); |
3158 | +} |
3159 | + |
3160 | +bool OriginalPathArrayParam::_updateLink(const Gtk::TreeIter& iter, PathAndDirection* pd) |
3161 | +{ |
3162 | + Gtk::TreeModel::Row row = *iter; |
3163 | + if (row[_model->_colObject] == pd) { |
3164 | + SPObject *obj = pd->ref.getObject(); |
3165 | + row[_model->_colLabel] = obj && obj->getId() ? ( obj->label() ? obj->label() : obj->getId() ) : pd->href; |
3166 | + return true; |
3167 | + } |
3168 | + return false; |
3169 | +} |
3170 | + |
3171 | +void OriginalPathArrayParam::linked_changed(SPObject */*old_obj*/, SPObject *new_obj, PathAndDirection* to) |
3172 | +{ |
3173 | + to->linked_delete_connection.disconnect(); |
3174 | + to->linked_modified_connection.disconnect(); |
3175 | + to->linked_transformed_connection.disconnect(); |
3176 | + |
3177 | + if (new_obj && SP_IS_ITEM(new_obj)) { |
3178 | + to->linked_delete_connection = new_obj->connectDelete(sigc::bind<PathAndDirection*>(sigc::mem_fun(*this, &OriginalPathArrayParam::linked_delete), to)); |
3179 | + to->linked_modified_connection = new_obj->connectModified(sigc::bind<PathAndDirection*>(sigc::mem_fun(*this, &OriginalPathArrayParam::linked_modified), to)); |
3180 | + to->linked_transformed_connection = SP_ITEM(new_obj)->connectTransformed(sigc::bind<PathAndDirection*>(sigc::mem_fun(*this, &OriginalPathArrayParam::linked_transformed), to)); |
3181 | + |
3182 | + linked_modified(new_obj, SP_OBJECT_MODIFIED_FLAG, to); |
3183 | + } else { |
3184 | + to->_pathvector = Geom::PathVector(); |
3185 | + SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG); |
3186 | + _store->foreach_iter(sigc::bind<PathAndDirection*>(sigc::mem_fun(*this, &OriginalPathArrayParam::_updateLink), to)); |
3187 | + } |
3188 | +} |
3189 | + |
3190 | +void OriginalPathArrayParam::linked_transformed(Geom::Affine const *mp, SPItem* original, PathAndDirection* to) |
3191 | +{ |
3192 | + |
3193 | +} |
3194 | + |
3195 | +void OriginalPathArrayParam::setPathVector(SPObject *linked_obj, guint flags, PathAndDirection* to) |
3196 | +{ |
3197 | + if (!to) { |
3198 | + return; |
3199 | + } |
3200 | + SPCurve *curve = NULL; |
3201 | + if (SP_IS_SHAPE(linked_obj)) { |
3202 | + curve = SP_SHAPE(linked_obj)->getCurveBeforeLPE(); |
3203 | + } |
3204 | + if (SP_IS_TEXT(linked_obj)) { |
3205 | + curve = SP_TEXT(linked_obj)->getNormalizedBpath(); |
3206 | + } |
3207 | + |
3208 | + if (curve == NULL) { |
3209 | + // curve invalid, set empty pathvector |
3210 | + to->_pathvector = Geom::PathVector(); |
3211 | + } else { |
3212 | + to->_pathvector = curve->get_pathvector(); |
3213 | + curve->unref(); |
3214 | + } |
3215 | +} |
3216 | + |
3217 | +void OriginalPathArrayParam::linked_modified(SPObject *linked_obj, guint flags, PathAndDirection* to) |
3218 | +{ |
3219 | + if (!to) { |
3220 | + return; |
3221 | + } |
3222 | + setPathVector(linked_obj, flags, to); |
3223 | + SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG); |
3224 | + _store->foreach_iter(sigc::bind<PathAndDirection*>(sigc::mem_fun(*this, &OriginalPathArrayParam::_updateLink), to)); |
3225 | +} |
3226 | + |
3227 | +//void PathParam::linked_transformed(Geom::Affine const *rel_transf, SPItem *moved_item) |
3228 | +//{ |
3229 | +// linked_transformed_callback(rel_transf, moved_item); |
3230 | +//} |
3231 | + |
3232 | +bool OriginalPathArrayParam::param_readSVGValue(const gchar* strvalue) |
3233 | +{ |
3234 | + if (strvalue) { |
3235 | + while (!_vector.empty()) { |
3236 | + PathAndDirection *w = _vector.back(); |
3237 | + unlink(w); |
3238 | + _vector.pop_back(); |
3239 | + delete w; |
3240 | + } |
3241 | + _store->clear(); |
3242 | + |
3243 | + gchar ** strarray = g_strsplit(strvalue, "|", 0); |
3244 | + for (gchar ** iter = strarray; *iter != NULL; iter++) { |
3245 | + if ((*iter)[0] == '#') { |
3246 | + gchar ** substrarray = g_strsplit(*iter, ",", 0); |
3247 | + PathAndDirection* w = new PathAndDirection((SPObject *)param_effect->getLPEObj()); |
3248 | + w->href = g_strdup(*substrarray); |
3249 | + w->reversed = *(substrarray+1) != NULL && (*(substrarray+1))[0] == '1'; |
3250 | + |
3251 | + w->linked_changed_connection = w->ref.changedSignal().connect(sigc::bind<PathAndDirection *>(sigc::mem_fun(*this, &OriginalPathArrayParam::linked_changed), w)); |
3252 | + w->ref.attach(URI(w->href)); |
3253 | + |
3254 | + _vector.push_back(w); |
3255 | + |
3256 | + Gtk::TreeModel::iterator iter = _store->append(); |
3257 | + Gtk::TreeModel::Row row = *iter; |
3258 | + SPObject *obj = w->ref.getObject(); |
3259 | + |
3260 | + row[_model->_colObject] = w; |
3261 | + row[_model->_colLabel] = obj ? ( obj->label() ? obj->label() : obj->getId() ) : w->href; |
3262 | + row[_model->_colReverse] = w->reversed; |
3263 | + g_strfreev (substrarray); |
3264 | + } |
3265 | + } |
3266 | + g_strfreev (strarray); |
3267 | + return true; |
3268 | + } |
3269 | + return false; |
3270 | +} |
3271 | + |
3272 | +gchar * OriginalPathArrayParam::param_getSVGValue() const |
3273 | +{ |
3274 | + Inkscape::SVGOStringStream os; |
3275 | + bool foundOne = false; |
3276 | + for (std::vector<PathAndDirection*>::const_iterator iter = _vector.begin(); iter != _vector.end(); iter++) { |
3277 | + if (foundOne) { |
3278 | + os << "|"; |
3279 | + } else { |
3280 | + foundOne = true; |
3281 | + } |
3282 | + os << (*iter)->href << "," << ((*iter)->reversed ? "1" : "0"); |
3283 | + } |
3284 | + gchar * str = g_strdup(os.str().c_str()); |
3285 | + return str; |
3286 | +} |
3287 | + |
3288 | +} /* namespace LivePathEffect */ |
3289 | + |
3290 | +} /* namespace Inkscape */ |
3291 | + |
3292 | +/* |
3293 | + Local Variables: |
3294 | + mode:c++ |
3295 | + c-file-style:"stroustrup" |
3296 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
3297 | + indent-tabs-mode:nil |
3298 | + fill-column:99 |
3299 | + End: |
3300 | +*/ |
3301 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
3302 | |
3303 | === added file 'src/live_effects/parameter/originalpatharray.h' |
3304 | --- src/live_effects/parameter/originalpatharray.h 1970-01-01 00:00:00 +0000 |
3305 | +++ src/live_effects/parameter/originalpatharray.h 2014-09-07 17:02:32 +0000 |
3306 | @@ -0,0 +1,123 @@ |
3307 | +#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINALPATHARRAY_H |
3308 | +#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINALPATHARRAY_H |
3309 | + |
3310 | +/* |
3311 | + * Inkscape::LivePathEffectParameters |
3312 | + * |
3313 | +* Copyright (C) Johan Engelen 2008 <j.b.c.engelen@utwente.nl> |
3314 | + * |
3315 | + * Released under GNU GPL, read the file 'COPYING' for more information |
3316 | + */ |
3317 | + |
3318 | +#include <vector> |
3319 | + |
3320 | +#include <gtkmm/box.h> |
3321 | +#include <gtkmm/treeview.h> |
3322 | +#include <gtkmm/treestore.h> |
3323 | +#include <gtkmm/scrolledwindow.h> |
3324 | + |
3325 | +#include "live_effects/parameter/parameter.h" |
3326 | +#include "live_effects/parameter/path-reference.h" |
3327 | + |
3328 | +#include "svg/svg.h" |
3329 | +#include "svg/stringstream.h" |
3330 | +#include "path-reference.h" |
3331 | +#include "sp-object.h" |
3332 | + |
3333 | +namespace Inkscape { |
3334 | + |
3335 | +namespace LivePathEffect { |
3336 | + |
3337 | +class PathAndDirection { |
3338 | +public: |
3339 | + PathAndDirection(SPObject *owner) |
3340 | + : href(NULL), |
3341 | + ref(owner), |
3342 | + _pathvector(Geom::PathVector()), |
3343 | + reversed(false) |
3344 | + { |
3345 | + |
3346 | + } |
3347 | + gchar *href; |
3348 | + URIReference ref; |
3349 | + //SPItem *obj; |
3350 | + std::vector<Geom::Path> _pathvector; |
3351 | + bool reversed; |
3352 | + |
3353 | + sigc::connection linked_changed_connection; |
3354 | + sigc::connection linked_delete_connection; |
3355 | + sigc::connection linked_modified_connection; |
3356 | + sigc::connection linked_transformed_connection; |
3357 | +}; |
3358 | + |
3359 | +class OriginalPathArrayParam : public Parameter { |
3360 | +public: |
3361 | + class ModelColumns; |
3362 | + |
3363 | + OriginalPathArrayParam( const Glib::ustring& label, |
3364 | + const Glib::ustring& tip, |
3365 | + const Glib::ustring& key, |
3366 | + Inkscape::UI::Widget::Registry* wr, |
3367 | + Effect* effect); |
3368 | + |
3369 | + virtual ~OriginalPathArrayParam(); |
3370 | + |
3371 | + virtual Gtk::Widget * param_newWidget(); |
3372 | + virtual bool param_readSVGValue(const gchar * strvalue); |
3373 | + virtual gchar * param_getSVGValue() const; |
3374 | + virtual void param_set_default(); |
3375 | + |
3376 | + /** Disable the canvas indicators of parent class by overriding this method */ |
3377 | + virtual void param_editOncanvas(SPItem * /*item*/, SPDesktop * /*dt*/) {}; |
3378 | + /** Disable the canvas indicators of parent class by overriding this method */ |
3379 | + virtual void addCanvasIndicators(SPLPEItem const* /*lpeitem*/, std::vector<Geom::PathVector> & /*hp_vec*/) {}; |
3380 | + |
3381 | + std::vector<PathAndDirection*> _vector; |
3382 | + |
3383 | +protected: |
3384 | + bool _updateLink(const Gtk::TreeIter& iter, PathAndDirection* pd); |
3385 | + bool _selectIndex(const Gtk::TreeIter& iter, int* i); |
3386 | + void unlink(PathAndDirection* to); |
3387 | + void remove_link(PathAndDirection* to); |
3388 | + void setPathVector(SPObject *linked_obj, guint flags, PathAndDirection* to); |
3389 | + |
3390 | + void linked_changed(SPObject *old_obj, SPObject *new_obj, PathAndDirection* to); |
3391 | + void linked_modified(SPObject *linked_obj, guint flags, PathAndDirection* to); |
3392 | + void linked_transformed(Geom::Affine const *mp, SPItem *original, PathAndDirection* to); |
3393 | + void linked_delete(SPObject *deleted, PathAndDirection* to); |
3394 | + |
3395 | + ModelColumns *_model; |
3396 | + Glib::RefPtr<Gtk::TreeStore> _store; |
3397 | + Gtk::TreeView _tree; |
3398 | + Gtk::CellRendererText *_text_renderer; |
3399 | + Gtk::CellRendererToggle *_toggle_renderer; |
3400 | + Gtk::TreeView::Column *_name_column; |
3401 | + Gtk::ScrolledWindow _scroller; |
3402 | + |
3403 | + void on_link_button_click(); |
3404 | + void on_remove_button_click(); |
3405 | + void on_up_button_click(); |
3406 | + void on_down_button_click(); |
3407 | + void on_reverse_toggled(const Glib::ustring& path); |
3408 | + |
3409 | +private: |
3410 | + OriginalPathArrayParam(const OriginalPathArrayParam&); |
3411 | + OriginalPathArrayParam& operator=(const OriginalPathArrayParam&); |
3412 | +}; |
3413 | + |
3414 | +} //namespace LivePathEffect |
3415 | + |
3416 | +} //namespace Inkscape |
3417 | + |
3418 | +#endif |
3419 | + |
3420 | +/* |
3421 | + Local Variables: |
3422 | + mode:c++ |
3423 | + c-file-style:"stroustrup" |
3424 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
3425 | + indent-tabs-mode:nil |
3426 | + fill-column:99 |
3427 | + End: |
3428 | +*/ |
3429 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
3430 | |
3431 | === modified file 'src/live_effects/parameter/powerstrokepointarray.cpp' |
3432 | --- src/live_effects/parameter/powerstrokepointarray.cpp 2014-07-27 01:17:50 +0000 |
3433 | +++ src/live_effects/parameter/powerstrokepointarray.cpp 2014-09-07 17:02:32 +0000 |
3434 | @@ -4,8 +4,7 @@ |
3435 | * Released under GNU GPL, read the file 'COPYING' for more information |
3436 | */ |
3437 | |
3438 | -#include <glibmm/i18n.h> |
3439 | - |
3440 | +#include "ui/dialog/lpe-powerstroke-properties.h" |
3441 | #include "live_effects/parameter/powerstrokepointarray.h" |
3442 | |
3443 | #include "live_effects/effect.h" |
3444 | @@ -21,6 +20,8 @@ |
3445 | #include "desktop.h" |
3446 | #include "live_effects/lpeobject.h" |
3447 | |
3448 | +#include <glibmm/i18n.h> |
3449 | + |
3450 | namespace Inkscape { |
3451 | |
3452 | namespace LivePathEffect { |
3453 | @@ -102,6 +103,23 @@ |
3454 | } |
3455 | } |
3456 | |
3457 | +float PowerStrokePointArrayParam::median_width() |
3458 | +{ |
3459 | + size_t size = _vector.size(); |
3460 | + if (size > 0) |
3461 | + { |
3462 | + if (size % 2 == 0) |
3463 | + { |
3464 | + return (_vector[size / 2 - 1].y() + _vector[size / 2].y()) / 2; |
3465 | + } |
3466 | + else |
3467 | + { |
3468 | + return _vector[size / 2].y(); |
3469 | + } |
3470 | + } |
3471 | + return 1; |
3472 | +} |
3473 | + |
3474 | void |
3475 | PowerStrokePointArrayParam::set_pwd2(Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in, Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_normal_in) |
3476 | { |
3477 | @@ -117,7 +135,7 @@ |
3478 | knot_mode = mode; |
3479 | knot_color = color; |
3480 | } |
3481 | - |
3482 | +/* |
3483 | class PowerStrokePointArrayParamKnotHolderEntity : public KnotHolderEntity { |
3484 | public: |
3485 | PowerStrokePointArrayParamKnotHolderEntity(PowerStrokePointArrayParam *p, unsigned int index); |
3486 | @@ -127,7 +145,7 @@ |
3487 | virtual Geom::Point knot_get() const; |
3488 | virtual void knot_click(guint state); |
3489 | |
3490 | - /** Checks whether the index falls within the size of the parameter's vector */ |
3491 | + // Checks whether the index falls within the size of the parameter's vector |
3492 | bool valid_index(unsigned int index) const { |
3493 | return (_pparam->_vector.size() > index); |
3494 | }; |
3495 | @@ -135,7 +153,7 @@ |
3496 | private: |
3497 | PowerStrokePointArrayParam *_pparam; |
3498 | unsigned int _index; |
3499 | -}; |
3500 | +};*/ |
3501 | |
3502 | PowerStrokePointArrayParamKnotHolderEntity::PowerStrokePointArrayParamKnotHolderEntity(PowerStrokePointArrayParam *p, unsigned int index) |
3503 | : _pparam(p), |
3504 | @@ -184,6 +202,12 @@ |
3505 | return canvas_point; |
3506 | } |
3507 | |
3508 | +void PowerStrokePointArrayParamKnotHolderEntity::knot_set_offset(Geom::Point offset) |
3509 | +{ |
3510 | + _pparam->_vector.at(_index) = Geom::Point(offset.x(), offset.y() / 2); |
3511 | + this->parent_holder->knot_ungrabbed_handler(this->knot, 0); |
3512 | +} |
3513 | + |
3514 | void |
3515 | PowerStrokePointArrayParamKnotHolderEntity::knot_click(guint state) |
3516 | { |
3517 | @@ -226,10 +250,15 @@ |
3518 | // add knot to knotholder |
3519 | PowerStrokePointArrayParamKnotHolderEntity *e = new PowerStrokePointArrayParamKnotHolderEntity(_pparam, _index+1); |
3520 | e->create( this->desktop, this->item, parent_holder, Inkscape::CTRL_TYPE_UNKNOWN, |
3521 | - _("<b>Stroke width control point</b>: drag to alter the stroke width. <b>Ctrl+click</b> adds a control point, <b>Ctrl+Alt+click</b> deletes it."), |
3522 | + _("<b>Stroke width control point</b>: drag to alter the stroke width. <b>Ctrl+click</b> adds a control point, <b>Ctrl+Alt+click</b> deletes it, <b>Shift+click</b> launches width dialog."), |
3523 | _pparam->knot_shape, _pparam->knot_mode, _pparam->knot_color); |
3524 | parent_holder->add(e); |
3525 | } |
3526 | + } |
3527 | + else if ((state & GDK_MOD1_MASK) || (state & GDK_SHIFT_MASK)) |
3528 | + { |
3529 | + Geom::Point offset = Geom::Point(_pparam->_vector.at(_index).x(), _pparam->_vector.at(_index).y() * 2); |
3530 | + Inkscape::UI::Dialogs::PowerstrokePropertiesDialog::showDialog(this->desktop, offset, this); |
3531 | } |
3532 | } |
3533 | |
3534 | @@ -238,7 +267,7 @@ |
3535 | for (unsigned int i = 0; i < _vector.size(); ++i) { |
3536 | PowerStrokePointArrayParamKnotHolderEntity *e = new PowerStrokePointArrayParamKnotHolderEntity(this, i); |
3537 | e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, |
3538 | - _("<b>Stroke width control point</b>: drag to alter the stroke width. <b>Ctrl+click</b> adds a control point, <b>Ctrl+Alt+click</b> deletes it."), |
3539 | + _("<b>Stroke width control point</b>: drag to alter the stroke width. <b>Ctrl+click</b> adds a control point, <b>Ctrl+Alt+click</b> deletes it, <b>Shift+click</b> launches width dialog."), |
3540 | knot_shape, knot_mode, knot_color); |
3541 | knotholder->add(e); |
3542 | } |
3543 | |
3544 | === modified file 'src/live_effects/parameter/powerstrokepointarray.h' |
3545 | --- src/live_effects/parameter/powerstrokepointarray.h 2014-03-27 01:33:44 +0000 |
3546 | +++ src/live_effects/parameter/powerstrokepointarray.h 2014-09-07 17:02:32 +0000 |
3547 | @@ -20,8 +20,6 @@ |
3548 | |
3549 | namespace LivePathEffect { |
3550 | |
3551 | -class PowerStrokePointArrayParamKnotHolderEntity; |
3552 | - |
3553 | class PowerStrokePointArrayParam : public ArrayParam<Geom::Point> { |
3554 | public: |
3555 | PowerStrokePointArrayParam( const Glib::ustring& label, |
3556 | @@ -37,6 +35,8 @@ |
3557 | |
3558 | void set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color); |
3559 | |
3560 | + float median_width(); |
3561 | + |
3562 | virtual bool providesKnotHolderEntities() const { return true; } |
3563 | virtual void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); |
3564 | |
3565 | @@ -60,6 +60,25 @@ |
3566 | Geom::Piecewise<Geom::D2<Geom::SBasis> > last_pwd2_normal; |
3567 | }; |
3568 | |
3569 | +class PowerStrokePointArrayParamKnotHolderEntity : public KnotHolderEntity { |
3570 | +public: |
3571 | + PowerStrokePointArrayParamKnotHolderEntity(PowerStrokePointArrayParam *p, unsigned int index); |
3572 | + virtual ~PowerStrokePointArrayParamKnotHolderEntity() {} |
3573 | + |
3574 | + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); |
3575 | + virtual Geom::Point knot_get() const; |
3576 | + virtual void knot_set_offset(Geom::Point offset); |
3577 | + virtual void knot_click(guint state); |
3578 | + |
3579 | + /** Checks whether the index falls within the size of the parameter's vector */ |
3580 | + bool valid_index(unsigned int index) const { |
3581 | + return (_pparam->_vector.size() > index); |
3582 | + }; |
3583 | + |
3584 | +private: |
3585 | + PowerStrokePointArrayParam *_pparam; |
3586 | + unsigned int _index; |
3587 | +}; |
3588 | |
3589 | } //namespace LivePathEffect |
3590 | |
3591 | |
3592 | === added file 'src/live_effects/parameter/transformedpoint.cpp' |
3593 | --- src/live_effects/parameter/transformedpoint.cpp 1970-01-01 00:00:00 +0000 |
3594 | +++ src/live_effects/parameter/transformedpoint.cpp 2014-09-07 17:02:32 +0000 |
3595 | @@ -0,0 +1,182 @@ |
3596 | +/* |
3597 | + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> |
3598 | + * |
3599 | + * Released under GNU GPL, read the file 'COPYING' for more information |
3600 | + */ |
3601 | + |
3602 | +#include "ui/widget/registered-widget.h" |
3603 | +#include "live_effects/parameter/transformedpoint.h" |
3604 | +#include "sp-lpe-item.h" |
3605 | +#include "knotholder.h" |
3606 | +#include "svg/svg.h" |
3607 | +#include "svg/stringstream.h" |
3608 | + |
3609 | +#include "live_effects/effect.h" |
3610 | +#include "desktop.h" |
3611 | +#include "verbs.h" |
3612 | + |
3613 | +#include <glibmm/i18n.h> |
3614 | + |
3615 | +namespace Inkscape { |
3616 | + |
3617 | +namespace LivePathEffect { |
3618 | + |
3619 | +TransformedPointParam::TransformedPointParam( const Glib::ustring& label, const Glib::ustring& tip, |
3620 | + const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr, |
3621 | + Effect* effect, Geom::Point default_vector, |
3622 | + bool dontTransform) |
3623 | + : Parameter(label, tip, key, wr, effect), |
3624 | + defvalue(default_vector), |
3625 | + origin(0.,0.), |
3626 | + vector(default_vector), |
3627 | + noTransform(dontTransform) |
3628 | +{ |
3629 | + vec_knot_shape = SP_KNOT_SHAPE_DIAMOND; |
3630 | + vec_knot_mode = SP_KNOT_MODE_XOR; |
3631 | + vec_knot_color = 0xffffb500; |
3632 | +} |
3633 | + |
3634 | +TransformedPointParam::~TransformedPointParam() |
3635 | +{ |
3636 | + |
3637 | +} |
3638 | + |
3639 | +void |
3640 | +TransformedPointParam::param_set_default() |
3641 | +{ |
3642 | + setOrigin(Geom::Point(0.,0.)); |
3643 | + setVector(defvalue); |
3644 | +} |
3645 | + |
3646 | +bool |
3647 | +TransformedPointParam::param_readSVGValue(const gchar * strvalue) |
3648 | +{ |
3649 | + gchar ** strarray = g_strsplit(strvalue, ",", 4); |
3650 | + if (!strarray) { |
3651 | + return false; |
3652 | + } |
3653 | + double val[4]; |
3654 | + unsigned int i = 0; |
3655 | + while (i < 4 && strarray[i]) { |
3656 | + if (sp_svg_number_read_d(strarray[i], &val[i]) != 0) { |
3657 | + i++; |
3658 | + } else { |
3659 | + break; |
3660 | + } |
3661 | + } |
3662 | + g_strfreev (strarray); |
3663 | + if (i == 4) { |
3664 | + setOrigin( Geom::Point(val[0], val[1]) ); |
3665 | + setVector( Geom::Point(val[2], val[3]) ); |
3666 | + return true; |
3667 | + } |
3668 | + return false; |
3669 | +} |
3670 | + |
3671 | +gchar * |
3672 | +TransformedPointParam::param_getSVGValue() const |
3673 | +{ |
3674 | + Inkscape::SVGOStringStream os; |
3675 | + os << origin << " , " << vector; |
3676 | + gchar * str = g_strdup(os.str().c_str()); |
3677 | + return str; |
3678 | +} |
3679 | + |
3680 | +Gtk::Widget * |
3681 | +TransformedPointParam::param_newWidget() |
3682 | +{ |
3683 | + Inkscape::UI::Widget::RegisteredVector * pointwdg = Gtk::manage( |
3684 | + new Inkscape::UI::Widget::RegisteredVector( param_label, |
3685 | + param_tooltip, |
3686 | + param_key, |
3687 | + *param_wr, |
3688 | + param_effect->getRepr(), |
3689 | + param_effect->getSPDoc() ) ); |
3690 | + pointwdg->setPolarCoords(); |
3691 | + pointwdg->setValue( vector, origin ); |
3692 | + pointwdg->clearProgrammatically(); |
3693 | + pointwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change vector parameter")); |
3694 | + |
3695 | + Gtk::HBox * hbox = Gtk::manage( new Gtk::HBox() ); |
3696 | + static_cast<Gtk::HBox*>(hbox)->pack_start(*pointwdg, true, true); |
3697 | + static_cast<Gtk::HBox*>(hbox)->show_all_children(); |
3698 | + |
3699 | + return dynamic_cast<Gtk::Widget *> (hbox); |
3700 | +} |
3701 | + |
3702 | +void |
3703 | +TransformedPointParam::set_and_write_new_values(Geom::Point const &new_origin, Geom::Point const &new_vector) |
3704 | +{ |
3705 | + setValues(new_origin, new_vector); |
3706 | + gchar * str = param_getSVGValue(); |
3707 | + param_write_to_repr(str); |
3708 | + g_free(str); |
3709 | +} |
3710 | + |
3711 | +void |
3712 | +TransformedPointParam::param_transform_multiply(Geom::Affine const& postmul, bool /*set*/) |
3713 | +{ |
3714 | + if (!noTransform) { |
3715 | + set_and_write_new_values( origin * postmul, vector * postmul.withoutTranslation() ); |
3716 | + } |
3717 | +} |
3718 | + |
3719 | + |
3720 | +void |
3721 | +TransformedPointParam::set_vector_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color) |
3722 | +{ |
3723 | + vec_knot_shape = shape; |
3724 | + vec_knot_mode = mode; |
3725 | + vec_knot_color = color; |
3726 | +} |
3727 | + |
3728 | +void |
3729 | +TransformedPointParam::set_oncanvas_color(guint32 color) |
3730 | +{ |
3731 | + vec_knot_color = color; |
3732 | +} |
3733 | + |
3734 | +class TransformedPointParamKnotHolderEntity_Vector : public KnotHolderEntity { |
3735 | +public: |
3736 | + TransformedPointParamKnotHolderEntity_Vector(TransformedPointParam *p) : param(p) { } |
3737 | + virtual ~TransformedPointParamKnotHolderEntity_Vector() {} |
3738 | + |
3739 | + virtual void knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint /*state*/) { |
3740 | + Geom::Point const s = p - param->origin; |
3741 | + /// @todo implement angle snapping when holding CTRL |
3742 | + param->setVector(s); |
3743 | + sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false); |
3744 | + }; |
3745 | + virtual Geom::Point knot_get() const{ |
3746 | + return param->origin + param->vector; |
3747 | + }; |
3748 | + virtual void knot_click(guint /*state*/){ |
3749 | + g_print ("This is the vector handle associated to parameter '%s'\n", param->param_key.c_str()); |
3750 | + }; |
3751 | + |
3752 | +private: |
3753 | + TransformedPointParam *param; |
3754 | +}; |
3755 | + |
3756 | +void |
3757 | +TransformedPointParam::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) |
3758 | +{ |
3759 | + TransformedPointParamKnotHolderEntity_Vector *vector_e = new TransformedPointParamKnotHolderEntity_Vector(this); |
3760 | + vector_e->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, handleTip(), vec_knot_shape, vec_knot_mode, vec_knot_color); |
3761 | + knotholder->add(vector_e); |
3762 | +} |
3763 | + |
3764 | +} /* namespace LivePathEffect */ |
3765 | + |
3766 | +} /* namespace Inkscape */ |
3767 | + |
3768 | +/* |
3769 | + Local Variables: |
3770 | + mode:c++ |
3771 | + c-file-style:"stroustrup" |
3772 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
3773 | + indent-tabs-mode:nil |
3774 | + fill-column:99 |
3775 | + End: |
3776 | +*/ |
3777 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |
3778 | |
3779 | === added file 'src/live_effects/parameter/transformedpoint.h' |
3780 | --- src/live_effects/parameter/transformedpoint.h 1970-01-01 00:00:00 +0000 |
3781 | +++ src/live_effects/parameter/transformedpoint.h 2014-09-07 17:02:32 +0000 |
3782 | @@ -0,0 +1,87 @@ |
3783 | +#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_TRANSFORMED_POINT_H |
3784 | +#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_TRANSFORMED_POINT_H |
3785 | + |
3786 | +/* |
3787 | + * Inkscape::LivePathEffectParameters |
3788 | + * |
3789 | + * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com> |
3790 | + * |
3791 | + * Released under GNU GPL, read the file 'COPYING' for more information |
3792 | + */ |
3793 | + |
3794 | +#include <glib.h> |
3795 | +#include <2geom/point.h> |
3796 | + |
3797 | +#include "live_effects/parameter/parameter.h" |
3798 | + |
3799 | +#include "knot-holder-entity.h" |
3800 | + |
3801 | +namespace Inkscape { |
3802 | + |
3803 | +namespace LivePathEffect { |
3804 | + |
3805 | + |
3806 | +class TransformedPointParam : public Parameter { |
3807 | +public: |
3808 | + TransformedPointParam( const Glib::ustring& label, |
3809 | + const Glib::ustring& tip, |
3810 | + const Glib::ustring& key, |
3811 | + Inkscape::UI::Widget::Registry* wr, |
3812 | + Effect* effect, |
3813 | + Geom::Point default_vector = Geom::Point(1,0), |
3814 | + bool dontTransform = false); |
3815 | + virtual ~TransformedPointParam(); |
3816 | + |
3817 | + virtual Gtk::Widget * param_newWidget(); |
3818 | + inline const gchar *handleTip() const { return param_tooltip.c_str(); } |
3819 | + |
3820 | + virtual bool param_readSVGValue(const gchar * strvalue); |
3821 | + virtual gchar * param_getSVGValue() const; |
3822 | + |
3823 | + Geom::Point getVector() const { return vector; }; |
3824 | + Geom::Point getOrigin() const { return origin; }; |
3825 | + void setValues(Geom::Point const &new_origin, Geom::Point const &new_vector) { setVector(new_vector); setOrigin(new_origin); }; |
3826 | + void setVector(Geom::Point const &new_vector) { vector = new_vector; }; |
3827 | + void setOrigin(Geom::Point const &new_origin) { origin = new_origin; }; |
3828 | + virtual void param_set_default(); |
3829 | + |
3830 | + void set_and_write_new_values(Geom::Point const &new_origin, Geom::Point const &new_vector); |
3831 | + |
3832 | + virtual void param_transform_multiply(Geom::Affine const &postmul, bool set); |
3833 | + |
3834 | + void set_vector_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color); |
3835 | + //void set_origin_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color); |
3836 | + void set_oncanvas_color(guint32 color); |
3837 | + |
3838 | + virtual bool providesKnotHolderEntities() const { return true; } |
3839 | + virtual void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); |
3840 | + |
3841 | +private: |
3842 | + TransformedPointParam(const TransformedPointParam&); |
3843 | + TransformedPointParam& operator=(const TransformedPointParam&); |
3844 | + |
3845 | + Geom::Point defvalue; |
3846 | + |
3847 | + Geom::Point origin; |
3848 | + Geom::Point vector; |
3849 | + |
3850 | + bool noTransform; |
3851 | + |
3852 | + /// The looks of the vector and origin knots oncanvas |
3853 | + SPKnotShapeType vec_knot_shape; |
3854 | + SPKnotModeType vec_knot_mode; |
3855 | + guint32 vec_knot_color; |
3856 | +// SPKnotShapeType ori_knot_shape; |
3857 | +// SPKnotModeType ori_knot_mode; |
3858 | +// guint32 ori_knot_color; |
3859 | + |
3860 | +// friend class VectorParamKnotHolderEntity_Origin; |
3861 | + friend class TransformedPointParamKnotHolderEntity_Vector; |
3862 | +}; |
3863 | + |
3864 | + |
3865 | +} //namespace LivePathEffect |
3866 | + |
3867 | +} //namespace Inkscape |
3868 | + |
3869 | +#endif |
3870 | |
3871 | === added file 'src/live_effects/pathoutlineprovider.cpp' |
3872 | --- src/live_effects/pathoutlineprovider.cpp 1970-01-01 00:00:00 +0000 |
3873 | +++ src/live_effects/pathoutlineprovider.cpp 2014-09-07 17:02:32 +0000 |
3874 | @@ -0,0 +1,795 @@ |
3875 | +#include <glib.h> //g_critical |
3876 | + |
3877 | +#include "pathoutlineprovider.h" |
3878 | +#include "livarot/path-description.h" |
3879 | +#include <2geom/angle.h> |
3880 | +#include <2geom/path.h> |
3881 | +#include <2geom/circle.h> |
3882 | +#include <2geom/sbasis-to-bezier.h> |
3883 | +#include <2geom/shape.h> |
3884 | +#include <2geom/transforms.h> |
3885 | +#include <2geom/path-sink.h> |
3886 | +#include "helper/geom-nodetype.h" |
3887 | +#include <svg/svg.h> |
3888 | + |
3889 | +namespace Geom { |
3890 | +/** |
3891 | +* Refer to: Weisstein, Eric W. "Circle-Circle Intersection." |
3892 | + From MathWorld--A Wolfram Web Resource. |
3893 | + http://mathworld.wolfram.com/Circle-CircleIntersection.html |
3894 | +* |
3895 | +* @return 0 if no intersection |
3896 | +* @return 1 if one circle is contained in the other |
3897 | +* @return 2 if intersections are found (they are written to p0 and p1) |
3898 | +*/ |
3899 | +static int circle_circle_intersection(Circle const &circle0, Circle const &circle1, |
3900 | + Point & p0, Point & p1) |
3901 | +{ |
3902 | + Point X0 = circle0.center(); |
3903 | + double r0 = circle0.ray(); |
3904 | + Point X1 = circle1.center(); |
3905 | + double r1 = circle1.ray(); |
3906 | + |
3907 | + /* dx and dy are the vertical and horizontal distances between |
3908 | + * the circle centers. |
3909 | + */ |
3910 | + Point D = X1 - X0; |
3911 | + |
3912 | + /* Determine the straight-line distance between the centers. */ |
3913 | + double d = L2(D); |
3914 | + |
3915 | + /* Check for solvability. */ |
3916 | + if (d > (r0 + r1)) { |
3917 | + /* no solution. circles do not intersect. */ |
3918 | + return 0; |
3919 | + } |
3920 | + if (d <= fabs(r0 - r1)) { |
3921 | + /* no solution. one circle is contained in the other */ |
3922 | + return 1; |
3923 | + } |
3924 | + |
3925 | + /* 'point 2' is the point where the line through the circle |
3926 | + * intersection points crosses the line between the circle |
3927 | + * centers. |
3928 | + */ |
3929 | + |
3930 | + /* Determine the distance from point 0 to point 2. */ |
3931 | + double a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ; |
3932 | + |
3933 | + /* Determine the coordinates of point 2. */ |
3934 | + Point p2 = X0 + D * (a/d); |
3935 | + |
3936 | + /* Determine the distance from point 2 to either of the |
3937 | + * intersection points. |
3938 | + */ |
3939 | + double h = std::sqrt((r0*r0) - (a*a)); |
3940 | + |
3941 | + /* Now determine the offsets of the intersection points from |
3942 | + * point 2. |
3943 | + */ |
3944 | + Point r = (h/d)*rot90(D); |
3945 | + |
3946 | + /* Determine the absolute intersection points. */ |
3947 | + p0 = p2 + r; |
3948 | + p1 = p2 - r; |
3949 | + |
3950 | + return 2; |
3951 | +} |
3952 | +/** |
3953 | +* Find circle that touches inside of the curve, with radius matching the curvature, at time value \c t. |
3954 | +* Because this method internally uses unitTangentAt, t should be smaller than 1.0 (see unitTangentAt). |
3955 | +*/ |
3956 | +static Circle touching_circle( D2<SBasis> const &curve, double t, double tol=0.01 ) |
3957 | +{ |
3958 | + D2<SBasis> dM=derivative(curve); |
3959 | + if ( are_near(L2sq(dM(t)),0.) ) { |
3960 | + dM=derivative(dM); |
3961 | + } |
3962 | + if ( are_near(L2sq(dM(t)),0.) ) { // try second time |
3963 | + dM=derivative(dM); |
3964 | + } |
3965 | + Piecewise<D2<SBasis> > unitv = unitVector(dM,tol); |
3966 | + Piecewise<SBasis> dMlength = dot(Piecewise<D2<SBasis> >(dM),unitv); |
3967 | + Piecewise<SBasis> k = cross(derivative(unitv),unitv); |
3968 | + k = divide(k,dMlength,tol,3); |
3969 | + double curv = k(t); // note that this value is signed |
3970 | + |
3971 | + Geom::Point normal = unitTangentAt(curve, t).cw(); |
3972 | + double radius = 1/curv; |
3973 | + Geom::Point center = curve(t) + radius*normal; |
3974 | + return Geom::Circle(center, fabs(radius)); |
3975 | +} |
3976 | + |
3977 | +std::vector<Geom::Path> split_at_cusps(const Geom::Path& in) |
3978 | +{ |
3979 | + PathVector out = PathVector(); |
3980 | + Path temp = Path(); |
3981 | + |
3982 | + for (unsigned i = 0; i < in.size(); i++) { |
3983 | + temp.append(in[i]); |
3984 | + if ( get_nodetype(in[i], in[i + 1]) != Geom::NODE_SMOOTH ) { |
3985 | + out.push_back(temp); |
3986 | + temp = Path(); |
3987 | + } |
3988 | + } |
3989 | + if (temp.size() > 0) { |
3990 | + out.push_back(temp); |
3991 | + } |
3992 | + return out; |
3993 | +} |
3994 | + |
3995 | +Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2<Geom::SBasis> const & sbasis_in) |
3996 | +{ |
3997 | + std::vector<Geom::Point> temp; |
3998 | + sbasis_to_bezier(temp, sbasis_in, 4); |
3999 | + return Geom::CubicBezier( temp ); |
4000 | +} |
4001 | + |
4002 | +static boost::optional<Geom::Point> intersection_point(Geom::Point const & origin_a, Geom::Point const & vector_a, Geom::Point const & origin_b, Geom::Point const & vector_b) |
4003 | +{ |
4004 | + Geom::Coord denom = cross(vector_b, vector_a); |
4005 | + if (!Geom::are_near(denom,0.)) { |
4006 | + Geom::Coord t = (cross(origin_a,vector_b) + cross(vector_b,origin_b)) / denom; |
4007 | + return origin_a + t * vector_a; |
4008 | + } |
4009 | + return boost::none; |
4010 | +} |
4011 | + |
4012 | +} // namespace Geom |
4013 | + |
4014 | +namespace Outline { |
4015 | + |
4016 | +typedef Geom::D2<Geom::SBasis> D2SB; |
4017 | +typedef Geom::Piecewise<D2SB> PWD2; |
4018 | + |
4019 | +// UTILITY |
4020 | + |
4021 | +unsigned bezierOrder (const Geom::Curve* curve_in) |
4022 | +{ |
4023 | + using namespace Geom; |
4024 | + if ( const BezierCurve* bz = dynamic_cast<const BezierCurve*>(curve_in) ) { |
4025 | + return bz->order(); |
4026 | + } |
4027 | + return 0; |
4028 | +} |
4029 | + |
4030 | +/** |
4031 | + * @return true if the angle formed by the curves and their handles is greater than 180 degrees clockwise, otherwise false. |
4032 | + */ |
4033 | +bool outside_angle (const Geom::Curve& cbc1, const Geom::Curve& cbc2) |
4034 | +{ |
4035 | + Geom::Point start_point; |
4036 | + Geom::Point cross_point = cbc1.finalPoint(); |
4037 | + Geom::Point end_point; |
4038 | + |
4039 | + if (cross_point != cbc2.initialPoint()) { |
4040 | + g_warning("Non-contiguous path in Outline::outside_angle()"); |
4041 | + return false; |
4042 | + } |
4043 | + |
4044 | + Geom::CubicBezier cubicBezier = Geom::sbasis_to_cubicbezier(cbc1.toSBasis()); |
4045 | + start_point = cubicBezier [2]; |
4046 | + |
4047 | + /* |
4048 | + * Because the node editor does not yet support true quadratics, paths are converted to |
4049 | + * cubic beziers in the node tool with degenerate handles on one side. |
4050 | + */ |
4051 | + |
4052 | + if (are_near(start_point, cross_point, 0.0000001)) { |
4053 | + start_point = cubicBezier [1]; |
4054 | + } |
4055 | + cubicBezier = Geom::sbasis_to_cubicbezier(cbc2.toSBasis()); |
4056 | + end_point = cubicBezier [1]; |
4057 | + if (are_near(end_point, cross_point, 0.0000001)) { |
4058 | + end_point = cubicBezier [2]; |
4059 | + } |
4060 | + |
4061 | + // got our three points, now let's see what their clockwise angle is |
4062 | + |
4063 | + // Definition of a Graham scan |
4064 | + |
4065 | + /******************************************************************** |
4066 | + # Three points are a counter-clockwise turn if ccw > 0, clockwise if |
4067 | + # ccw < 0, and collinear if ccw = 0 because ccw is a determinant that |
4068 | + # gives the signed area of the triangle formed by p1, p2 and p3. |
4069 | + function ccw(p1, p2, p3): |
4070 | + return (p2.x - p1.x)*(p3.y - p1.y) - (p2.y - p1.y)*(p3.x - p1.x) |
4071 | + *********************************************************************/ |
4072 | + |
4073 | + double ccw = ( (cross_point.x() - start_point.x()) * (end_point.y() - start_point.y()) ) - |
4074 | + ( (cross_point.y() - start_point.y()) * (end_point.x() - start_point.x()) ); |
4075 | + return ccw > 0; |
4076 | +} |
4077 | + |
4078 | +// LINE JOINS |
4079 | + |
4080 | +typedef Geom::BezierCurveN<1u> BezierLine; |
4081 | + |
4082 | +/** |
4083 | + * Removes the crossings on an interior join. |
4084 | + * @param path_builder Contains the incoming segment; result is appended to this |
4085 | + * @param outgoing The outgoing segment |
4086 | + */ |
4087 | +void joinInside(Geom::Path& path_builder, Geom::Curve const& outgoing) { |
4088 | + Geom::Curve const& incoming = path_builder.back(); |
4089 | + |
4090 | + // Using Geom::crossings to find intersections between two curves |
4091 | + Geom::Crossings cross = Geom::crossings(incoming, outgoing); |
4092 | + if (!cross.empty()) { |
4093 | + // Crossings found, create the join |
4094 | + Geom::CubicBezier cubic = Geom::sbasis_to_cubicbezier(incoming.toSBasis()); |
4095 | + cubic = cubic.subdivide(cross[0].ta).first; |
4096 | + // erase the last segment, as we're going to overwrite it now |
4097 | + path_builder.erase_last(); |
4098 | + path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS); |
4099 | + |
4100 | + cubic = Geom::sbasis_to_cubicbezier(outgoing.toSBasis()); |
4101 | + cubic = cubic.subdivide(cross[0].tb).second; |
4102 | + path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS); |
4103 | + } else { |
4104 | + // No crossings occurred, or Geom::crossings() failed; default to bevel |
4105 | + if (Geom::are_near(incoming.finalPoint(), outgoing.initialPoint())) { |
4106 | + path_builder.appendNew<BezierLine>(outgoing.initialPoint()); |
4107 | + } else { |
4108 | + path_builder.setFinal(outgoing.initialPoint()); |
4109 | + } |
4110 | + } |
4111 | +} |
4112 | + |
4113 | +/** |
4114 | + * Try to create a miter join. Falls back to bevel if no miter can be created. |
4115 | + * @param path_builder Path to append curves to; back() is the incoming curve |
4116 | + * @param outgoing Outgoing curve. |
4117 | + * @param miter_limit When mitering, don't exceed this length |
4118 | + * @param line_width The thickness of the line. |
4119 | + */ |
4120 | +void miter_curves(Geom::Path& path_builder, Geom::Curve const& outgoing, double miter_limit, double line_width) { |
4121 | + using namespace Geom; |
4122 | + Curve const& incoming = path_builder.back(); |
4123 | + Point tang1 = unitTangentAt(Geom::reverse(incoming.toSBasis()), 0.); |
4124 | + Point tang2 = unitTangentAt(outgoing.toSBasis(), 0); |
4125 | + |
4126 | + boost::optional <Point> p = intersection_point (incoming.finalPoint(), tang1, outgoing.initialPoint(), tang2); |
4127 | + if (p) { |
4128 | + // check size of miter |
4129 | + Point point_on_path = incoming.finalPoint() - rot90(tang1) * line_width; |
4130 | + Coord len = distance(*p, point_on_path); |
4131 | + if (len <= miter_limit) { |
4132 | + // miter OK |
4133 | + path_builder.appendNew<BezierLine>(*p); |
4134 | + } |
4135 | + } |
4136 | + path_builder.appendNew<BezierLine>(outgoing.initialPoint()); |
4137 | +} |
4138 | + |
4139 | +/** |
4140 | + * Smoothly extrapolate curves along a circular route. Falls back to miter if necessary. |
4141 | + * @param path_builder Path to append curves to; back() is the incoming curve |
4142 | + * @param outgoing Outgoing curve. |
4143 | + * @param miter_limit When mitering, don't exceed this length |
4144 | + * @param line_width The thickness of the line. Used for miter fallback. |
4145 | + */ |
4146 | +void extrapolate_curves(Geom::Path& path_builder, Geom::Curve const& outgoing, double miter_limit, double line_width) { |
4147 | + Geom::Curve const& incoming = path_builder.back(); |
4148 | + Geom::Point endPt = outgoing.initialPoint(); |
4149 | + |
4150 | + // The method used when extrapolating curves fails to work when either side of the join to be extrapolated |
4151 | + // is a line segment. When this situation is encountered, fall back to a regular miter join. |
4152 | + bool lineProblem = (dynamic_cast<const BezierLine *>(&incoming)) || (dynamic_cast<const BezierLine *>(&outgoing)); |
4153 | + if (lineProblem == false) { |
4154 | + // Geom::Point tang1 = Geom::unitTangentAt(Geom::reverse(incoming.toSBasis()), 0.); |
4155 | + Geom::Point tang2 = Geom::unitTangentAt(outgoing.toSBasis(), 0); |
4156 | + |
4157 | + Geom::Circle circle1 = Geom::touching_circle(Geom::reverse(incoming.toSBasis()), 0.); |
4158 | + Geom::Circle circle2 = Geom::touching_circle(outgoing.toSBasis(), 0); |
4159 | + |
4160 | + Geom::Point points[2]; |
4161 | + int solutions = Geom::circle_circle_intersection(circle1, circle2, points[0], points[1]); |
4162 | + if (solutions == 2) { |
4163 | + Geom::Point sol(0,0); |
4164 | + if ( dot(tang2,points[0]-endPt) > 0 ) { |
4165 | + // points[0] is bad, choose points[1] |
4166 | + sol = points[1]; |
4167 | + } else if ( dot(tang2,points[1]-endPt) > 0 ) { // points[0] could be good, now check points[1] |
4168 | + // points[1] is bad, choose points[0] |
4169 | + sol = points[0]; |
4170 | + } else { |
4171 | + // both points are good, choose nearest |
4172 | + sol = ( distanceSq(endPt, points[0]) < distanceSq(endPt, points[1]) ) ? points[0] : points[1]; |
4173 | + } |
4174 | + |
4175 | + Geom::EllipticalArc *arc0 = circle1.arc(incoming.finalPoint(), 0.5*(incoming.finalPoint()+sol), sol, true); |
4176 | + Geom::EllipticalArc *arc1 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true); |
4177 | + try { |
4178 | + if (arc0) { |
4179 | + path_builder.append (arc0->toSBasis()); |
4180 | + delete arc0; |
4181 | + arc0 = NULL; |
4182 | + } else { |
4183 | + throw std::exception(); |
4184 | + } |
4185 | + |
4186 | + if (arc1) { |
4187 | + path_builder.append (arc1->toSBasis()); |
4188 | + delete arc1; |
4189 | + arc1 = NULL; |
4190 | + } else { |
4191 | + throw std::exception(); |
4192 | + } |
4193 | + |
4194 | + } catch (std::exception const & ex) { |
4195 | + g_warning("Error extrapolating line join: %s\n", ex.what()); |
4196 | + path_builder.appendNew<Geom::LineSegment>(endPt); |
4197 | + } |
4198 | + } else { |
4199 | + // 1 or no solutions found, default to miter |
4200 | + miter_curves(path_builder, outgoing, miter_limit, line_width); |
4201 | + } |
4202 | + } else { |
4203 | + // Line segments exist |
4204 | + miter_curves(path_builder, outgoing, miter_limit, line_width); |
4205 | + } |
4206 | +} |
4207 | + |
4208 | +/** |
4209 | + * Extrapolate curves by reflecting them along the line that would be given by beveling the join. |
4210 | + * @param path_builder Path to append curves to; back() is the incoming curve |
4211 | + * @param outgoing Outgoing curve. |
4212 | + * @param miter_limit When mitering, don't exceed this length |
4213 | + * @param line_width The thickness of the line. Used for miter fallback. |
4214 | + */ |
4215 | +void reflect_curves(Geom::Path& path_builder, Geom::Curve const& outgoing, double miter_limit, double line_width) |
4216 | +{ |
4217 | + using namespace Geom; |
4218 | + Curve const& incoming = path_builder.back(); |
4219 | + // On the outside, we'll take the incoming curve, the outgoing curve, and |
4220 | + // reflect them over the line formed by taking the unit tangent vector at times |
4221 | + // 0 and 1, respectively, rotated by 90 degrees. |
4222 | + Crossings cross; |
4223 | + |
4224 | + // reflect curves along the line that would be given by beveling the join |
4225 | + Point tang1 = unitTangentAt(reverse(incoming.toSBasis()), 0.); |
4226 | + D2SB newcurve1 = incoming.toSBasis() * reflection(-rot90(tang1), incoming.finalPoint()); |
4227 | + CubicBezier bzr1 = sbasis_to_cubicbezier(reverse(newcurve1)); |
4228 | + |
4229 | + Point tang2 = Geom::unitTangentAt(outgoing.toSBasis(), 0.); |
4230 | + D2SB newcurve2 = outgoing.toSBasis() * reflection(-rot90(tang2), outgoing.initialPoint()); |
4231 | + CubicBezier bzr2 = sbasis_to_cubicbezier(reverse(newcurve2)); |
4232 | + |
4233 | + cross = crossings(bzr1, bzr2); |
4234 | + if (cross.empty()) { |
4235 | + // paths don't cross, fall back to miter |
4236 | + miter_curves(path_builder, outgoing, miter_limit, line_width); |
4237 | + } else { |
4238 | + // reflected join |
4239 | + std::pair<CubicBezier, CubicBezier> sub1 = bzr1.subdivide(cross[0].ta); |
4240 | + std::pair<CubicBezier, CubicBezier> sub2 = bzr2.subdivide(cross[0].tb); |
4241 | + |
4242 | + // TODO it seems as if a bug in 2geom sometimes doesn't catch the first |
4243 | + // crossing of paths, but the second instead; but only sometimes. |
4244 | + path_builder.appendNew <CubicBezier> (sub1.first[1], sub1.first[2], sub2.second[0]); |
4245 | + path_builder.appendNew <CubicBezier> (sub2.second[1], sub2.second[2], outgoing.initialPoint()); |
4246 | + } |
4247 | +} |
4248 | + |
4249 | +// Ideal function pointer we want to pass |
4250 | +typedef void JoinFunc(Geom::Path& /*path_builder*/, Geom::Curve const& /*outgoing*/, double /*miter_limit*/, double /*line_width*/); |
4251 | + |
4252 | +/** |
4253 | + * Helper function for repeated logic in outlineHalf. |
4254 | + */ |
4255 | +static void outlineHelper(Geom::Path& path_builder, Geom::PathVector* path_vec, bool outside, double width, double miter, JoinFunc func) { |
4256 | + Geom::Curve * cbc2 = path_vec->front()[0].duplicate(); |
4257 | + |
4258 | + if (outside) { |
4259 | + func(path_builder, *cbc2, miter, width); |
4260 | + } else { |
4261 | + joinInside(path_builder, *cbc2); |
4262 | + } |
4263 | + |
4264 | + // store it |
4265 | + Geom::Path temp_path = path_vec->front(); |
4266 | + if (!outside) { |
4267 | + // erase the first segment since the inside join code already appended it |
4268 | + temp_path.erase(temp_path.begin()); |
4269 | + } |
4270 | + |
4271 | + if (temp_path.initialPoint() != path_builder.finalPoint()) { |
4272 | + temp_path.setInitial(path_builder.finalPoint()); |
4273 | + } |
4274 | + |
4275 | + path_builder.append(temp_path); |
4276 | + |
4277 | + delete cbc2; |
4278 | +} |
4279 | + |
4280 | +/** |
4281 | + * Offsets exactly one half of a bezier spline (path). |
4282 | + * @param path_in The input path to use. (To create the other side use path_in.reverse() ) |
4283 | + * @param line_width the line width to use (usually you want to divide this by 2) |
4284 | + * @param miter_limit the miter parameter |
4285 | + * @param func Join function to apply at each join. |
4286 | + */ |
4287 | + |
4288 | +Geom::Path outlineHalf(const Geom::Path& path_in, double line_width, double miter_limit, JoinFunc func) { |
4289 | + // NOTE: it is important to notice the distinction between a Geom::Path and a livarot ::Path here! |
4290 | + // if you do not see "Geom::" there is a different function set! |
4291 | + |
4292 | + Geom::PathVector pv = split_at_cusps(path_in); |
4293 | + |
4294 | + ::Path to_outline; |
4295 | + ::Path outlined_result; |
4296 | + |
4297 | + Geom::Path path_builder = Geom::Path(); // the path to store the result in |
4298 | + Geom::PathVector* path_vec; // needed because livarot returns a pointer (TODO make this not a pointer) |
4299 | + |
4300 | + // Do two curves at a time for efficiency, since the join function needs to know the outgoing curve as well |
4301 | + const size_t k = pv.size(); |
4302 | + for (size_t u = 0; u < k; u += 2) { |
4303 | + to_outline = Path(); |
4304 | + outlined_result = Path(); |
4305 | + |
4306 | + to_outline.LoadPath(pv[u], Geom::identity(), false, false); |
4307 | + to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10); |
4308 | + // now a curve has been outside outlined and loaded into outlined_result |
4309 | + |
4310 | + // get the Geom::Path |
4311 | + path_vec = outlined_result.MakePathVector(); |
4312 | + |
4313 | + // on the first run through, there is no join |
4314 | + if (u == 0) { |
4315 | + path_builder.start(path_vec->front().initialPoint()); |
4316 | + path_builder.append(path_vec->front()); |
4317 | + } else { |
4318 | + outlineHelper(path_builder, path_vec, outside_angle(pv[u-1][pv[u-1].size()-1], pv[u][0]), line_width, miter_limit, func); |
4319 | + } |
4320 | + |
4321 | + // outline the next segment, but don't store it yet |
4322 | + if (path_vec) |
4323 | + delete path_vec; |
4324 | + path_vec = NULL; |
4325 | + |
4326 | + // odd number of paths |
4327 | + if (u < k - 1) { |
4328 | + outlined_result = Path(); |
4329 | + to_outline = Path(); |
4330 | + |
4331 | + to_outline.LoadPath(pv[u+1], Geom::Affine(), false, false); |
4332 | + to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10); |
4333 | + |
4334 | + path_vec = outlined_result.MakePathVector(); |
4335 | + outlineHelper(path_builder, path_vec, outside_angle(pv[u][pv[u].size()-1], pv[u+1][0]), line_width, miter_limit, func); |
4336 | + |
4337 | + if (path_vec) |
4338 | + delete path_vec; |
4339 | + path_vec = NULL; |
4340 | + } |
4341 | + } |
4342 | + |
4343 | + if (path_in.closed()) { |
4344 | + Geom::Curve * cbc1; |
4345 | + Geom::Curve * cbc2; |
4346 | + |
4347 | + if ( path_in[path_in.size()].isDegenerate() ) { |
4348 | + // handle case for last segment curved |
4349 | + outlined_result = Path(); |
4350 | + to_outline = Path(); |
4351 | + |
4352 | + Geom::Path oneCurve; oneCurve.append(path_in[0]); |
4353 | + |
4354 | + to_outline.LoadPath(oneCurve, Geom::Affine(), false, false); |
4355 | + to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10); |
4356 | + |
4357 | + path_vec = outlined_result.MakePathVector(); |
4358 | + |
4359 | + cbc1 = path_builder[path_builder.size() - 1].duplicate(); |
4360 | + cbc2 = path_vec->front()[0].duplicate(); |
4361 | + |
4362 | + delete path_vec; |
4363 | + } else { |
4364 | + // handle case for last segment straight |
4365 | + // since the path doesn't actually give us access to it, we'll do it ourselves |
4366 | + outlined_result = Path(); |
4367 | + to_outline = Path(); |
4368 | + |
4369 | + Geom::Path oneCurve; oneCurve.append(Geom::LineSegment(path_in.finalPoint(), path_in.initialPoint())); |
4370 | + |
4371 | + to_outline.LoadPath(oneCurve, Geom::Affine(), false, false); |
4372 | + to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10); |
4373 | + |
4374 | + path_vec = outlined_result.MakePathVector(); |
4375 | + |
4376 | + cbc1 = path_builder[path_builder.size() - 1].duplicate(); |
4377 | + cbc2 = (*path_vec)[0] [0].duplicate(); |
4378 | + |
4379 | + outlineHelper(path_builder, path_vec, outside_angle(path_in[path_in.size()-1], oneCurve[0]), line_width, miter_limit, func); |
4380 | + |
4381 | + delete cbc1; |
4382 | + cbc1 = cbc2->duplicate(); |
4383 | + delete path_vec; |
4384 | + |
4385 | + oneCurve = Geom::Path(); oneCurve.append(path_in[0]); |
4386 | + |
4387 | + to_outline.LoadPath(oneCurve, Geom::Affine(), false, false); |
4388 | + to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10); |
4389 | + |
4390 | + path_vec = outlined_result.MakePathVector(); |
4391 | + delete cbc2; cbc2 = (*path_vec)[0] [0].duplicate(); |
4392 | + delete path_vec; |
4393 | + } |
4394 | + |
4395 | + Geom::Path temporary; |
4396 | + temporary.append(*cbc1); |
4397 | + |
4398 | + Geom::Curve const & prev_curve = path_in[path_in.size()].isDegenerate() ? path_in[path_in.size() - 1] : path_in[path_in.size()]; |
4399 | + Geom::Path isStraight; |
4400 | + isStraight.append(prev_curve); |
4401 | + isStraight.append(path_in[0]); |
4402 | + // does closing path require a join? |
4403 | + if (Geom::split_at_cusps(isStraight).size() > 1) { |
4404 | + bool outside = outside_angle(prev_curve, path_in[0]); |
4405 | + if (outside) { |
4406 | + func(temporary, *cbc2, miter_limit, line_width); |
4407 | + } else { |
4408 | + joinInside(temporary, *cbc2); |
4409 | + path_builder.erase(path_builder.begin()); |
4410 | + } |
4411 | + |
4412 | + // extract the appended curves |
4413 | + path_builder.erase_last(); |
4414 | + if (Geom::are_near(path_builder.finalPoint(), temporary.initialPoint())) { |
4415 | + path_builder.setFinal(temporary.initialPoint()); |
4416 | + } else { |
4417 | + path_builder.appendNew<BezierLine>(temporary.initialPoint()); |
4418 | + } |
4419 | + path_builder.append(temporary); |
4420 | + } else { |
4421 | + // closing path does not require a join |
4422 | + path_builder.setFinal(path_builder.initialPoint()); |
4423 | + } |
4424 | + path_builder.close(); |
4425 | + |
4426 | + if (cbc1) delete cbc1; |
4427 | + if (cbc2) delete cbc2; |
4428 | + } |
4429 | + |
4430 | + return path_builder; |
4431 | +} |
4432 | + |
4433 | +Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, LineJoinType join, ButtTypeMod butt, double miter_lim, bool extrapolate, double start_lean, double end_lean) |
4434 | +{ |
4435 | + Geom::PathVector path_out; |
4436 | + |
4437 | + unsigned pv_size = path_in.size(); |
4438 | + for (unsigned i = 0; i < pv_size; i++) { |
4439 | + |
4440 | + if (path_in[i].size() > 1) { |
4441 | + Geom::Path with_direction; |
4442 | + Geom::Path against_direction; |
4443 | + |
4444 | + with_direction = Outline::outlineHalf(path_in[i], -line_width, miter_lim, extrapolate ? extrapolate_curves : reflect_curves); |
4445 | + against_direction = Outline::outlineHalf(path_in[i].reverse(), -line_width, miter_lim, extrapolate ? extrapolate_curves : reflect_curves); |
4446 | + |
4447 | + Geom::PathBuilder pb; |
4448 | + |
4449 | + pb.moveTo(with_direction.initialPoint()); |
4450 | + pb.append(with_direction); |
4451 | + |
4452 | + //add in our line caps |
4453 | + if (!path_in[i].closed()) { |
4454 | + switch (butt) { |
4455 | + case BUTT_STRAIGHT: |
4456 | + pb.lineTo(against_direction.initialPoint()); |
4457 | + break; |
4458 | + case BUTT_ROUND: |
4459 | + pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, against_direction.initialPoint() ); |
4460 | + break; |
4461 | + case BUTT_POINTY: { |
4462 | + Geom::Point end_deriv = -Geom::unitTangentAt(Geom::reverse(path_in[i].back().toSBasis()), 0.); |
4463 | + double radius = 0.5 * Geom::distance(with_direction.finalPoint(), against_direction.initialPoint()); |
4464 | + Geom::Point midpoint = 0.5 * (with_direction.finalPoint() + against_direction.initialPoint()) + radius*end_deriv; |
4465 | + pb.lineTo(midpoint); |
4466 | + pb.lineTo(against_direction.initialPoint()); |
4467 | + break; |
4468 | + } |
4469 | + case BUTT_SQUARE: { |
4470 | + Geom::Point end_deriv = -Geom::unitTangentAt(Geom::reverse(path_in[i].back().toSBasis()), 0.); |
4471 | + double radius = 0.5 * Geom::distance(with_direction.finalPoint(), against_direction.initialPoint()); |
4472 | + pb.lineTo(with_direction.finalPoint() + radius*end_deriv); |
4473 | + pb.lineTo(against_direction.initialPoint() + radius*end_deriv); |
4474 | + pb.lineTo(against_direction.initialPoint()); |
4475 | + break; |
4476 | + } |
4477 | + case BUTT_LEANED: { |
4478 | + Geom::Point end_deriv = -Geom::unitTangentAt(Geom::reverse(path_in[i].back().toSBasis()), 0.); |
4479 | + double maxRadius = (end_lean+0.5) * Geom::distance(with_direction.finalPoint(), against_direction.initialPoint()); |
4480 | + double minRadius = ((end_lean*-1)+0.5) * Geom::distance(with_direction.finalPoint(), against_direction.initialPoint()); |
4481 | + pb.lineTo(with_direction.finalPoint() + maxRadius*end_deriv); |
4482 | + pb.lineTo(against_direction.initialPoint() + minRadius*end_deriv); |
4483 | + pb.lineTo(against_direction.initialPoint()); |
4484 | + break; |
4485 | + } |
4486 | + } |
4487 | + } else { |
4488 | + pb.moveTo(against_direction.initialPoint()); |
4489 | + } |
4490 | + |
4491 | + pb.append(against_direction); |
4492 | + |
4493 | + //cap (if necessary) |
4494 | + if (!path_in[i].closed()) { |
4495 | + switch (butt) { |
4496 | + case BUTT_STRAIGHT: |
4497 | + pb.lineTo(with_direction.initialPoint()); |
4498 | + break; |
4499 | + case BUTT_ROUND: |
4500 | + pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, with_direction.initialPoint() ); |
4501 | + break; |
4502 | + case BUTT_POINTY: { |
4503 | + Geom::Point end_deriv = -Geom::unitTangentAt(path_in[i].front().toSBasis(), 0.); |
4504 | + double radius = 0.5 * Geom::distance(against_direction.finalPoint(), with_direction.initialPoint()); |
4505 | + Geom::Point midpoint = 0.5 * (against_direction.finalPoint() + with_direction.initialPoint()) + radius*end_deriv; |
4506 | + pb.lineTo(midpoint); |
4507 | + pb.lineTo(with_direction.initialPoint()); |
4508 | + break; |
4509 | + } |
4510 | + case BUTT_SQUARE: { |
4511 | + Geom::Point end_deriv = -Geom::unitTangentAt(path_in[i].front().toSBasis(), 0.); |
4512 | + double radius = 0.5 * Geom::distance(against_direction.finalPoint(), with_direction.initialPoint()); |
4513 | + pb.lineTo(against_direction.finalPoint() + radius*end_deriv); |
4514 | + pb.lineTo(with_direction.initialPoint() + radius*end_deriv); |
4515 | + pb.lineTo(with_direction.initialPoint()); |
4516 | + break; |
4517 | + } |
4518 | + case BUTT_LEANED: { |
4519 | + Geom::Point end_deriv = -Geom::unitTangentAt(path_in[i].front().toSBasis(), 0.); |
4520 | + double maxRadius = (start_lean+0.5) * Geom::distance(against_direction.finalPoint(), with_direction.initialPoint()); |
4521 | + double minRadius = ((start_lean*-1)+0.5) * Geom::distance(against_direction.finalPoint(), with_direction.initialPoint()); |
4522 | + pb.lineTo(against_direction.finalPoint() + minRadius*end_deriv); |
4523 | + pb.lineTo(with_direction.initialPoint() + maxRadius*end_deriv); |
4524 | + pb.lineTo(with_direction.initialPoint()); |
4525 | + break; |
4526 | + } |
4527 | + } |
4528 | + } |
4529 | + pb.flush(); |
4530 | + path_out.push_back(pb.peek()[0]); |
4531 | + if (path_in[i].closed()) { |
4532 | + path_out.push_back(pb.peek()[1]); |
4533 | + } |
4534 | + } else { |
4535 | + Path p = Path(); |
4536 | + Path outlinepath = Path(); |
4537 | + ButtType original_butt; |
4538 | + switch (butt) { |
4539 | + case BUTT_STRAIGHT: |
4540 | + original_butt = butt_straight; |
4541 | + break; |
4542 | + case BUTT_ROUND: |
4543 | + original_butt = butt_round; |
4544 | + break; |
4545 | + case butt_pointy: { |
4546 | + original_butt = butt_pointy; |
4547 | + break; |
4548 | + } |
4549 | + case BUTT_SQUARE: { |
4550 | + original_butt = butt_square; |
4551 | + break; |
4552 | + } |
4553 | + case BUTT_LEANED: { |
4554 | + original_butt = butt_straight; |
4555 | + break; |
4556 | + } |
4557 | + } |
4558 | + p.LoadPath(path_in[i], Geom::Affine(), false, false); |
4559 | + p.Outline(&outlinepath, line_width / 2, static_cast<join_typ>(join), original_butt, miter_lim); |
4560 | + Geom::PathVector *pv_p = outlinepath.MakePathVector(); |
4561 | + //somewhat hack-ish |
4562 | + path_out.push_back( (*pv_p)[0].reverse() ); |
4563 | + if (pv_p) delete pv_p; |
4564 | + } |
4565 | + } |
4566 | + return path_out; |
4567 | +} |
4568 | + |
4569 | +Geom::PathVector PathVectorOutline(Geom::PathVector const & path_in, double line_width, ButtTypeMod linecap_type, LineJoinType linejoin_type, double miter_limit, double start_lean, double end_lean) |
4570 | +{ |
4571 | + std::vector<Geom::Path> path_out = std::vector<Geom::Path>(); |
4572 | + if (path_in.empty()) { |
4573 | + return path_out; |
4574 | + } |
4575 | + Path p = Path(); |
4576 | + Path outlinepath = Path(); |
4577 | + for (unsigned i = 0; i < path_in.size(); i++) { |
4578 | + p.LoadPath(path_in[i], Geom::Affine(), false, ( (i==0) ? false : true)); |
4579 | + } |
4580 | + |
4581 | +#define miter_lim fabs(line_width * miter_limit) |
4582 | + |
4583 | + //magic! |
4584 | + ButtType original_butt; |
4585 | + switch (linecap_type) { |
4586 | + case BUTT_STRAIGHT: |
4587 | + original_butt = butt_straight; |
4588 | + break; |
4589 | + case BUTT_ROUND: |
4590 | + original_butt = butt_round; |
4591 | + break; |
4592 | + case butt_pointy: { |
4593 | + original_butt = butt_pointy; |
4594 | + break; |
4595 | + } |
4596 | + case BUTT_SQUARE: { |
4597 | + original_butt = butt_square; |
4598 | + break; |
4599 | + } |
4600 | + case BUTT_LEANED: { |
4601 | + original_butt = butt_straight; |
4602 | + break; |
4603 | + } |
4604 | + } |
4605 | + if (linejoin_type <= LINEJOIN_POINTY) { |
4606 | + p.Outline(&outlinepath, line_width / 2, static_cast<join_typ>(linejoin_type), |
4607 | + original_butt, miter_lim); |
4608 | + // fix memory leak |
4609 | + std::vector<Geom::Path> *pv_p = outlinepath.MakePathVector(); |
4610 | + path_out = *pv_p; |
4611 | + delete pv_p; |
4612 | + |
4613 | + } else if (linejoin_type == LINEJOIN_REFLECTED) { |
4614 | + // reflected arc join |
4615 | + path_out = outlinePath(path_in, line_width, static_cast<LineJoinType>(linejoin_type), |
4616 | + linecap_type , miter_lim, false, start_lean, end_lean); |
4617 | + |
4618 | + } else if (linejoin_type == LINEJOIN_EXTRAPOLATED) { |
4619 | + // extrapolated arc join |
4620 | + path_out = outlinePath(path_in, line_width, LINEJOIN_STRAIGHT, linecap_type, miter_lim, true, start_lean, end_lean); |
4621 | + } |
4622 | + |
4623 | +#undef miter_lim |
4624 | + return path_out; |
4625 | +} |
4626 | + |
4627 | +Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, LineJoinType linejoin_type, double miter_limit) |
4628 | +{ |
4629 | + |
4630 | +#define miter_lim fabs(line_width * miter_limit) |
4631 | + |
4632 | + Geom::Path path_out; |
4633 | + |
4634 | + if (linejoin_type <= LINEJOIN_POINTY || path_in.size() <= 1) { |
4635 | + |
4636 | + Geom::PathVector * pathvec; |
4637 | + |
4638 | + Path path_tangent = Path(); |
4639 | + Path path_outline = Path(); |
4640 | + path_outline.LoadPath(path_in, Geom::Affine(), false, false); |
4641 | + path_outline.OutsideOutline(&path_tangent, line_width / 2, static_cast<join_typ>(linejoin_type), butt_straight, miter_lim); |
4642 | + |
4643 | + pathvec = path_tangent.MakePathVector(); |
4644 | + path_out = pathvec->front(); |
4645 | + delete pathvec; |
4646 | + return path_out; |
4647 | + } else if (linejoin_type == LINEJOIN_REFLECTED) { |
4648 | + path_out = outlineHalf(path_in, line_width, miter_lim, reflect_curves); |
4649 | + return path_out; |
4650 | + } else if (linejoin_type == LINEJOIN_EXTRAPOLATED) { |
4651 | + path_out = outlineHalf(path_in, line_width, miter_lim, extrapolate_curves); |
4652 | + return path_out; |
4653 | + } |
4654 | +#undef miter_lim |
4655 | + return path_out; |
4656 | +} |
4657 | + |
4658 | +} // namespace Outline |
4659 | + |
4660 | +/* |
4661 | + Local Variables: |
4662 | + mode:c++ |
4663 | + c-file-style:"stroustrup" |
4664 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
4665 | + indent-tabs-mode:nil |
4666 | + fill-column:99 |
4667 | + End: |
4668 | +*/ |
4669 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8 : |
4670 | |
4671 | === added file 'src/live_effects/pathoutlineprovider.h' |
4672 | --- src/live_effects/pathoutlineprovider.h 1970-01-01 00:00:00 +0000 |
4673 | +++ src/live_effects/pathoutlineprovider.h 2014-09-07 17:02:32 +0000 |
4674 | @@ -0,0 +1,55 @@ |
4675 | +#ifndef SEEN_PATH_OUTLINE_H |
4676 | +#define SEEN_PATH_OUTLINE_H |
4677 | + |
4678 | +/* Author: |
4679 | + * Liam P. White <inkscapebrony@gmail.com> |
4680 | + * |
4681 | + * Copyright (C) 2014 Author |
4682 | + * |
4683 | + * Released under GNU GPL, read the file 'COPYING' for more information |
4684 | + */ |
4685 | + |
4686 | +#include <livarot/Path.h> |
4687 | +#include <livarot/LivarotDefs.h> |
4688 | + |
4689 | +enum LineJoinType { |
4690 | + LINEJOIN_STRAIGHT, |
4691 | + LINEJOIN_ROUND, |
4692 | + LINEJOIN_POINTY, |
4693 | + LINEJOIN_REFLECTED, |
4694 | + LINEJOIN_EXTRAPOLATED |
4695 | +}; |
4696 | +enum ButtTypeMod { |
4697 | + BUTT_STRAIGHT, |
4698 | + BUTT_ROUND, |
4699 | + BUTT_SQUARE, |
4700 | + BUTT_POINTY, |
4701 | + BUTT_LEANED |
4702 | +}; |
4703 | + |
4704 | +namespace Geom |
4705 | +{ |
4706 | + Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2<Geom::SBasis> const & sbasis_in); |
4707 | + std::vector<Geom::Path> split_at_cusps(const Geom::Path& in); |
4708 | +} |
4709 | + |
4710 | +namespace Outline |
4711 | +{ |
4712 | + unsigned bezierOrder (const Geom::Curve* curve_in); |
4713 | + std::vector<Geom::Path> PathVectorOutline(std::vector<Geom::Path> const & path_in, double line_width, ButtTypeMod linecap_type, |
4714 | + LineJoinType linejoin_type, double miter_limit, double start_lean = 0, double end_lean = 0); |
4715 | + Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, LineJoinType linejoin_type, double miter_limit); |
4716 | +} |
4717 | + |
4718 | +#endif // SEEN_PATH_OUTLINE_H |
4719 | + |
4720 | +/* |
4721 | + Local Variables: |
4722 | + mode:c++ |
4723 | + c-file-style:"stroustrup" |
4724 | + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) |
4725 | + indent-tabs-mode:nil |
4726 | + fill-column:99 |
4727 | + End: |
4728 | +*/ |
4729 | +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 : |
4730 | |
4731 | === modified file 'src/menus-skeleton.h' |
4732 | --- src/menus-skeleton.h 2014-03-30 22:08:13 +0000 |
4733 | +++ src/menus-skeleton.h 2014-09-07 17:02:32 +0000 |
4734 | @@ -178,6 +178,9 @@ |
4735 | " <verb verb-id=\"DialogLayers\" />\n" |
4736 | " </submenu>\n" |
4737 | " <submenu name=\"" N_("_Object") "\">\n" |
4738 | +" <verb verb-id=\"DialogObjects\" />\n" |
4739 | +" <verb verb-id=\"DialogTags\" />\n" |
4740 | +" <separator/>\n" |
4741 | " <verb verb-id=\"DialogFillStroke\" />\n" |
4742 | " <verb verb-id=\"DialogObjectProperties\" />\n" |
4743 | " <verb verb-id=\"DialogSymbols\" />\n" |
4744 | |
4745 | === modified file 'src/path-chemistry.cpp' |
4746 | --- src/path-chemistry.cpp 2014-03-27 01:33:44 +0000 |
4747 | +++ src/path-chemistry.cpp 2014-09-07 17:02:32 +0000 |
4748 | @@ -22,6 +22,7 @@ |
4749 | #include "xml/repr.h" |
4750 | #include "svg/svg.h" |
4751 | #include "display/curve.h" |
4752 | +#include "color.h" |
4753 | #include <glib.h> |
4754 | #include <glibmm/i18n.h> |
4755 | #include "sp-path.h" |
4756 | @@ -433,6 +434,10 @@ |
4757 | gchar *title = item->title(); |
4758 | // remember description |
4759 | gchar *desc = item->desc(); |
4760 | + // remember highlight color |
4761 | + guint32 highlight_color = 0; |
4762 | + if (item->isHighlightSet()) |
4763 | + highlight_color = item->highlight_color(); |
4764 | |
4765 | // It's going to resurrect, so we delete without notifying listeners. |
4766 | item->deleteObject(false); |
4767 | @@ -450,6 +455,9 @@ |
4768 | newObj->setDesc(desc); |
4769 | g_free(desc); |
4770 | } |
4771 | + if (highlight_color && newObj) { |
4772 | + SP_ITEM(newObj)->setHighlightColor( highlight_color ); |
4773 | + } |
4774 | |
4775 | // move to the saved position |
4776 | repr->setPosition(pos > 0 ? pos : 0); |
4777 | |
4778 | === modified file 'src/selection-chemistry.cpp' |
4779 | --- src/selection-chemistry.cpp 2014-08-17 14:46:20 +0000 |
4780 | +++ src/selection-chemistry.cpp 2014-09-07 17:02:32 +0000 |
4781 | @@ -2792,54 +2792,53 @@ |
4782 | if (desktop == NULL) { |
4783 | return; |
4784 | } |
4785 | - |
4786 | - Inkscape::Selection *selection = sp_desktop_selection(desktop); |
4787 | - SPItem *item = selection->singleItem(); |
4788 | - if (g_slist_length(const_cast<GSList *>(selection->itemList())) != 1 || !item) { |
4789 | - desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>one</b> path to clone.")); |
4790 | - return; |
4791 | - } |
4792 | - if ( !(SP_IS_SHAPE(item) || SP_IS_TEXT(item)) ) { |
4793 | - desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select one <b>path</b> to clone.")); |
4794 | - return; |
4795 | - } |
4796 | - |
4797 | - Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc(); |
4798 | - Inkscape::XML::Node *parent = item->getRepr()->parent(); |
4799 | - |
4800 | - // create the LPE |
4801 | - Inkscape::XML::Node *lpe_repr = xml_doc->createElement("inkscape:path-effect"); |
4802 | - { |
4803 | - lpe_repr->setAttribute("effect", "clone_original"); |
4804 | - gchar *href = g_strdup_printf("#%s", item->getRepr()->attribute("id")); |
4805 | - lpe_repr->setAttribute("linkedpath", href); |
4806 | - g_free(href); |
4807 | - desktop->doc()->getDefs()->getRepr()->addChild(lpe_repr, NULL); // adds to <defs> and assigns the 'id' attribute |
4808 | - } |
4809 | - const gchar * lpe_id = lpe_repr->attribute("id"); |
4810 | - Inkscape::GC::release(lpe_repr); |
4811 | - |
4812 | - // create the new path |
4813 | - Inkscape::XML::Node *clone = xml_doc->createElement("svg:path"); |
4814 | - { |
4815 | - clone->setAttribute("d", "M 0 0", false); |
4816 | - // add the new clone to the top of the original's parent |
4817 | - parent->appendChild(clone); |
4818 | - SPObject *clone_obj = desktop->doc()->getObjectById(clone->attribute("id")); |
4819 | - if (SP_IS_LPE_ITEM(clone_obj)) { |
4820 | - gchar *href = g_strdup_printf("#%s", lpe_id); |
4821 | - SP_LPE_ITEM(clone_obj)->addPathEffect( href, false ); |
4822 | - g_free(href); |
4823 | - } |
4824 | - } |
4825 | - |
4826 | - DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_EDIT_CLONE_ORIGINAL_PATH_LPE, |
4827 | - _("Clone original path")); |
4828 | - |
4829 | - // select the new object: |
4830 | - selection->set(clone); |
4831 | - |
4832 | - Inkscape::GC::release(clone); |
4833 | + |
4834 | + Inkscape::SVGOStringStream os; |
4835 | + SPObject * firstItem = NULL; |
4836 | + for (const GSList * item = desktop->selection->itemList(); item != NULL; item = item->next) { |
4837 | + if (SP_IS_SHAPE(item->data) || SP_IS_TEXT(item->data)) { |
4838 | + if (firstItem) { |
4839 | + os << "|"; |
4840 | + } else { |
4841 | + firstItem = SP_ITEM(item->data); |
4842 | + } |
4843 | + os << "#" << SP_ITEM(item->data)->getId() << ",0"; |
4844 | + } |
4845 | + } |
4846 | + if (firstItem) { |
4847 | + Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc(); |
4848 | + SPObject *parent = firstItem->parent; |
4849 | + |
4850 | + // create the LPE |
4851 | + Inkscape::XML::Node *lpe_repr = xml_doc->createElement("inkscape:path-effect"); |
4852 | + { |
4853 | + lpe_repr->setAttribute("effect", "fill_between_many"); |
4854 | + lpe_repr->setAttribute("linkedpaths", os.str().c_str()); |
4855 | + desktop->doc()->getDefs()->getRepr()->addChild(lpe_repr, NULL); // adds to <defs> and assigns the 'id' attribute |
4856 | + } |
4857 | + const gchar * lpe_id = lpe_repr->attribute("id"); |
4858 | + Inkscape::GC::release(lpe_repr); |
4859 | + |
4860 | + // create the new path |
4861 | + Inkscape::XML::Node *clone = xml_doc->createElement("svg:path"); |
4862 | + { |
4863 | + clone->setAttribute("d", "M 0 0", false); |
4864 | + // add the new clone to the top of the original's parent |
4865 | + parent->appendChildRepr(clone); |
4866 | + SPObject *clone_obj = desktop->doc()->getObjectById(clone->attribute("id")); |
4867 | + if (SP_IS_LPE_ITEM(clone_obj)) { |
4868 | + gchar *href = g_strdup_printf("#%s", lpe_id); |
4869 | + //sp_lpe_item_add_path_effect( SP_LPE_ITEM(clone_obj), href, false ); |
4870 | + SP_LPE_ITEM(clone_obj)->addPathEffect(href, false); |
4871 | + g_free(href); |
4872 | + } |
4873 | + } |
4874 | + |
4875 | + DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_EDIT_CLONE_ORIGINAL_PATH_LPE, |
4876 | + _("Fill between strokes")); |
4877 | + } else { |
4878 | + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select path(s) to fill.")); |
4879 | + } |
4880 | } |
4881 | |
4882 | void sp_selection_to_marker(SPDesktop *desktop, bool apply) |
4883 | @@ -3663,6 +3662,118 @@ |
4884 | g_free(filepath); |
4885 | } |
4886 | |
4887 | +/* Creates a mask or clipPath from selection. |
4888 | + * What is a clip group? |
4889 | + * A clip group is a tangled mess of XML that allows an object inside a group |
4890 | + * to clip the entire group using a few <use>s and generally irritating me. |
4891 | + */ |
4892 | + |
4893 | +void sp_selection_set_clipgroup(SPDesktop *desktop) |
4894 | +{ |
4895 | + if (desktop == NULL) { |
4896 | + return; |
4897 | + } |
4898 | + SPDocument* doc = sp_desktop_document(desktop); |
4899 | + Inkscape::XML::Document *xml_doc = doc->getReprDoc(); |
4900 | + |
4901 | + Inkscape::Selection *selection = sp_desktop_selection(desktop); |
4902 | + if (selection->isEmpty()) { |
4903 | + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to create clippath or mask from.")); |
4904 | + return; |
4905 | + } |
4906 | + |
4907 | + GSList const *l = const_cast<GSList *>(selection->reprList()); |
4908 | + |
4909 | + GSList *p = g_slist_copy(const_cast<GSList *>(l)); |
4910 | + |
4911 | + p = g_slist_sort(p, (GCompareFunc) sp_repr_compare_position); |
4912 | + |
4913 | + selection->clear(); |
4914 | + |
4915 | + gint topmost = (static_cast<Inkscape::XML::Node *>(g_slist_last(p)->data))->position(); |
4916 | + Inkscape::XML::Node *topmost_parent = (static_cast<Inkscape::XML::Node *>(g_slist_last(p)->data))->parent(); |
4917 | + |
4918 | + Inkscape::XML::Node *inner = xml_doc->createElement("svg:g"); |
4919 | + inner->setAttribute("inkscape:label", "Clip"); |
4920 | + |
4921 | + while (p) { |
4922 | + Inkscape::XML::Node *current = static_cast<Inkscape::XML::Node *>(p->data); |
4923 | + |
4924 | + if (current->parent() == topmost_parent) { |
4925 | + Inkscape::XML::Node *spnew = current->duplicate(xml_doc); |
4926 | + sp_repr_unparent(current); |
4927 | + inner->appendChild(spnew); |
4928 | + Inkscape::GC::release(spnew); |
4929 | + topmost --; // only reduce count for those items deleted from topmost_parent |
4930 | + } else { // move it to topmost_parent first |
4931 | + GSList *temp_clip = NULL; |
4932 | + |
4933 | + // At this point, current may already have no item, due to its being a clone whose original is already moved away |
4934 | + // So we copy it artificially calculating the transform from its repr->attr("transform") and the parent transform |
4935 | + gchar const *t_str = current->attribute("transform"); |
4936 | + Geom::Affine item_t(Geom::identity()); |
4937 | + if (t_str) |
4938 | + sp_svg_transform_read(t_str, &item_t); |
4939 | + item_t *= SP_ITEM(doc->getObjectByRepr(current->parent()))->i2doc_affine(); |
4940 | + // FIXME: when moving both clone and original from a transformed group (either by |
4941 | + // grouping into another parent, or by cut/paste) the transform from the original's |
4942 | + // parent becomes embedded into original itself, and this affects its clones. Fix |
4943 | + // this by remembering the transform diffs we write to each item into an array and |
4944 | + // then, if this is clone, looking up its original in that array and pre-multiplying |
4945 | + // it by the inverse of that original's transform diff. |
4946 | + |
4947 | + sp_selection_copy_one(current, item_t, &temp_clip, xml_doc); |
4948 | + sp_repr_unparent(current); |
4949 | + |
4950 | + // paste into topmost_parent (temporarily) |
4951 | + GSList *copied = sp_selection_paste_impl(doc, doc->getObjectByRepr(topmost_parent), &temp_clip); |
4952 | + if (temp_clip) g_slist_free(temp_clip); |
4953 | + if (copied) { // if success, |
4954 | + // take pasted object (now in topmost_parent) |
4955 | + Inkscape::XML::Node *in_topmost = static_cast<Inkscape::XML::Node *>(copied->data); |
4956 | + // make a copy |
4957 | + Inkscape::XML::Node *spnew = in_topmost->duplicate(xml_doc); |
4958 | + // remove pasted |
4959 | + sp_repr_unparent(in_topmost); |
4960 | + // put its copy into group |
4961 | + inner->appendChild(spnew); |
4962 | + Inkscape::GC::release(spnew); |
4963 | + g_slist_free(copied); |
4964 | + } |
4965 | + } |
4966 | + p = g_slist_remove(p, current); |
4967 | + } |
4968 | + |
4969 | + Inkscape::XML::Node *outer = xml_doc->createElement("svg:g"); |
4970 | + outer->appendChild(inner); |
4971 | + topmost_parent->appendChild(outer); |
4972 | + outer->setPosition(topmost + 1); |
4973 | + |
4974 | + Inkscape::XML::Node *clone = xml_doc->createElement("svg:use"); |
4975 | + clone->setAttribute("x", "0", false); |
4976 | + clone->setAttribute("y", "0", false); |
4977 | + clone->setAttribute("xlink:href", g_strdup_printf("#%s", inner->attribute("id")), false); |
4978 | + |
4979 | + clone->setAttribute("inkscape:transform-center-x", inner->attribute("inkscape:transform-center-x"), false); |
4980 | + clone->setAttribute("inkscape:transform-center-y", inner->attribute("inkscape:transform-center-y"), false); |
4981 | + |
4982 | + const Geom::Affine maskTransform(Geom::Affine::identity()); |
4983 | + GSList *templist = NULL; |
4984 | + |
4985 | + templist = g_slist_append(templist, clone); |
4986 | + // add the new clone to the top of the original's parent |
4987 | + gchar const *mask_id = SPClipPath::create(templist, doc, &maskTransform); |
4988 | + |
4989 | + g_slist_free(templist); |
4990 | + |
4991 | + outer->setAttribute("clip-path", g_strdup_printf("url(#%s)", mask_id)); |
4992 | + |
4993 | + Inkscape::GC::release(clone); |
4994 | + |
4995 | + selection->set(outer); |
4996 | + DocumentUndo::done(doc, SP_VERB_OBJECT_SET_CLIPPATH, _("Create Clip Group")); |
4997 | +} |
4998 | + |
4999 | /** |
5000 | * Creates a mask or clipPath from selection. |
The diff has been truncated for viewing.
I've passed along a few clean up points over irc and I can't spot anything really bad. So I'm going ahead with a merge.