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

Proposed by Liam P. White
Status: Merged
Merged at revision: 13545
Proposed branch: lp:~liampwhite/inkscape/inkscape
Merge into: lp:inkscape/experimental
Diff against target: 12144 lines (+10546/-196)
97 files modified
po/POTFILES.in (+15/-0)
src/Makefile_insert (+3/-0)
src/attributes.cpp (+2/-0)
src/attributes.h (+2/-0)
src/display/cairo-utils.cpp (+1/-3)
src/display/cairo-utils.h (+3/-0)
src/display/drawing-item.cpp (+3/-2)
src/display/sp-canvas.cpp (+1/-1)
src/interface.cpp (+11/-0)
src/interface.h (+1/-0)
src/knotholder.cpp (+25/-11)
src/live_effects/CMakeLists.txt (+13/-0)
src/live_effects/Makefile_insert (+16/-0)
src/live_effects/effect-enum.h (+7/-0)
src/live_effects/effect.cpp (+71/-6)
src/live_effects/effect.h (+11/-0)
src/live_effects/lpe-attach-path.cpp (+198/-0)
src/live_effects/lpe-attach-path.h (+52/-0)
src/live_effects/lpe-bounding-box.cpp (+67/-0)
src/live_effects/lpe-bounding-box.h (+37/-0)
src/live_effects/lpe-ellipse_5pts.cpp (+214/-0)
src/live_effects/lpe-ellipse_5pts.h (+50/-0)
src/live_effects/lpe-fill-between-many.cpp (+78/-0)
src/live_effects/lpe-fill-between-many.h (+36/-0)
src/live_effects/lpe-fill-between-strokes.cpp (+116/-0)
src/live_effects/lpe-fill-between-strokes.h (+38/-0)
src/live_effects/lpe-jointype.cpp (+184/-0)
src/live_effects/lpe-jointype.h (+45/-0)
src/live_effects/lpe-knot.cpp (+4/-0)
src/live_effects/lpe-knot.h (+2/-1)
src/live_effects/lpe-powerstroke-interpolators.h (+40/-0)
src/live_effects/lpe-powerstroke.cpp (+89/-11)
src/live_effects/lpe-powerstroke.h (+2/-0)
src/live_effects/lpe-tangent_to_curve.cpp (+8/-11)
src/live_effects/lpe-tangent_to_curve.h (+0/-1)
src/live_effects/lpe-taperstroke.cpp (+630/-0)
src/live_effects/lpe-taperstroke.h (+72/-0)
src/live_effects/parameter/Makefile_insert (+4/-0)
src/live_effects/parameter/filletchamferpointarray.cpp (+3/-0)
src/live_effects/parameter/originalpatharray.cpp (+497/-0)
src/live_effects/parameter/originalpatharray.h (+123/-0)
src/live_effects/parameter/powerstrokepointarray.cpp (+36/-7)
src/live_effects/parameter/powerstrokepointarray.h (+21/-2)
src/live_effects/parameter/transformedpoint.cpp (+182/-0)
src/live_effects/parameter/transformedpoint.h (+87/-0)
src/live_effects/pathoutlineprovider.cpp (+795/-0)
src/live_effects/pathoutlineprovider.h (+55/-0)
src/menus-skeleton.h (+3/-0)
src/path-chemistry.cpp (+8/-0)
src/selection-chemistry.cpp (+159/-48)
src/selection-chemistry.h (+1/-0)
src/snap.cpp (+1/-1)
src/sp-item-group.cpp (+12/-0)
src/sp-item-group.h (+8/-0)
src/sp-item.cpp (+40/-0)
src/sp-item.h (+15/-0)
src/sp-lpe-item.cpp (+8/-2)
src/sp-object.h (+1/-0)
src/sp-tag-use-reference.cpp (+156/-0)
src/sp-tag-use-reference.h (+78/-0)
src/sp-tag-use.cpp (+206/-0)
src/sp-tag-use.h (+55/-0)
src/sp-tag.cpp (+154/-0)
src/sp-tag.h (+57/-0)
src/ui/dialog/Makefile_insert (+6/-0)
src/ui/dialog/calligraphic-profile-rename.h (+1/-1)
src/ui/dialog/color-item.cpp (+2/-0)
src/ui/dialog/dialog-manager.cpp (+6/-0)
src/ui/dialog/filedialog.h (+1/-0)
src/ui/dialog/lpe-powerstroke-properties.cpp (+211/-0)
src/ui/dialog/lpe-powerstroke-properties.h (+99/-0)
src/ui/dialog/objects.cpp (+2144/-0)
src/ui/dialog/objects.h (+263/-0)
src/ui/dialog/swatches.cpp (+32/-75)
src/ui/dialog/swatches.h (+0/-2)
src/ui/dialog/tags.cpp (+1165/-0)
src/ui/dialog/tags.h (+181/-0)
src/ui/tool/multi-path-manipulator.cpp (+3/-3)
src/ui/tool/multi-path-manipulator.h (+1/-1)
src/ui/tools/node-tool.cpp (+2/-1)
src/ui/tools/pen-tool.cpp (+3/-1)
src/ui/widget/Makefile_insert (+11/-2)
src/ui/widget/addtoicon.cpp (+157/-0)
src/ui/widget/addtoicon.h (+98/-0)
src/ui/widget/clipmaskicon.cpp (+184/-0)
src/ui/widget/clipmaskicon.h (+102/-0)
src/ui/widget/filter-effect-chooser.cpp (+2/-0)
src/ui/widget/filter-effect-chooser.h (+3/-1)
src/ui/widget/highlight-picker.cpp (+214/-0)
src/ui/widget/highlight-picker.h (+90/-0)
src/ui/widget/insertordericon.cpp (+173/-0)
src/ui/widget/insertordericon.h (+100/-0)
src/ui/widget/layertypeicon.cpp (+174/-0)
src/ui/widget/layertypeicon.h (+108/-0)
src/verbs.cpp (+92/-1)
src/verbs.h (+5/-0)
src/widgets/desktop-widget.cpp (+1/-1)
To merge this branch: bzr merge lp:~liampwhite/inkscape/inkscape
Reviewer Review Type Date Requested Status
Martin Owens Approve
Johan Engelen 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
13179. By Liam P. White <inkscapebrony at-sign gmail dot com>

Update to experimental r13428

13180. By Liam P. White <inkscapebrony at-sign gmail dot com>

Update to experimental r13429

13181. By Liam P. White <inkscapebrony at-sign gmail dot com>

Duplicate LPE entries

13182. By Liam P. White <inkscapebrony at-sign gmail dot com>

Update to experimental r13436

13183. By Liam P. White <inkscapebrony at-sign gmail dot com>

This file does not need executable permissions

13184. By Liam P. White <inkscapebrony at-sign gmail dot com>

Refactoring of linejoin code

13185. By Liam P. White

Update to experimental r13452

13186. By Liam P. White

Fix make check

13187. By Liam P. White

I'm an idiot

13188. By Liam P. White

Update to experimental r13460

13189. By Liam P. White

Update to experimental r13464

13190. By Liam P. White

Messed up German translation (??)

13191. By Liam P. White

Ponyscape feature: finish pen drawing on context switch

13192. By Liam P. White

Update to experimental r13465

13193. By Liam P. White

Update to experimental r13479

13194. By Liam P. White

Clone Original -> Fill Between Many

13195. By Liam P. White

Update to experimental r13483

13196. By Liam P. White

Update to experimental r13531

13197. By Liam P. White

Fix gtk3 build

13198. By Liam P. White

Update to experimental r13543

Revision history for this message
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