Merge lp:~liampwhite/inkscape/inkscape into lp:inkscape/experimental

Proposed by Liam P. White on 2014-07-07
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
Reviewer Review Type Date Requested Status
Martin Owens 2014-07-07 Approve on 2014-09-14
Johan Engelen 2014-07-07 Pending
Review via email: mp+225765@code.launchpad.net

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 on 2014-09-07
13179. By Liam P. White <inkscapebrony at-sign gmail dot com> on 2014-07-09

Update to experimental r13428

13180. By Liam P. White <inkscapebrony at-sign gmail dot com> on 2014-07-17

Update to experimental r13429

13181. By Liam P. White <inkscapebrony at-sign gmail dot com> on 2014-07-23

Duplicate LPE entries

13182. By Liam P. White <inkscapebrony at-sign gmail dot com> on 2014-07-23

Update to experimental r13436

13183. By Liam P. White <inkscapebrony at-sign gmail dot com> on 2014-07-23

This file does not need executable permissions

13184. By Liam P. White <inkscapebrony at-sign gmail dot com> on 2014-07-23

Refactoring of linejoin code

13185. By Liam P. White on 2014-07-30

Update to experimental r13452

13186. By Liam P. White on 2014-07-30

Fix make check

13187. By Liam P. White on 2014-07-31

I'm an idiot

13188. By Liam P. White on 2014-08-06

Update to experimental r13460

13189. By Liam P. White on 2014-08-08

Update to experimental r13464

13190. By Liam P. White on 2014-08-08

Messed up German translation (??)

13191. By Liam P. White on 2014-08-08

Ponyscape feature: finish pen drawing on context switch

13192. By Liam P. White on 2014-08-08

Update to experimental r13465

13193. By Liam P. White on 2014-08-12

Update to experimental r13479

13194. By Liam P. White on 2014-08-14

Clone Original -> Fill Between Many

13195. By Liam P. White on 2014-08-17

Update to experimental r13483

13196. By Liam P. White on 2014-08-31

Update to experimental r13531

13197. By Liam P. White on 2014-09-02

Fix gtk3 build

13198. By Liam P. White on 2014-09-07

Update to experimental r13543

Martin Owens (doctormo) wrote :

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.

review: Approve

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.

Subscribers

People subscribed via source and target branches