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
=== modified file 'po/POTFILES.in'
--- po/POTFILES.in 2014-08-30 15:50:55 +0000
+++ po/POTFILES.in 2014-09-07 17:02:32 +0000
@@ -153,7 +153,18 @@
153src/live_effects/lpe-skeleton.cpp153src/live_effects/lpe-skeleton.cpp
154src/live_effects/lpe-sketch.cpp154src/live_effects/lpe-sketch.cpp
155src/live_effects/lpe-vonkoch.cpp155src/live_effects/lpe-vonkoch.cpp
156src/live_effects/lpe-envelope-perspective.cpp
157src/live_effects/lpe-attach-path.cpp
158src/live_effects/lpe-bounding-box.cpp
159src/live_effects/lpe-ellipse_5pts.cpp
160src/live_effects/lpe-fill-between-many.cpp
161src/live_effects/lpe-fill-between-strokes.cpp
162src/live_effects/lpe-jointype.cpp
163src/live_effects/lpe-taperstroke.cpp
164src/live_effects/lpe-fillet-chamfer.cpp
156src/live_effects/parameter/filletchamferpointarray.cpp165src/live_effects/parameter/filletchamferpointarray.cpp
166src/live_effects/parameter/originalpatharray.cpp
167src/live_effects/parameter/transformedpoint.cpp
157src/live_effects/parameter/pointreseteable.cpp168src/live_effects/parameter/pointreseteable.cpp
158src/live_effects/parameter/togglebutton.cpp169src/live_effects/parameter/togglebutton.cpp
159src/live_effects/parameter/bool.cpp170src/live_effects/parameter/bool.cpp
@@ -260,6 +271,10 @@
260src/ui/dialog/tracedialog.cpp271src/ui/dialog/tracedialog.cpp
261src/ui/dialog/transformation.cpp272src/ui/dialog/transformation.cpp
262src/ui/dialog/xml-tree.cpp273src/ui/dialog/xml-tree.cpp
274src/ui/dialog/lpe-fillet-chamfer-properties.cpp
275src/ui/dialog/lpe-powerstroke-properties.cpp
276src/ui/dialog/objects.cpp
277src/ui/dialog/tags.cpp
263src/ui/tool/curve-drag-point.cpp278src/ui/tool/curve-drag-point.cpp
264src/ui/tool/multi-path-manipulator.cpp279src/ui/tool/multi-path-manipulator.cpp
265src/ui/tool/node.cpp280src/ui/tool/node.cpp
266281
=== modified file 'src/Makefile_insert'
--- src/Makefile_insert 2014-08-31 18:17:26 +0000
+++ src/Makefile_insert 2014-09-07 17:02:32 +0000
@@ -199,6 +199,9 @@
199 sp-style-elem.cpp sp-style-elem.h \199 sp-style-elem.cpp sp-style-elem.h \
200 sp-switch.cpp sp-switch.h \200 sp-switch.cpp sp-switch.h \
201 sp-symbol.cpp sp-symbol.h \201 sp-symbol.cpp sp-symbol.h \
202 sp-tag.cpp sp-tag.h \
203 sp-tag-use.cpp sp-tag-use.h \
204 sp-tag-use-reference.cpp sp-tag-use-reference.h \
202 sp-text.cpp sp-text.h \205 sp-text.cpp sp-text.h \
203 sp-textpath.h \206 sp-textpath.h \
204 sp-title.cpp sp-title.h \207 sp-title.cpp sp-title.h \
205208
=== modified file 'src/attributes.cpp'
--- src/attributes.cpp 2014-08-18 20:19:55 +0000
+++ src/attributes.cpp 2014-09-07 17:02:32 +0000
@@ -40,6 +40,7 @@
40 {SP_ATTR_TRANSFORM_CENTER_X, "inkscape:transform-center-x"},40 {SP_ATTR_TRANSFORM_CENTER_X, "inkscape:transform-center-x"},
41 {SP_ATTR_TRANSFORM_CENTER_Y, "inkscape:transform-center-y"},41 {SP_ATTR_TRANSFORM_CENTER_Y, "inkscape:transform-center-y"},
42 {SP_ATTR_INKSCAPE_PATH_EFFECT, "inkscape:path-effect"},42 {SP_ATTR_INKSCAPE_PATH_EFFECT, "inkscape:path-effect"},
43 {SP_ATTR_INKSCAPE_HIGHLIGHT_COLOR, "inkscape:highlight-color"},
43 /* SPAnchor */44 /* SPAnchor */
44 {SP_ATTR_XLINK_HREF, "xlink:href"},45 {SP_ATTR_XLINK_HREF, "xlink:href"},
45 {SP_ATTR_XLINK_TYPE, "xlink:type"},46 {SP_ATTR_XLINK_TYPE, "xlink:type"},
@@ -50,6 +51,7 @@
50 {SP_ATTR_XLINK_ACTUATE, "xlink:actuate"},51 {SP_ATTR_XLINK_ACTUATE, "xlink:actuate"},
51 {SP_ATTR_TARGET, "target"},52 {SP_ATTR_TARGET, "target"},
52 {SP_ATTR_INKSCAPE_GROUPMODE, "inkscape:groupmode"},53 {SP_ATTR_INKSCAPE_GROUPMODE, "inkscape:groupmode"},
54 {SP_ATTR_INKSCAPE_EXPANDED, "inkscape:expanded"},
53 /* SPRoot */55 /* SPRoot */
54 {SP_ATTR_VERSION, "version"},56 {SP_ATTR_VERSION, "version"},
55 {SP_ATTR_WIDTH, "width"},57 {SP_ATTR_WIDTH, "width"},
5658
=== modified file 'src/attributes.h'
--- src/attributes.h 2014-08-04 16:19:41 +0000
+++ src/attributes.h 2014-09-07 17:02:32 +0000
@@ -40,6 +40,7 @@
40 SP_ATTR_TRANSFORM_CENTER_X,40 SP_ATTR_TRANSFORM_CENTER_X,
41 SP_ATTR_TRANSFORM_CENTER_Y,41 SP_ATTR_TRANSFORM_CENTER_Y,
42 SP_ATTR_INKSCAPE_PATH_EFFECT,42 SP_ATTR_INKSCAPE_PATH_EFFECT,
43 SP_ATTR_INKSCAPE_HIGHLIGHT_COLOR,
43 /* SPAnchor */44 /* SPAnchor */
44 SP_ATTR_XLINK_HREF,45 SP_ATTR_XLINK_HREF,
45 SP_ATTR_XLINK_TYPE,46 SP_ATTR_XLINK_TYPE,
@@ -51,6 +52,7 @@
51 SP_ATTR_TARGET,52 SP_ATTR_TARGET,
52 /* SPGroup */53 /* SPGroup */
53 SP_ATTR_INKSCAPE_GROUPMODE,54 SP_ATTR_INKSCAPE_GROUPMODE,
55 SP_ATTR_INKSCAPE_EXPANDED,
54 /* SPRoot */56 /* SPRoot */
55 SP_ATTR_VERSION,57 SP_ATTR_VERSION,
56 SP_ATTR_WIDTH,58 SP_ATTR_WIDTH,
5759
=== modified file 'src/display/cairo-utils.cpp'
--- src/display/cairo-utils.cpp 2014-09-06 15:25:51 +0000
+++ src/display/cairo-utils.cpp 2014-09-07 17:02:32 +0000
@@ -33,8 +33,6 @@
33#include "helper/geom-curves.h"33#include "helper/geom-curves.h"
34#include "display/cairo-templates.h"34#include "display/cairo-templates.h"
3535
36static void ink_cairo_pixbuf_cleanup(guchar *, void *);
37
38/**36/**
39 * Key for cairo_surface_t to keep track of current color interpolation value37 * Key for cairo_surface_t to keep track of current color interpolation value
40 * Only the address of the structure is used, it is never initialized. See:38 * Only the address of the structure is used, it is never initialized. See:
@@ -1172,7 +1170,7 @@
1172 * to gdk_pixbuf_new_from_data when creating a GdkPixbuf backed by1170 * to gdk_pixbuf_new_from_data when creating a GdkPixbuf backed by
1173 * a Cairo surface.1171 * a Cairo surface.
1174 */1172 */
1175static void ink_cairo_pixbuf_cleanup(guchar * /*pixels*/, void *data)1173void ink_cairo_pixbuf_cleanup(guchar * /*pixels*/, void *data)
1176{1174{
1177 cairo_surface_t *surface = static_cast<cairo_surface_t*>(data);1175 cairo_surface_t *surface = static_cast<cairo_surface_t*>(data);
1178 cairo_surface_destroy(surface);1176 cairo_surface_destroy(surface);
11791177
=== modified file 'src/display/cairo-utils.h'
--- src/display/cairo-utils.h 2014-08-30 17:23:17 +0000
+++ src/display/cairo-utils.h 2014-09-07 17:02:32 +0000
@@ -20,6 +20,9 @@
20struct SPColor;20struct SPColor;
21typedef struct _GdkPixbuf GdkPixbuf;21typedef struct _GdkPixbuf GdkPixbuf;
2222
23void ink_cairo_pixbuf_cleanup(unsigned char *, void *);
24void convert_pixbuf_argb32_to_normal(GdkPixbuf *pb);
25
23namespace Inkscape {26namespace Inkscape {
2427
25/**28/**
2629
=== modified file 'src/display/drawing-item.cpp'
--- src/display/drawing-item.cpp 2014-08-18 21:18:05 +0000
+++ src/display/drawing-item.cpp 2014-09-07 17:02:32 +0000
@@ -825,9 +825,10 @@
825{825{
826 // Sometimes there's no BBOX in state, reason unknown (bug 992817)826 // Sometimes there's no BBOX in state, reason unknown (bug 992817)
827 // I made this not an assert to remove the warning827 // I made this not an assert to remove the warning
828 // This warning clutters the console output, so commented out
828 if (!(_state & STATE_BBOX) || !(_state & STATE_PICK)) {829 if (!(_state & STATE_BBOX) || !(_state & STATE_PICK)) {
829 g_warning("Invalid state when picking: STATE_BBOX = %d, STATE_PICK = %d",830 /*g_warning("Invalid state when picking: STATE_BBOX = %d, STATE_PICK = %d",
830 _state & STATE_BBOX, _state & STATE_PICK);831 _state & STATE_BBOX, _state & STATE_PICK);*/
831 return NULL;832 return NULL;
832 }833 }
833 // ignore invisible and insensitive items unless sticky834 // ignore invisible and insensitive items unless sticky
834835
=== modified file 'src/display/sp-canvas.cpp'
--- src/display/sp-canvas.cpp 2014-08-26 11:14:18 +0000
+++ src/display/sp-canvas.cpp 2014-09-07 17:02:32 +0000
@@ -1146,7 +1146,7 @@
1146sp_canvas_init(SPCanvas *canvas)1146sp_canvas_init(SPCanvas *canvas)
1147{1147{
1148 gtk_widget_set_has_window (GTK_WIDGET (canvas), TRUE);1148 gtk_widget_set_has_window (GTK_WIDGET (canvas), TRUE);
1149 //gtk_widget_set_double_buffered (GTK_WIDGET (canvas), TRUE);1149 gtk_widget_set_double_buffered (GTK_WIDGET (canvas), FALSE);
1150 gtk_widget_set_can_focus (GTK_WIDGET (canvas), TRUE);1150 gtk_widget_set_can_focus (GTK_WIDGET (canvas), TRUE);
11511151
1152 canvas->pick_event.type = GDK_LEAVE_NOTIFY;1152 canvas->pick_event.type = GDK_LEAVE_NOTIFY;
11531153
=== modified file 'src/interface.cpp'
--- src/interface.cpp 2014-09-02 21:14:55 +0000
+++ src/interface.cpp 2014-09-07 17:02:32 +0000
@@ -1756,6 +1756,13 @@
1756 }1756 }
1757 mi->show();1757 mi->show();
1758 append(*mi);1758 append(*mi);
1759
1760 /*SSet Clip Group */
1761 mi = Gtk::manage(new Gtk::MenuItem(_("Create Clip G_roup"),1));
1762 mi->signal_activate().connect(sigc::mem_fun(*this, &ContextMenu::CreateGroupClip));
1763 mi->set_sensitive(TRUE);
1764 mi->show();
1765 append(*mi);
1759 1766
1760 /* Set Clip */1767 /* Set Clip */
1761 mi = Gtk::manage(new Gtk::MenuItem(_("Set Cl_ip"), 1));1768 mi = Gtk::manage(new Gtk::MenuItem(_("Set Cl_ip"), 1));
@@ -1867,6 +1874,10 @@
1867 sp_selection_unset_mask(_desktop, false);1874 sp_selection_unset_mask(_desktop, false);
1868}1875}
18691876
1877void ContextMenu::CreateGroupClip(void)
1878{
1879 sp_selection_set_clipgroup(_desktop);
1880}
18701881
1871void ContextMenu::SetClip(void)1882void ContextMenu::SetClip(void)
1872{1883{
18731884
=== modified file 'src/interface.h'
--- src/interface.h 2014-08-31 18:17:26 +0000
+++ src/interface.h 2014-09-07 17:02:32 +0000
@@ -183,6 +183,7 @@
183 void SelectSameStrokeStyle(void);183 void SelectSameStrokeStyle(void);
184 void SelectSameObjectType(void);184 void SelectSameObjectType(void);
185 void ItemCreateLink(void);185 void ItemCreateLink(void);
186 void CreateGroupClip(void);
186 void SetMask(void);187 void SetMask(void);
187 void ReleaseMask(void);188 void ReleaseMask(void);
188 void SetClip(void);189 void SetClip(void);
189190
=== modified file 'src/knotholder.cpp'
--- src/knotholder.cpp 2014-08-04 16:10:37 +0000
+++ src/knotholder.cpp 2014-09-07 17:02:32 +0000
@@ -154,8 +154,15 @@
154 }154 }
155155
156 // for drag, this is done by ungrabbed_handler, but for click we must do it here156 // for drag, this is done by ungrabbed_handler, but for click we must do it here
157 DocumentUndo::done(saved_item->document, object_verb,157
158 _("Change handle"));158 if (saved_item) { //increasingly aggressive sanity checks
159 if (saved_item->document) {
160 if (object_verb <= SP_VERB_LAST && object_verb >= SP_VERB_INVALID) {
161 DocumentUndo::done(saved_item->document, object_verb,
162 _("Change handle"));
163 }
164 }
165 } // else { abort(); }
159}166}
160167
161void168void
@@ -203,14 +210,16 @@
203 /* do cleanup tasks (e.g., for LPE items write the parameter values210 /* do cleanup tasks (e.g., for LPE items write the parameter values
204 * that were changed by dragging the handle to SVG)211 * that were changed by dragging the handle to SVG)
205 */212 */
206 if (SP_IS_LPE_ITEM(object)) {213 if (dynamic_cast<SPLPEItem*> (object)) {
207 // This writes all parameters to SVG. Is this sufficiently efficient or should we only214 // This writes all parameters to SVG. Is this sufficiently efficient or should we only
208 // write the ones that were changed?215 // write the ones that were changed?
209216 SPLPEItem * lpeitem = SP_LPE_ITEM(object);
210 Inkscape::LivePathEffect::Effect *lpe = SP_LPE_ITEM(object)->getCurrentLPE();217 if (lpeitem) {
211 if (lpe) {218 Inkscape::LivePathEffect::Effect *lpe = lpeitem->getCurrentLPE();
212 LivePathEffectObject *lpeobj = lpe->getLPEObj();219 if (lpe) {
213 lpeobj->updateRepr();220 LivePathEffectObject *lpeobj = lpe->getLPEObj();
221 lpeobj->updateRepr();
222 }
214 }223 }
215 }224 }
216225
@@ -232,9 +241,14 @@
232 else241 else
233 object_verb = SP_VERB_SELECTION_DYNAMIC_OFFSET;242 object_verb = SP_VERB_SELECTION_DYNAMIC_OFFSET;
234 }243 }
235244 if (object) { //increasingly aggressive sanity checks
236 DocumentUndo::done(object->document, object_verb,245 if (object->document) {
237 _("Move handle"));246 if (object_verb <= SP_VERB_LAST && object_verb >= SP_VERB_INVALID) {
247 DocumentUndo::done(object->document, object_verb,
248 _("Move handle"));
249 }
250 }
251 } //else { abort(); }
238 }252 }
239}253}
240254
241255
=== modified file 'src/live_effects/CMakeLists.txt'
--- src/live_effects/CMakeLists.txt 2014-08-12 22:30:34 +0000
+++ src/live_effects/CMakeLists.txt 2014-09-07 17:02:32 +0000
@@ -2,8 +2,10 @@
2set(live_effects_SRC2set(live_effects_SRC
3 effect.cpp3 effect.cpp
4 lpe-angle_bisector.cpp4 lpe-angle_bisector.cpp
5 lpe-attach-path.cpp
5 lpe-bendpath.cpp6 lpe-bendpath.cpp
6 lpe-boolops.cpp7 lpe-boolops.cpp
8 lpe-bounding-box.cpp
7 lpe-circle_3pts.cpp9 lpe-circle_3pts.cpp
8 lpe-circle_with_radius.cpp10 lpe-circle_with_radius.cpp
9 lpe-clone-original.cpp11 lpe-clone-original.cpp
@@ -11,9 +13,12 @@
11 lpe-copy_rotate.cpp13 lpe-copy_rotate.cpp
12 lpe-curvestitch.cpp14 lpe-curvestitch.cpp
13 lpe-dynastroke.cpp15 lpe-dynastroke.cpp
16 lpe-ellipse-5pts.cpp
14 lpe-envelope.cpp17 lpe-envelope.cpp
15 lpe-envelope-perspective.cpp18 lpe-envelope-perspective.cpp
16 lpe-extrude.cpp19 lpe-extrude.cpp
20 lpe-fill-between-many.cpp
21 lpe-fill-between-strokes.cpp
17 lpe-fillet-chamfer.cpp22 lpe-fillet-chamfer.cpp
18 lpe-gears.cpp23 lpe-gears.cpp
19 lpe-interpolate.cpp24 lpe-interpolate.cpp
@@ -55,11 +60,13 @@
55 parameter/parameter.cpp60 parameter/parameter.cpp
56 parameter/path.cpp61 parameter/path.cpp
57 parameter/originalpath.cpp62 parameter/originalpath.cpp
63 parameter/originalpatharray.cpp
58 parameter/path-reference.cpp64 parameter/path-reference.cpp
59 parameter/point.cpp65 parameter/point.cpp
60 parameter/powerstrokepointarray.cpp66 parameter/powerstrokepointarray.cpp
61 parameter/random.cpp67 parameter/random.cpp
62 parameter/text.cpp68 parameter/text.cpp
69 paramter/transformedpoint.cpp
63 parameter/togglebutton.cpp70 parameter/togglebutton.cpp
64 parameter/unit.cpp71 parameter/unit.cpp
65 parameter/vector.cpp72 parameter/vector.cpp
@@ -70,8 +77,10 @@
70 effect-enum.h77 effect-enum.h
71 effect.h78 effect.h
72 lpe-angle_bisector.h79 lpe-angle_bisector.h
80 lpe-attach-path.h
73 lpe-bendpath.h81 lpe-bendpath.h
74 lpe-boolops.h82 lpe-boolops.h
83 lpe-bounding-box.h
75 lpe-circle_3pts.h84 lpe-circle_3pts.h
76 lpe-circle_with_radius.h85 lpe-circle_with_radius.h
77 lpe-clone-original.h86 lpe-clone-original.h
@@ -79,8 +88,11 @@
79 lpe-copy_rotate.h88 lpe-copy_rotate.h
80 lpe-curvestitch.h89 lpe-curvestitch.h
81 lpe-dynastroke.h90 lpe-dynastroke.h
91 lpe-ellipse-5pts.h
82 lpe-envelope.h92 lpe-envelope.h
83 lpe-extrude.h93 lpe-extrude.h
94 lpe-fill-between-many.h
95 lpe-fill-between-strokes.h
84 lpe-fillet-chamfer.h96 lpe-fillet-chamfer.h
85 lpe-gears.h97 lpe-gears.h
86 lpe-interpolate.h98 lpe-interpolate.h
@@ -125,6 +137,7 @@
125 parameter/path-reference.h137 parameter/path-reference.h
126 parameter/path.h138 parameter/path.h
127 parameter/originalpath.h139 parameter/originalpath.h
140 parameter/originalpatharray.h
128 parameter/point.h141 parameter/point.h
129 parameter/powerstrokepointarray.h142 parameter/powerstrokepointarray.h
130 parameter/random.h143 parameter/random.h
131144
=== modified file 'src/live_effects/Makefile_insert'
--- src/live_effects/Makefile_insert 2014-08-23 16:39:36 +0000
+++ src/live_effects/Makefile_insert 2014-09-07 17:02:32 +0000
@@ -97,5 +97,21 @@
97 live_effects/lpe-path_length.h \97 live_effects/lpe-path_length.h \
98 live_effects/lpe-line_segment.cpp \98 live_effects/lpe-line_segment.cpp \
99 live_effects/lpe-line_segment.h \99 live_effects/lpe-line_segment.h \
100 live_effects/lpe-bounding-box.cpp \
101 live_effects/lpe-bounding-box.h \
102 live_effects/lpe-attach-path.cpp \
103 live_effects/lpe-attach-path.h \
104 live_effects/lpe-fill-between-strokes.cpp \
105 live_effects/lpe-fill-between-strokes.h \
106 live_effects/lpe-fill-between-many.cpp \
107 live_effects/lpe-fill-between-many.h \
108 live_effects/lpe-ellipse_5pts.cpp \
109 live_effects/lpe-ellipse_5pts.h \
110 live_effects/pathoutlineprovider.cpp \
111 live_effects/pathoutlineprovider.h \
112 live_effects/lpe-jointype.cpp \
113 live_effects/lpe-jointype.h \
114 live_effects/lpe-taperstroke.cpp \
115 live_effects/lpe-taperstroke.h \
100 live_effects/lpe-envelope-perspective.cpp \116 live_effects/lpe-envelope-perspective.cpp \
101 live_effects/lpe-envelope-perspective.h117 live_effects/lpe-envelope-perspective.h
102118
=== modified file 'src/live_effects/effect-enum.h'
--- src/live_effects/effect-enum.h 2014-08-23 16:39:36 +0000
+++ src/live_effects/effect-enum.h 2014-09-07 17:02:32 +0000
@@ -56,6 +56,13 @@
56 EXTRUDE,56 EXTRUDE,
57 POWERSTROKE,57 POWERSTROKE,
58 CLONE_ORIGINAL,58 CLONE_ORIGINAL,
59 ATTACH_PATH,
60 FILL_BETWEEN_STROKES,
61 FILL_BETWEEN_MANY,
62 ELLIPSE_5PTS,
63 BOUNDING_BOX,
64 JOIN_TYPE,
65 TAPER_STROKE,
59 ENVELOPE_PERSPECTIVE,66 ENVELOPE_PERSPECTIVE,
60 FILLET_CHAMFER,67 FILLET_CHAMFER,
61 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)68 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)
6269
=== modified file 'src/live_effects/effect.cpp'
--- src/live_effects/effect.cpp 2014-08-23 16:39:36 +0000
+++ src/live_effects/effect.cpp 2014-09-07 17:02:32 +0000
@@ -5,6 +5,8 @@
5 * Released under GNU GPL, read the file 'COPYING' for more information5 * Released under GNU GPL, read the file 'COPYING' for more information
6 */6 */
77
8//#define LPE_ENABLE_TEST_EFFECTS //uncomment for toy effects
9
8#ifdef HAVE_CONFIG_H10#ifdef HAVE_CONFIG_H
9# include "config.h"11# include "config.h"
10#endif12#endif
@@ -19,7 +21,6 @@
19#include "live_effects/lpe-rough-hatches.h"21#include "live_effects/lpe-rough-hatches.h"
20#include "live_effects/lpe-dynastroke.h"22#include "live_effects/lpe-dynastroke.h"
21#include "live_effects/lpe-test-doEffect-stack.h"23#include "live_effects/lpe-test-doEffect-stack.h"
22#include "live_effects/lpe-bspline.h"
23#include "live_effects/lpe-gears.h"24#include "live_effects/lpe-gears.h"
24#include "live_effects/lpe-curvestitch.h"25#include "live_effects/lpe-curvestitch.h"
25#include "live_effects/lpe-circle_with_radius.h"26#include "live_effects/lpe-circle_with_radius.h"
@@ -51,6 +52,14 @@
51#include "live_effects/lpe-extrude.h"52#include "live_effects/lpe-extrude.h"
52#include "live_effects/lpe-powerstroke.h"53#include "live_effects/lpe-powerstroke.h"
53#include "live_effects/lpe-clone-original.h"54#include "live_effects/lpe-clone-original.h"
55#include "live_effects/lpe-bspline.h"
56#include "live_effects/lpe-attach-path.h"
57#include "live_effects/lpe-fill-between-strokes.h"
58#include "live_effects/lpe-fill-between-many.h"
59#include "live_effects/lpe-ellipse_5pts.h"
60#include "live_effects/lpe-bounding-box.h"
61#include "live_effects/lpe-jointype.h"
62#include "live_effects/lpe-taperstroke.h"
54#include "live_effects/lpe-envelope-perspective.h"63#include "live_effects/lpe-envelope-perspective.h"
55#include "live_effects/lpe-fillet-chamfer.h"64#include "live_effects/lpe-fillet-chamfer.h"
5665
@@ -132,10 +141,18 @@
132 {SHOW_HANDLES, N_("Show handles"), "show_handles"},141 {SHOW_HANDLES, N_("Show handles"), "show_handles"},
133 {ROUGHEN, N_("Roughen"), "roughen"},142 {ROUGHEN, N_("Roughen"), "roughen"},
134 {BSPLINE, N_("BSpline"), "bspline"},143 {BSPLINE, N_("BSpline"), "bspline"},
135 {SIMPLIFY, N_("Simplify"), "simplify"},144 {JOIN_TYPE, N_("Join type"), "join_type"},
136 {LATTICE2, N_("Lattice Deformation 2"), "lattice2"},145 {TAPER_STROKE, N_("Taper stroke"), "taper_stroke"},
137 // TRANSLATORS: "Envelope Perspective" should be equivalent to "perspective transformation"146/* Ponyscape */
138 {ENVELOPE_PERSPECTIVE, N_("Envelope Perspective"), "envelope-perspective"},147 {ATTACH_PATH, N_("Attach path"), "attach_path"},
148 {FILL_BETWEEN_STROKES, N_("Fill between strokes"), "fill_between_strokes"},
149 {FILL_BETWEEN_MANY, N_("Fill between many"), "fill_between_many"},
150 {ELLIPSE_5PTS, N_("Ellipse by 5 points"), "ellipse_5pts"},
151 {BOUNDING_BOX, N_("Bounding Box"), "bounding_box"},
152/* 0.91 */
153 {SIMPLIFY, N_("Simplify"), "simplify"},
154 {LATTICE2, N_("Lattice Deformation 2"), "lattice2"},
155 {ENVELOPE_PERSPECTIVE, N_("Envelope-Perspective"), "envelope-perspective"},
139 {FILLET_CHAMFER, N_("Fillet/Chamfer"), "fillet-chamfer"},156 {FILLET_CHAMFER, N_("Fillet/Chamfer"), "fillet-chamfer"},
140 {INTERPOLATE_POINTS, N_("Interpolate points"), "interpolate_points"},157 {INTERPOLATE_POINTS, N_("Interpolate points"), "interpolate_points"},
141};158};
@@ -267,6 +284,27 @@
267 case CLONE_ORIGINAL:284 case CLONE_ORIGINAL:
268 neweffect = static_cast<Effect*> ( new LPECloneOriginal(lpeobj) );285 neweffect = static_cast<Effect*> ( new LPECloneOriginal(lpeobj) );
269 break;286 break;
287 case ATTACH_PATH:
288 neweffect = static_cast<Effect*> ( new LPEAttachPath(lpeobj) );
289 break;
290 case FILL_BETWEEN_STROKES:
291 neweffect = static_cast<Effect*> ( new LPEFillBetweenStrokes(lpeobj) );
292 break;
293 case FILL_BETWEEN_MANY:
294 neweffect = static_cast<Effect*> ( new LPEFillBetweenMany(lpeobj) );
295 break;
296 case ELLIPSE_5PTS:
297 neweffect = static_cast<Effect*> ( new LPEEllipse5Pts(lpeobj) );
298 break;
299 case BOUNDING_BOX:
300 neweffect = static_cast<Effect*> ( new LPEBoundingBox(lpeobj) );
301 break;
302 case JOIN_TYPE:
303 neweffect = static_cast<Effect*> ( new LPEJoinType(lpeobj) );
304 break;
305 case TAPER_STROKE:
306 neweffect = static_cast<Effect*> ( new LPETaperStroke(lpeobj) );
307 break;
270 case SIMPLIFY:308 case SIMPLIFY:
271 neweffect = static_cast<Effect*> ( new LPESimplify(lpeobj) );309 neweffect = static_cast<Effect*> ( new LPESimplify(lpeobj) );
272 break;310 break;
@@ -286,7 +324,7 @@
286 neweffect = static_cast<Effect*> ( new LPEShowHandles(lpeobj) );324 neweffect = static_cast<Effect*> ( new LPEShowHandles(lpeobj) );
287 break;325 break;
288 default:326 default:
289 g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);327 g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);
290 neweffect = NULL;328 neweffect = NULL;
291 break;329 break;
292 }330 }
@@ -369,6 +407,33 @@
369 //Do nothing for simple effects407 //Do nothing for simple effects
370}408}
371409
410void Effect::doAfterEffect (SPLPEItem const* lpeitem)
411{
412}
413
414void Effect::doOnRemove (SPLPEItem const* lpeitem)
415{
416}
417
418//secret impl methods (shhhh!)
419void Effect::doOnApply_impl(SPLPEItem const* lpeitem)
420{
421 sp_lpe_item = const_cast<SPLPEItem *>(lpeitem);
422 /*sp_curve = SP_SHAPE(sp_lpe_item)->getCurve();
423 pathvector_before_effect = sp_curve->get_pathvector();*/
424 doOnApply(lpeitem);
425}
426
427void Effect::doBeforeEffect_impl(SPLPEItem const* lpeitem)
428{
429 sp_lpe_item = const_cast<SPLPEItem *>(lpeitem);
430 //printf("(SPLPEITEM*) %p\n", sp_lpe_item);
431 sp_curve = SP_SHAPE(sp_lpe_item)->getCurve();
432 pathvector_before_effect = sp_curve->get_pathvector();
433
434 doBeforeEffect(lpeitem);
435}
436
372/**437/**
373 * Effects can have a parameter path set before they are applied by accepting a nonzero number of438 * Effects can have a parameter path set before they are applied by accepting a nonzero number of
374 * mouse clicks. This method activates the pen context, which waits for the specified number of439 * mouse clicks. This method activates the pen context, which waits for the specified number of
375440
=== modified file 'src/live_effects/effect.h'
--- src/live_effects/effect.h 2014-05-05 07:13:35 +0000
+++ src/live_effects/effect.h 2014-09-07 17:02:32 +0000
@@ -53,8 +53,16 @@
5353
54 EffectType effectType() const;54 EffectType effectType() const;
5555
56 //basically, to get this method called before the derived classes, a bit
57 //of indirection is needed. We first call these methods, then the below.
58 void doOnApply_impl(SPLPEItem const* lpeitem);
59 void doBeforeEffect_impl(SPLPEItem const* lpeitem);
60
56 virtual void doOnApply (SPLPEItem const* lpeitem);61 virtual void doOnApply (SPLPEItem const* lpeitem);
57 virtual void doBeforeEffect (SPLPEItem const* lpeitem);62 virtual void doBeforeEffect (SPLPEItem const* lpeitem);
63
64 virtual void doAfterEffect (SPLPEItem const* lpeitem);
65 virtual void doOnRemove (SPLPEItem const* lpeitem);
5866
59 void writeParamsToSVG();67 void writeParamsToSVG();
6068
@@ -147,6 +155,9 @@
147 // instead of normally 'splitting' the path into continuous pwd2 paths and calling doEffect_pwd2 for each.155 // instead of normally 'splitting' the path into continuous pwd2 paths and calling doEffect_pwd2 for each.
148 bool concatenate_before_pwd2;156 bool concatenate_before_pwd2;
149157
158 SPLPEItem * sp_lpe_item; // these get stored in doBeforeEffect_impl, and derived classes may do as they please with them.
159 SPCurve * sp_curve;
160 std::vector<Geom::Path> pathvector_before_effect;
150private:161private:
151 bool provides_own_flash_paths; // if true, the standard flash path is suppressed162 bool provides_own_flash_paths; // if true, the standard flash path is suppressed
152163
153164
=== added file 'src/live_effects/lpe-attach-path.cpp'
--- src/live_effects/lpe-attach-path.cpp 1970-01-01 00:00:00 +0000
+++ src/live_effects/lpe-attach-path.cpp 2014-09-07 17:02:32 +0000
@@ -0,0 +1,198 @@
1/*
2 * Copyright (C) Johan Engelen 2012 <j.b.c.engelen@alumnus.utwente.nl>
3 *
4 * Released under GNU GPL, read the file 'COPYING' for more information
5 */
6
7#include <glibmm/i18n.h>
8#include <math.h>
9
10#include "live_effects/lpe-attach-path.h"
11
12#include "display/curve.h"
13#include "sp-item.h"
14#include "2geom/path.h"
15#include "sp-shape.h"
16#include "sp-text.h"
17#include "2geom/bezier-curve.h"
18#include "2geom/path-sink.h"
19#include "parameter/parameter.h"
20#include "live_effects/parameter/point.h"
21#include "parameter/originalpath.h"
22#include "2geom/affine.h"
23
24namespace Inkscape {
25namespace LivePathEffect {
26
27LPEAttachPath::LPEAttachPath(LivePathEffectObject *lpeobject) :
28 Effect(lpeobject),
29 start_path(_("Start path:"), _("Path to attach to the start of this path"), "startpath", &wr, this),
30 start_path_position(_("Start path position:"), _("Position to attach path start to"), "startposition", &wr, this, 0.0),
31 start_path_curve_start(_("Start path curve start:"), _("Starting curve"), "startcurvestart", &wr, this, Geom::Point(20,0)/*, true*/),
32 start_path_curve_end(_("Start path curve end:"), _("Ending curve"), "startcurveend", &wr, this, Geom::Point(20,0)/*, true*/),
33 end_path(_("End path:"), _("Path to attach to the end of this path"), "endpath", &wr, this),
34 end_path_position(_("End path position:"), _("Position to attach path end to"), "endposition", &wr, this, 0.0),
35 end_path_curve_start(_("End path curve start:"), _("Starting curve"), "endcurvestart", &wr, this, Geom::Point(20,0)/*, true*/),
36 end_path_curve_end(_("End path curve end:"), _("Ending curve"), "endcurveend", &wr, this, Geom::Point(20,0)/*, true*/)
37{
38 registerParameter( dynamic_cast<Parameter *>(&start_path) );
39 registerParameter( dynamic_cast<Parameter *>(&start_path_position) );
40 registerParameter( dynamic_cast<Parameter *>(&start_path_curve_start) );
41 registerParameter( dynamic_cast<Parameter *>(&start_path_curve_end) );
42
43 registerParameter( dynamic_cast<Parameter *>(&end_path) );
44 registerParameter( dynamic_cast<Parameter *>(&end_path_position) );
45 registerParameter( dynamic_cast<Parameter *>(&end_path_curve_start) );
46 registerParameter( dynamic_cast<Parameter *>(&end_path_curve_end) );
47
48 //perceived_path = true;
49 show_orig_path = true;
50 curve_start_previous_origin = start_path_curve_end.getOrigin();
51 curve_end_previous_origin = end_path_curve_end.getOrigin();
52}
53
54LPEAttachPath::~LPEAttachPath()
55{
56
57}
58
59void LPEAttachPath::resetDefaults(SPItem const * item)
60{
61 curve_start_previous_origin = start_path_curve_end.getOrigin();
62 curve_end_previous_origin = end_path_curve_end.getOrigin();
63}
64
65void LPEAttachPath::doEffect (SPCurve * curve)
66{
67 std::vector<Geom::Path> this_pathv = curve->get_pathvector();
68 if (sp_lpe_item && !this_pathv.empty()) {
69 Geom::Path p = Geom::Path(this_pathv.front().initialPoint());
70
71 bool set_start_end = start_path_curve_end.getOrigin() != curve_start_previous_origin;
72 bool set_end_end = end_path_curve_end.getOrigin() != curve_end_previous_origin;
73
74 if (start_path.linksToPath()) {
75
76 std::vector<Geom::Path> linked_pathv = start_path.get_pathvector();
77 Geom::Affine linkedtransform = start_path.getObject()->getRelativeTransform(sp_lpe_item);
78
79 if ( !linked_pathv.empty() )
80 {
81 Geom::Path transformedpath = linked_pathv.front() * linkedtransform;
82 start_path_curve_start.setOrigin(this_pathv.front().initialPoint());
83
84 std::vector<Geom::Point> derivs = this_pathv.front().front().pointAndDerivatives(0, 3);
85
86 for (unsigned deriv_n = 1; deriv_n < derivs.size(); deriv_n++) {
87 Geom::Coord length = derivs[deriv_n].length();
88 if ( ! Geom::are_near(length, 0) ) {
89 if (set_start_end) {
90 start_path_position.param_set_value(transformedpath.nearestPoint(start_path_curve_end.getOrigin()));
91 }
92
93 if (start_path_position > transformedpath.size()) {
94 start_path_position.param_set_value(transformedpath.size());
95 } else if (start_path_position < 0) {
96 start_path_position.param_set_value(0);
97 }
98 const Geom::Curve *c = start_path_position >= transformedpath.size() ? &transformedpath.back() : &transformedpath.at_index((int)start_path_position);
99
100 std::vector<Geom::Point> derivs_2 = c->pointAndDerivatives(start_path_position >= transformedpath.size() ? 1 : (start_path_position - (int)start_path_position), 3);
101 for (unsigned deriv_n_2 = 1; deriv_n_2 < derivs_2.size(); deriv_n_2++) {
102 Geom::Coord length_2 = derivs[deriv_n_2].length();
103 if ( ! Geom::are_near(length_2, 0) ) {
104 start_path_curve_end.setOrigin(derivs_2[0]);
105 curve_start_previous_origin = start_path_curve_end.getOrigin();
106
107 double startangle = atan2(start_path_curve_start.getVector().y(), start_path_curve_start.getVector().x());
108 double endangle = atan2(start_path_curve_end.getVector().y(), start_path_curve_end.getVector().x());
109 double startderiv = atan2(derivs[deriv_n].y(), derivs[deriv_n].x());
110 double endderiv = atan2(derivs_2[deriv_n_2].y(), derivs_2[deriv_n_2].x());
111 Geom::Point pt1 = Geom::Point(start_path_curve_start.getVector().length() * cos(startangle + startderiv), start_path_curve_start.getVector().length() * sin(startangle + startderiv));
112 Geom::Point pt2 = Geom::Point(start_path_curve_end.getVector().length() * cos(endangle + endderiv), start_path_curve_end.getVector().length() * sin(endangle + endderiv));
113 p = Geom::Path(derivs_2[0]);
114 p.appendNew<Geom::CubicBezier>(-pt2 + derivs_2[0], -pt1 + this_pathv.front().initialPoint(), this_pathv.front().initialPoint());
115 break;
116
117 }
118 }
119 break;
120 }
121 }
122 }
123 }
124
125 p.append(this_pathv.front());
126
127 if (end_path.linksToPath()) {
128
129 std::vector<Geom::Path> linked_pathv = end_path.get_pathvector();
130 Geom::Affine linkedtransform = end_path.getObject()->getRelativeTransform(sp_lpe_item);
131
132 if ( !linked_pathv.empty() )
133 {
134 Geom::Path transformedpath = linked_pathv.front() * linkedtransform;
135 Geom::Curve * last_seg_reverse = this_pathv.front().back().reverse();
136
137 end_path_curve_start.setOrigin(last_seg_reverse->initialPoint());
138
139 std::vector<Geom::Point> derivs = last_seg_reverse->pointAndDerivatives(0, 3);
140 for (unsigned deriv_n = 1; deriv_n < derivs.size(); deriv_n++) {
141 Geom::Coord length = derivs[deriv_n].length();
142 if ( ! Geom::are_near(length, 0) ) {
143 if (set_end_end) {
144 end_path_position.param_set_value(transformedpath.nearestPoint(end_path_curve_end.getOrigin()));
145 }
146
147 if (end_path_position > transformedpath.size()) {
148 end_path_position.param_set_value(transformedpath.size());
149 } else if (end_path_position < 0) {
150 end_path_position.param_set_value(0);
151 }
152 const Geom::Curve *c = end_path_position >= transformedpath.size() ? &transformedpath.back() : &transformedpath.at_index((int)end_path_position);
153
154 std::vector<Geom::Point> derivs_2 = c->pointAndDerivatives(end_path_position >= transformedpath.size() ? 1 : (end_path_position - (int)end_path_position), 3);
155 for (unsigned deriv_n_2 = 1; deriv_n_2 < derivs_2.size(); deriv_n_2++) {
156 Geom::Coord length_2 = derivs[deriv_n_2].length();
157 if ( ! Geom::are_near(length_2, 0) ) {
158
159 end_path_curve_end.setOrigin(derivs_2[0]);
160 curve_end_previous_origin = end_path_curve_end.getOrigin();
161
162 double startangle = atan2(end_path_curve_start.getVector().y(), end_path_curve_start.getVector().x());
163 double endangle = atan2(end_path_curve_end.getVector().y(), end_path_curve_end.getVector().x());
164 double startderiv = atan2(derivs[deriv_n].y(), derivs[deriv_n].x());
165 double endderiv = atan2(derivs_2[deriv_n_2].y(), derivs_2[deriv_n_2].x());
166 Geom::Point pt1 = Geom::Point(end_path_curve_start.getVector().length() * cos(startangle + startderiv), end_path_curve_start.getVector().length() * sin(startangle + startderiv));
167 Geom::Point pt2 = Geom::Point(end_path_curve_end.getVector().length() * cos(endangle + endderiv), end_path_curve_end.getVector().length() * sin(endangle + endderiv));
168 p.appendNew<Geom::CubicBezier>(-pt1 + this_pathv.front().finalPoint(), -pt2 + derivs_2[0], derivs_2[0]);
169
170 break;
171
172 }
173 }
174 break;
175 }
176 }
177 delete last_seg_reverse;
178 }
179 }
180 Geom::PathVector outvector;
181 outvector.push_back(p);
182 curve->set_pathvector(outvector);
183 }
184}
185
186} // namespace LivePathEffect
187} /* namespace Inkscape */
188
189/*
190 Local Variables:
191 mode:c++
192 c-file-style:"stroustrup"
193 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
194 indent-tabs-mode:nil
195 fill-column:99
196 End:
197*/
198// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
0199
=== added file 'src/live_effects/lpe-attach-path.h'
--- src/live_effects/lpe-attach-path.h 1970-01-01 00:00:00 +0000
+++ src/live_effects/lpe-attach-path.h 2014-09-07 17:02:32 +0000
@@ -0,0 +1,52 @@
1#ifndef INKSCAPE_LPE_ATTACH_PATH_H
2#define INKSCAPE_LPE_ATTACH_PATH_H
3
4/*
5 * Inkscape::LPEAttachPath
6 *
7 * Copyright (C) Ted Janeczko 2012 <flutterguy317@gmail.com>
8 *
9 * Released under GNU GPL, read the file 'COPYING' for more information
10 */
11
12#include "live_effects/effect.h"
13#include "live_effects/parameter/parameter.h"
14#include "live_effects/parameter/point.h"
15#include "live_effects/parameter/originalpath.h"
16#include "live_effects/parameter/vector.h"
17#include "live_effects/parameter/bool.h"
18#include "live_effects/parameter/transformedpoint.h"
19
20namespace Inkscape {
21namespace LivePathEffect {
22
23class LPEAttachPath : public Effect {
24public:
25 LPEAttachPath(LivePathEffectObject *lpeobject);
26 virtual ~LPEAttachPath();
27
28 virtual void doEffect (SPCurve * curve);
29 virtual void resetDefaults(SPItem const * item);
30
31private:
32 LPEAttachPath(const LPEAttachPath&);
33 LPEAttachPath& operator=(const LPEAttachPath&);
34
35 Geom::Point curve_start_previous_origin;
36 Geom::Point curve_end_previous_origin;
37
38 OriginalPathParam start_path;
39 ScalarParam start_path_position;
40 TransformedPointParam start_path_curve_start;
41 VectorParam start_path_curve_end;
42
43 OriginalPathParam end_path;
44 ScalarParam end_path_position;
45 TransformedPointParam end_path_curve_start;
46 VectorParam end_path_curve_end;
47};
48
49}; //namespace LivePathEffect
50}; //namespace Inkscape
51
52#endif
053
=== added file 'src/live_effects/lpe-bounding-box.cpp'
--- src/live_effects/lpe-bounding-box.cpp 1970-01-01 00:00:00 +0000
+++ src/live_effects/lpe-bounding-box.cpp 2014-09-07 17:02:32 +0000
@@ -0,0 +1,67 @@
1/*
2 * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
3 *
4 * Released under GNU GPL, read the file 'COPYING' for more information
5 */
6
7#include <glibmm/i18n.h>
8
9#include "live_effects/lpe-bounding-box.h"
10
11#include "display/curve.h"
12#include "sp-item.h"
13#include "2geom/path.h"
14#include "sp-shape.h"
15#include "sp-text.h"
16#include "2geom/bezier-curve.h"
17#include "lpe-bounding-box.h"
18
19namespace Inkscape {
20namespace LivePathEffect {
21
22LPEBoundingBox::LPEBoundingBox(LivePathEffectObject *lpeobject) :
23 Effect(lpeobject),
24 linked_path(_("Linked path:"), _("Path from which to take the original path data"), "linkedpath", &wr, this),
25 visual_bounds(_("Visual Bounds"), _("Uses the visual bounding box"), "visualbounds", &wr, this)
26{
27 registerParameter( dynamic_cast<Parameter *>(&linked_path) );
28 registerParameter( dynamic_cast<Parameter *>(&visual_bounds) );
29 //perceived_path = true;
30}
31
32LPEBoundingBox::~LPEBoundingBox()
33{
34
35}
36
37void LPEBoundingBox::doEffect (SPCurve * curve)
38{
39 if (curve) {
40 if ( linked_path.linksToPath() && linked_path.getObject() ) {
41 SPItem * item = linked_path.getObject();
42 Geom::OptRect bbox = visual_bounds.get_value() ? item->visualBounds() : item->geometricBounds();
43 Geom::Path p(Geom::Point(bbox->left(), bbox->top()));
44 p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->top()));
45 p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->bottom()));
46 p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->bottom()));
47 p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->top()));
48 std::vector<Geom::Path> out;
49 out.push_back(p);
50 curve->set_pathvector(out);
51 }
52 }
53}
54
55} // namespace LivePathEffect
56} /* namespace Inkscape */
57
58/*
59 Local Variables:
60 mode:c++
61 c-file-style:"stroustrup"
62 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
63 indent-tabs-mode:nil
64 fill-column:99
65 End:
66*/
67// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
068
=== added file 'src/live_effects/lpe-bounding-box.h'
--- src/live_effects/lpe-bounding-box.h 1970-01-01 00:00:00 +0000
+++ src/live_effects/lpe-bounding-box.h 2014-09-07 17:02:32 +0000
@@ -0,0 +1,37 @@
1#ifndef INKSCAPE_LPE_BOUNDING_BOX_H
2#define INKSCAPE_LPE_BOUNDING_BOX_H
3
4/*
5 * Inkscape::LPEFillBetweenStrokes
6 *
7 * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
8 *
9 * Released under GNU GPL, read the file 'COPYING' for more information
10 */
11
12#include "live_effects/effect.h"
13#include "live_effects/parameter/originalpath.h"
14
15namespace Inkscape {
16namespace LivePathEffect {
17
18class LPEBoundingBox : public Effect {
19public:
20 LPEBoundingBox(LivePathEffectObject *lpeobject);
21 virtual ~LPEBoundingBox();
22
23 virtual void doEffect (SPCurve * curve);
24
25private:
26 OriginalPathParam linked_path;
27 BoolParam visual_bounds;
28
29private:
30 LPEBoundingBox(const LPEBoundingBox&);
31 LPEBoundingBox& operator=(const LPEBoundingBox&);
32};
33
34}; //namespace LivePathEffect
35}; //namespace Inkscape
36
37#endif
038
=== added file 'src/live_effects/lpe-ellipse_5pts.cpp'
--- src/live_effects/lpe-ellipse_5pts.cpp 1970-01-01 00:00:00 +0000
+++ src/live_effects/lpe-ellipse_5pts.cpp 2014-09-07 17:02:32 +0000
@@ -0,0 +1,214 @@
1/** \file
2 * LPE "Ellipse through 5 points" implementation
3 */
4
5/*
6 * Authors:
7 * Theodore Janeczko
8 *
9 * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
10 *
11 * Released under GNU GPL, read the file 'COPYING' for more information
12 */
13
14#include "live_effects/lpe-ellipse_5pts.h"
15
16// You might need to include other 2geom files. You can add them here:
17#include <glibmm/i18n.h>
18#include <2geom/path.h>
19#include <2geom/circle.h>
20#include <2geom/ellipse.h>
21#include <2geom/path-sink.h>
22#include "inkscape.h"
23#include "desktop.h"
24#include "message-stack.h"
25
26namespace Inkscape {
27namespace LivePathEffect {
28
29LPEEllipse5Pts::LPEEllipse5Pts(LivePathEffectObject *lpeobject) :
30 Effect(lpeobject)
31{
32 //perceived_path = true;
33}
34
35LPEEllipse5Pts::~LPEEllipse5Pts()
36{
37}
38
39static double _det3(double (*mat)[3])
40{
41 for (int i = 0; i < 2; i++)
42 {
43 for (int j = i + 1; j < 3; j++)
44 {
45 for (int k = i + 1; k < 3; k++)
46 {
47 mat[j][k] = (mat[j][k] * mat[i][i] - mat[j][i] * mat[i][k]);
48 if (i) mat[j][k] /= mat[i-1][i-1];
49 }
50 }
51 }
52 return mat[2][2];
53}
54static double _det5(double (*mat)[5])
55{
56 for (int i = 0; i < 4; i++)
57 {
58 for (int j = i + 1; j < 5; j++)
59 {
60 for (int k = i + 1; k < 5; k++)
61 {
62 mat[j][k] = (mat[j][k] * mat[i][i] - mat[j][i] * mat[i][k]);
63 if (i) mat[j][k] /= mat[i-1][i-1];
64 }
65 }
66 }
67 return mat[4][4];
68}
69
70std::vector<Geom::Path>
71LPEEllipse5Pts::doEffect_path (std::vector<Geom::Path> const & path_in)
72{
73 std::vector<Geom::Path> path_out = std::vector<Geom::Path>();
74
75 if (path_in[0].size() < 4) {
76
77 SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Five points required for constructing an ellipse"));
78 return path_in;
79 }
80 // we assume that the path has >= 3 nodes
81 Geom::Point A = path_in[0].initialPoint();
82 Geom::Point B = path_in[0].pointAt(1);
83 Geom::Point C = path_in[0].pointAt(2);
84 Geom::Point D = path_in[0].pointAt(3);
85 Geom::Point E = path_in[0].pointAt(4);
86
87 using namespace Geom;
88
89 double rowmajor_matrix[5][6] =
90 {
91 {A.x()*A.x(), A.x()*A.y(), A.y()*A.y(), A.x(), A.y(), 1},
92 {B.x()*B.x(), B.x()*B.y(), B.y()*B.y(), B.x(), B.y(), 1},
93 {C.x()*C.x(), C.x()*C.y(), C.y()*C.y(), C.x(), C.y(), 1},
94 {D.x()*D.x(), D.x()*D.y(), D.y()*D.y(), D.x(), D.y(), 1},
95 {E.x()*E.x(), E.x()*E.y(), E.y()*E.y(), E.x(), E.y(), 1}
96 };
97
98 double mat_a[5][5] =
99 {
100 {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]},
101 {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]},
102 {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]},
103 {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]},
104 {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]}
105 };
106 double mat_b[5][5] =
107 {
108 {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]},
109 {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]},
110 {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]},
111 {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]},
112 {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]}
113 };
114 double mat_c[5][5] =
115 {
116 {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]},
117 {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]},
118 {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]},
119 {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]},
120 {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]}
121 };
122 double mat_d[5][5] =
123 {
124 {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]},
125 {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]},
126 {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]},
127 {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]},
128 {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]}
129 };
130 double mat_e[5][5] =
131 {
132 {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]},
133 {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]},
134 {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]},
135 {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]},
136 {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]}
137 };
138 double mat_f[5][5] =
139 {
140 {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]},
141 {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]},
142 {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]},
143 {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]},
144 {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]}
145 };
146
147 double a1 = _det5(mat_a);
148 double b1 = -_det5(mat_b);
149 double c1 = _det5(mat_c);
150 double d1 = -_det5(mat_d);
151 double e1 = _det5(mat_e);
152 double f1 = -_det5(mat_f);
153
154 double mat_check[][3] =
155 {
156 {a1, b1/2, d1/2},
157 {b1/2, c1, e1/2},
158 {d1/2, e1/2, f1}
159 };
160
161 if (_det3(mat_check) == 0 || a1*c1 - b1*b1/4 <= 0) {
162 SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No ellipse found for specified points"));
163 return path_in;
164 }
165
166 Geom::Ellipse el(a1, b1, c1, d1, e1, f1);
167
168 double s, e;
169 double x0, y0, x1, y1, x2, y2, x3, y3;
170 double len;
171
172 // figure out if we have a slice, guarding against rounding errors
173
174 Path p(Geom::Point(cos(0), sin(0)));
175
176 double end = 2 * M_PI;
177 for (s = 0; s < end; s += M_PI_2) {
178 e = s + M_PI_2;
179 if (e > end)
180 e = end;
181 len = 4*tan((e - s)/4)/3;
182 x0 = cos(s);
183 y0 = sin(s);
184 x1 = x0 + len * cos(s + M_PI_2);
185 y1 = y0 + len * sin(s + M_PI_2);
186 x3 = cos(e);
187 y3 = sin(e);
188 x2 = x3 + len * cos(e - M_PI_2);
189 y2 = y3 + len * sin(e - M_PI_2);
190 p.appendNew<Geom::CubicBezier>(Geom::Point(x1,y1), Geom::Point(x2,y2), Geom::Point(x3,y3));
191 }
192
193 Geom::Affine aff = Geom::Scale(el.ray(Geom::X), el.ray(Geom::Y)) * Geom::Rotate(el.rot_angle()) * Geom::Translate(el.center());
194
195 path_out.push_back(p * aff);
196
197 return path_out;
198}
199
200/* ######################## */
201
202} //namespace LivePathEffect
203} /* namespace Inkscape */
204
205/*
206 Local Variables:
207 mode:c++
208 c-file-style:"stroustrup"
209 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
210 indent-tabs-mode:nil
211 fill-column:99
212 End:
213*/
214// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
0215
=== added file 'src/live_effects/lpe-ellipse_5pts.h'
--- src/live_effects/lpe-ellipse_5pts.h 1970-01-01 00:00:00 +0000
+++ src/live_effects/lpe-ellipse_5pts.h 2014-09-07 17:02:32 +0000
@@ -0,0 +1,50 @@
1#ifndef INKSCAPE_LPE_ELLIPSE_5PTS_H
2#define INKSCAPE_LPE_ELLIPSE_5PTS_H
3
4/** \file
5 * LPE "Ellipse through 5 points" implementation
6 */
7
8/*
9 * Authors:
10 * Theodore Janeczko
11 *
12 * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
13 *
14 * Released under GNU GPL, read the file 'COPYING' for more information
15 */
16
17#include "live_effects/effect.h"
18#include "live_effects/parameter/parameter.h"
19#include "live_effects/parameter/point.h"
20
21namespace Inkscape {
22namespace LivePathEffect {
23
24class LPEEllipse5Pts : public Effect {
25public:
26 LPEEllipse5Pts(LivePathEffectObject *lpeobject);
27 virtual ~LPEEllipse5Pts();
28
29 virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
30
31private:
32 LPEEllipse5Pts(const LPEEllipse5Pts&);
33 LPEEllipse5Pts& operator=(const LPEEllipse5Pts&);
34};
35
36} //namespace LivePathEffect
37} //namespace Inkscape
38
39#endif
40
41/*
42 Local Variables:
43 mode:c++
44 c-file-style:"stroustrup"
45 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
46 indent-tabs-mode:nil
47 fill-column:99
48 End:
49*/
50// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
051
=== added file 'src/live_effects/lpe-fill-between-many.cpp'
--- src/live_effects/lpe-fill-between-many.cpp 1970-01-01 00:00:00 +0000
+++ src/live_effects/lpe-fill-between-many.cpp 2014-09-07 17:02:32 +0000
@@ -0,0 +1,78 @@
1/*
2 * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
3 *
4 * Released under GNU GPL, read the file 'COPYING' for more information
5 */
6
7#include <gtkmm/box.h>
8
9#include "live_effects/lpe-fill-between-many.h"
10
11#include "display/curve.h"
12#include "sp-item.h"
13#include "2geom/path.h"
14#include "sp-shape.h"
15#include "sp-text.h"
16#include "2geom/bezier-curve.h"
17
18#include <glibmm/i18n.h>
19
20namespace Inkscape {
21namespace LivePathEffect {
22
23LPEFillBetweenMany::LPEFillBetweenMany(LivePathEffectObject *lpeobject) :
24 Effect(lpeobject),
25 linked_paths(_("Linked path:"), _("Paths from which to take the original path data"), "linkedpaths", &wr, this)
26{
27 registerParameter( dynamic_cast<Parameter *>(&linked_paths) );
28 //perceived_path = true;
29}
30
31LPEFillBetweenMany::~LPEFillBetweenMany()
32{
33
34}
35
36void LPEFillBetweenMany::doEffect (SPCurve * curve)
37{
38 std::vector<Geom::Path> res_pathv;
39 SPItem * firstObj = NULL;
40 for (std::vector<PathAndDirection*>::iterator iter = linked_paths._vector.begin(); iter != linked_paths._vector.end(); iter++) {
41 SPObject *obj;
42 if ((*iter)->ref.isAttached() && (obj = (*iter)->ref.getObject()) && SP_IS_ITEM(obj) && !(*iter)->_pathvector.empty()) {
43 Geom::Path linked_path;
44 if ((*iter)->reversed) {
45 linked_path = (*iter)->_pathvector.front().reverse();
46 } else {
47 linked_path = (*iter)->_pathvector.front();
48 }
49
50 if (!res_pathv.empty()) {
51 linked_path = linked_path * SP_ITEM(obj)->getRelativeTransform(firstObj);
52 res_pathv.front().appendNew<Geom::LineSegment>(linked_path.initialPoint());
53 res_pathv.front().append(linked_path);
54 } else {
55 firstObj = SP_ITEM(obj);
56 res_pathv.push_back(linked_path);
57 }
58 }
59 }
60 if (!res_pathv.empty()) {
61 res_pathv.front().close();
62 }
63 curve->set_pathvector(res_pathv);
64}
65
66} // namespace LivePathEffect
67} /* namespace Inkscape */
68
69/*
70 Local Variables:
71 mode:c++
72 c-file-style:"stroustrup"
73 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
74 indent-tabs-mode:nil
75 fill-column:99
76 End:
77*/
78// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
079
=== added file 'src/live_effects/lpe-fill-between-many.h'
--- src/live_effects/lpe-fill-between-many.h 1970-01-01 00:00:00 +0000
+++ src/live_effects/lpe-fill-between-many.h 2014-09-07 17:02:32 +0000
@@ -0,0 +1,36 @@
1#ifndef INKSCAPE_LPE_FILL_BETWEEN_MANY_H
2#define INKSCAPE_LPE_FILL_BETWEEN_MANY_H
3
4/*
5 * Inkscape::LPEFillBetweenStrokes
6 *
7 * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
8 *
9 * Released under GNU GPL, read the file 'COPYING' for more information
10 */
11
12#include "live_effects/effect.h"
13#include "live_effects/parameter/originalpatharray.h"
14
15namespace Inkscape {
16namespace LivePathEffect {
17
18class LPEFillBetweenMany : public Effect {
19public:
20 LPEFillBetweenMany(LivePathEffectObject *lpeobject);
21 virtual ~LPEFillBetweenMany();
22
23 virtual void doEffect (SPCurve * curve);
24
25private:
26 OriginalPathArrayParam linked_paths;
27
28private:
29 LPEFillBetweenMany(const LPEFillBetweenMany&);
30 LPEFillBetweenMany& operator=(const LPEFillBetweenMany&);
31};
32
33}; //namespace LivePathEffect
34}; //namespace Inkscape
35
36#endif
037
=== added file 'src/live_effects/lpe-fill-between-strokes.cpp'
--- src/live_effects/lpe-fill-between-strokes.cpp 1970-01-01 00:00:00 +0000
+++ src/live_effects/lpe-fill-between-strokes.cpp 2014-09-07 17:02:32 +0000
@@ -0,0 +1,116 @@
1/*
2 * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
3 *
4 * Released under GNU GPL, read the file 'COPYING' for more information
5 */
6
7#include <glibmm/i18n.h>
8
9#include "live_effects/lpe-fill-between-strokes.h"
10
11#include "display/curve.h"
12#include "sp-item.h"
13#include "2geom/path.h"
14#include "sp-shape.h"
15#include "sp-text.h"
16#include "2geom/bezier-curve.h"
17
18namespace Inkscape {
19namespace LivePathEffect {
20
21LPEFillBetweenStrokes::LPEFillBetweenStrokes(LivePathEffectObject *lpeobject) :
22 Effect(lpeobject),
23 linked_path(_("Linked path:"), _("Path from which to take the original path data"), "linkedpath", &wr, this),
24 second_path(_("Second path:"), _("Second path from which to take the original path data"), "secondpath", &wr, this),
25 reverse_second(_("Reverse Second"), _("Reverses the second path order"), "reversesecond", &wr, this)
26{
27 registerParameter( dynamic_cast<Parameter *>(&linked_path) );
28 registerParameter( dynamic_cast<Parameter *>(&second_path) );
29 registerParameter( dynamic_cast<Parameter *>(&reverse_second) );
30 //perceived_path = true;
31}
32
33LPEFillBetweenStrokes::~LPEFillBetweenStrokes()
34{
35
36}
37
38void LPEFillBetweenStrokes::doEffect (SPCurve * curve)
39{
40 if (curve) {
41 if ( linked_path.linksToPath() && second_path.linksToPath() && linked_path.getObject() && second_path.getObject() ) {
42 std::vector<Geom::Path> linked_pathv = linked_path.get_pathvector();
43 std::vector<Geom::Path> second_pathv = second_path.get_pathvector();
44 std::vector<Geom::Path> result_linked_pathv;
45 std::vector<Geom::Path> result_second_pathv;
46 Geom::Affine second_transform = second_path.getObject()->getRelativeTransform(linked_path.getObject());
47
48 for (std::vector<Geom::Path>::iterator iter = linked_pathv.begin(); iter != linked_pathv.end(); ++iter)
49 {
50 result_linked_pathv.push_back((*iter));
51 }
52 for (std::vector<Geom::Path>::iterator iter = second_pathv.begin(); iter != second_pathv.end(); ++iter)
53 {
54 result_second_pathv.push_back((*iter) * second_transform);
55 }
56
57 if ( !result_linked_pathv.empty() && !result_second_pathv.empty() && !result_linked_pathv.front().closed() ) {
58 if (reverse_second.get_value())
59 {
60 result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().finalPoint());
61 result_linked_pathv.front().append(result_second_pathv.front().reverse());
62 }
63 else
64 {
65 result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().initialPoint());
66 result_linked_pathv.front().append(result_second_pathv.front());
67 }
68 curve->set_pathvector(result_linked_pathv);
69 }
70 else if ( !result_linked_pathv.empty() ) {
71 curve->set_pathvector(result_linked_pathv);
72 }
73 else if ( !result_second_pathv.empty() ) {
74 curve->set_pathvector(result_second_pathv);
75 }
76 }
77 else if ( linked_path.linksToPath() && linked_path.getObject() ) {
78 std::vector<Geom::Path> linked_pathv = linked_path.get_pathvector();
79 std::vector<Geom::Path> result_pathv;
80
81 for (std::vector<Geom::Path>::iterator iter = linked_pathv.begin(); iter != linked_pathv.end(); ++iter)
82 {
83 result_pathv.push_back((*iter));
84 }
85 if ( !result_pathv.empty() ) {
86 curve->set_pathvector(result_pathv);
87 }
88 }
89 else if ( second_path.linksToPath() && second_path.getObject() ) {
90 std::vector<Geom::Path> second_pathv = second_path.get_pathvector();
91 std::vector<Geom::Path> result_pathv;
92
93 for (std::vector<Geom::Path>::iterator iter = second_pathv.begin(); iter != second_pathv.end(); ++iter)
94 {
95 result_pathv.push_back((*iter));
96 }
97 if ( !result_pathv.empty() ) {
98 curve->set_pathvector(result_pathv);
99 }
100 }
101 }
102}
103
104} // namespace LivePathEffect
105} /* namespace Inkscape */
106
107/*
108 Local Variables:
109 mode:c++
110 c-file-style:"stroustrup"
111 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
112 indent-tabs-mode:nil
113 fill-column:99
114 End:
115*/
116// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
0117
=== added file 'src/live_effects/lpe-fill-between-strokes.h'
--- src/live_effects/lpe-fill-between-strokes.h 1970-01-01 00:00:00 +0000
+++ src/live_effects/lpe-fill-between-strokes.h 2014-09-07 17:02:32 +0000
@@ -0,0 +1,38 @@
1#ifndef INKSCAPE_LPE_FILL_BETWEEN_STROKES_H
2#define INKSCAPE_LPE_FILL_BETWEEN_STROKES_H
3
4/*
5 * Inkscape::LPEFillBetweenStrokes
6 *
7 * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
8 *
9 * Released under GNU GPL, read the file 'COPYING' for more information
10 */
11
12#include "live_effects/effect.h"
13#include "live_effects/parameter/originalpath.h"
14
15namespace Inkscape {
16namespace LivePathEffect {
17
18class LPEFillBetweenStrokes : public Effect {
19public:
20 LPEFillBetweenStrokes(LivePathEffectObject *lpeobject);
21 virtual ~LPEFillBetweenStrokes();
22
23 virtual void doEffect (SPCurve * curve);
24
25private:
26 OriginalPathParam linked_path;
27 OriginalPathParam second_path;
28 BoolParam reverse_second;
29
30private:
31 LPEFillBetweenStrokes(const LPEFillBetweenStrokes&);
32 LPEFillBetweenStrokes& operator=(const LPEFillBetweenStrokes&);
33};
34
35}; //namespace LivePathEffect
36}; //namespace Inkscape
37
38#endif
039
=== added file 'src/live_effects/lpe-jointype.cpp'
--- src/live_effects/lpe-jointype.cpp 1970-01-01 00:00:00 +0000
+++ src/live_effects/lpe-jointype.cpp 2014-09-07 17:02:32 +0000
@@ -0,0 +1,184 @@
1/** \file
2 * LPE "Join Type" implementation
3 */
4 /* Authors:
5 *
6 * Liam P White
7 *
8 * Copyright (C) 2014 Authors
9 *
10 * Released under GNU GPL v2, read the file 'COPYING' for more information
11 */
12
13#include <math.h>
14
15#include "live_effects/parameter/enum.h"
16#include "live_effects/pathoutlineprovider.h"
17
18#include "sp-shape.h"
19#include "style.h"
20#include "xml/repr.h"
21#include "sp-paint-server.h"
22#include "svg/svg-color.h"
23#include "desktop-style.h"
24#include "svg/css-ostringstream.h"
25#include "display/curve.h"
26
27#include <2geom/path.h>
28#include <2geom/svg-elliptical-arc.h>
29
30#include "lpe-jointype.h"
31
32namespace Inkscape {
33namespace LivePathEffect {
34
35static const Util::EnumData<unsigned> JoinTypeData[] = {
36 {LINEJOIN_STRAIGHT, N_("Beveled"), "bevel"},
37 {LINEJOIN_ROUND, N_("Rounded"), "round"},
38 {LINEJOIN_POINTY, N_("Miter"), "miter"},
39 {LINEJOIN_REFLECTED, N_("Reflected"), "extrapolated"},
40 {LINEJOIN_EXTRAPOLATED, N_("Extrapolated arc"), "extrp_arc"}
41};
42
43static const Util::EnumData<unsigned> CapTypeData[] = {
44 {BUTT_STRAIGHT, N_("Butt"), "butt"},
45 {BUTT_ROUND, N_("Rounded"), "round"},
46 {BUTT_SQUARE, N_("Square"), "square"},
47 {BUTT_POINTY, N_("Peak"), "peak"},
48 {BUTT_LEANED, N_("Leaned"), "leaned"}
49};
50
51static const Util::EnumDataConverter<unsigned> CapTypeConverter(CapTypeData, sizeof(CapTypeData)/sizeof(*CapTypeData));
52static const Util::EnumDataConverter<unsigned> JoinTypeConverter(JoinTypeData, sizeof(JoinTypeData)/sizeof(*JoinTypeData));
53
54LPEJoinType::LPEJoinType(LivePathEffectObject *lpeobject) :
55 Effect(lpeobject),
56 line_width(_("Line width"), _("Thickness of the stroke"), "line_width", &wr, this, 1.),
57 linecap_type(_("Line cap"), _("The end shape of the stroke"), "linecap_type", CapTypeConverter, &wr, this, butt_straight),
58 linejoin_type(_("Join:"), _("Determines the shape of the path's corners"), "linejoin_type", JoinTypeConverter, &wr, this, LINEJOIN_EXTRAPOLATED),
59 start_lean(_("Start path lean"), _("Start path lean"), "start_lean", &wr, this, 0.),
60 end_lean(_("End path lean"), _("End path lean"), "end_lean", &wr, this, 0.),
61 miter_limit(_("Miter limit:"), _("Maximum length of the miter join (in units of stroke width)"), "miter_limit", &wr, this, 100.),
62 attempt_force_join(_("Force miter"), _("Overrides the miter limit and forces a join."), "attempt_force_join", &wr, this, true)
63{
64 show_orig_path = true;
65 registerParameter( dynamic_cast<Parameter *>(&linecap_type) );
66 registerParameter( dynamic_cast<Parameter *>(&line_width) );
67 registerParameter( dynamic_cast<Parameter *>(&linejoin_type) );
68 registerParameter( dynamic_cast<Parameter *>(&start_lean) );
69 registerParameter( dynamic_cast<Parameter *>(&end_lean) );
70 registerParameter( dynamic_cast<Parameter *>(&miter_limit) );
71 registerParameter( dynamic_cast<Parameter *>(&attempt_force_join) );
72 was_initialized = false;
73 start_lean.param_set_range(-1,1);
74 start_lean.param_set_increments(0.1, 0.1);
75 start_lean.param_set_digits(4);
76 end_lean.param_set_range(-1,1);
77 end_lean.param_set_increments(0.1, 0.1);
78 end_lean.param_set_digits(4);
79}
80
81LPEJoinType::~LPEJoinType()
82{
83}
84
85//from LPEPowerStroke -- sets fill if stroke color because we will
86//be converting to a fill to make the new join.
87
88void LPEJoinType::doOnApply(SPLPEItem const* lpeitem)
89{
90 if (SP_IS_SHAPE(lpeitem)) {
91 SPLPEItem* item = const_cast<SPLPEItem*>(lpeitem);
92 double width = (lpeitem && lpeitem->style) ? lpeitem->style->stroke_width.computed : 1.;
93
94 SPCSSAttr *css = sp_repr_css_attr_new ();
95 if (true) {
96 if (lpeitem->style->stroke.isPaintserver()) {
97 SPPaintServer * server = lpeitem->style->getStrokePaintServer();
98 if (server) {
99 Glib::ustring str;
100 str += "url(#";
101 str += server->getId();
102 str += ")";
103 sp_repr_css_set_property (css, "fill", str.c_str());
104 }
105 } else if (lpeitem->style->stroke.isColor()) {
106 gchar c[64];
107 sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value)));
108 sp_repr_css_set_property (css, "fill", c);
109 } else {
110 sp_repr_css_set_property (css, "fill", "none");
111 }
112 } else {
113 sp_repr_css_unset_property (css, "fill");
114 }
115
116 sp_repr_css_set_property(css, "stroke", "none");
117
118 sp_desktop_apply_css_recursive(item, css, true);
119 sp_repr_css_attr_unref (css);
120 if (!was_initialized)
121 {
122 was_initialized = true;
123 line_width.param_set_value(width);
124 }
125 } else {
126 g_warning("LPE Join Type can only be applied to paths (not groups).");
127 }
128}
129
130//from LPEPowerStroke -- sets stroke color from existing fill color
131
132void LPEJoinType::doOnRemove(SPLPEItem const* lpeitem)
133{
134
135 if (SP_IS_SHAPE(lpeitem)) {
136 SPLPEItem *item = const_cast<SPLPEItem*>(lpeitem);
137
138 SPCSSAttr *css = sp_repr_css_attr_new ();
139 if (true) {
140 if (lpeitem->style->fill.isPaintserver()) {
141 SPPaintServer * server = lpeitem->style->getFillPaintServer();
142 if (server) {
143 Glib::ustring str;
144 str += "url(#";
145 str += server->getId();
146 str += ")";
147 sp_repr_css_set_property (css, "stroke", str.c_str());
148 }
149 } else if (lpeitem->style->fill.isColor()) {
150 gchar c[64];
151 sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value)));
152 sp_repr_css_set_property (css, "stroke", c);
153 } else {
154 sp_repr_css_set_property (css, "stroke", "none");
155 }
156 } else {
157 sp_repr_css_unset_property (css, "stroke");
158 }
159
160 Inkscape::CSSOStringStream os;
161 os << fabs(line_width);
162 sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
163
164 sp_repr_css_set_property(css, "fill", "none");
165
166 sp_desktop_apply_css_recursive(item, css, true);
167 sp_repr_css_attr_unref (css);
168 item->updateRepr();
169 }
170}
171
172//NOTE: I originally had all the outliner functions defined in here, but they were actually useful
173//enough for other LPEs so I moved them all into pathoutlineprovider.cpp. The code here is just a
174//wrapper around it.
175std::vector<Geom::Path> LPEJoinType::doEffect_path(std::vector<Geom::Path> const & path_in)
176{
177 return Outline::PathVectorOutline(path_in, line_width, static_cast<ButtTypeMod>(linecap_type.get_value()),
178 static_cast<LineJoinType>(linejoin_type.get_value()),
179 (attempt_force_join ? std::numeric_limits<double>::max() : miter_limit),
180 start_lean/2 ,end_lean/2);
181}
182
183} //namespace LivePathEffect
184} //namespace Inkscape
0185
=== added file 'src/live_effects/lpe-jointype.h'
--- src/live_effects/lpe-jointype.h 1970-01-01 00:00:00 +0000
+++ src/live_effects/lpe-jointype.h 2014-09-07 17:02:32 +0000
@@ -0,0 +1,45 @@
1/* Authors:
2 * Liam P White
3 *
4 * Copyright (C) 2014 Authors
5 *
6 * Released under GNU GPL v2, read the file COPYING for more information
7 */
8#ifndef INKSCAPE_LPE_JOINTYPE_H
9#define INKSCAPE_LPE_JOINTYPE_H
10
11#include "live_effects/effect.h"
12#include "live_effects/parameter/parameter.h"
13#include "live_effects/parameter/point.h"
14#include "live_effects/parameter/enum.h"
15
16namespace Inkscape {
17namespace LivePathEffect {
18
19class LPEJoinType : public Effect {
20public:
21 LPEJoinType(LivePathEffectObject *lpeobject);
22 virtual ~LPEJoinType();
23
24 virtual void doOnApply(SPLPEItem const* lpeitem);
25 virtual void doOnRemove(SPLPEItem const* lpeitem);
26 virtual std::vector <Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
27
28private:
29 LPEJoinType(const LPEJoinType&);
30 LPEJoinType& operator=(const LPEJoinType&);
31
32 ScalarParam line_width;
33 EnumParam<unsigned> linecap_type;
34 EnumParam<unsigned> linejoin_type;
35 ScalarParam start_lean;
36 ScalarParam end_lean;
37 ScalarParam miter_limit;
38 BoolParam attempt_force_join;
39 bool was_initialized;
40};
41
42} //namespace LivePathEffect
43} //namespace Inkscape
44
45#endif
046
=== modified file 'src/live_effects/lpe-knot.cpp'
--- src/live_effects/lpe-knot.cpp 2014-08-31 18:17:26 +0000
+++ src/live_effects/lpe-knot.cpp 2014-09-07 17:02:32 +0000
@@ -537,6 +537,10 @@
537{537{
538 using namespace Geom;538 using namespace Geom;
539 original_bbox(lpeitem);539 original_bbox(lpeitem);
540
541 if (SP_IS_PATH(lpeitem)) {
542 supplied_path = SP_PATH(lpeitem)->getCurve()->get_pathvector();
543 }
540544
541 gpaths.clear();545 gpaths.clear();
542 gstroke_widths.clear();546 gstroke_widths.clear();
543547
=== modified file 'src/live_effects/lpe-knot.h'
--- src/live_effects/lpe-knot.h 2014-06-16 21:34:16 +0000
+++ src/live_effects/lpe-knot.h 2014-09-07 17:02:32 +0000
@@ -64,7 +64,8 @@
64 void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item);64 void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item);
6565
66protected:66protected:
67 virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec);67 virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec);
68 std::vector<Geom::Path> supplied_path; //for knotholder business
68 69
69private:70private:
70 void updateSwitcher();71 void updateSwitcher();
7172
=== modified file 'src/live_effects/lpe-powerstroke-interpolators.h'
--- src/live_effects/lpe-powerstroke-interpolators.h 2014-08-23 17:14:02 +0000
+++ src/live_effects/lpe-powerstroke-interpolators.h 2014-09-07 17:02:32 +0000
@@ -28,6 +28,7 @@
28 INTERP_CUBICBEZIER,28 INTERP_CUBICBEZIER,
29 INTERP_CUBICBEZIER_JOHAN,29 INTERP_CUBICBEZIER_JOHAN,
30 INTERP_SPIRO,30 INTERP_SPIRO,
31 INTERP_CUBICBEZIER_SMOOTH,
31 INTERP_CENTRIPETAL_CATMULLROM32 INTERP_CENTRIPETAL_CATMULLROM
32};33};
3334
@@ -134,6 +135,43 @@
134 CubicBezierJohan& operator=(const CubicBezierJohan&);135 CubicBezierJohan& operator=(const CubicBezierJohan&);
135};136};
136137
138/// @todo invent name for this class
139class CubicBezierSmooth : public Interpolator {
140public:
141 CubicBezierSmooth(double beta = 0.2) {
142 _beta = beta;
143 };
144 virtual ~CubicBezierSmooth() {};
145
146 virtual Path interpolateToPath(std::vector<Point> const &points) const {
147 Path fit;
148 fit.start(points.at(0));
149 unsigned int num_points = points.size();
150 for (unsigned int i = 1; i < num_points; ++i) {
151 Point p0 = points.at(i-1);
152 Point p1 = points.at(i);
153 Point dx = Point(p1[X] - p0[X], 0);
154 if (i == 1) {
155 fit.appendNew<CubicBezier>(p0, p1-0.75*dx, p1);
156 } else if (i == points.size() - 1) {
157 fit.appendNew<CubicBezier>(p0+0.75*dx, p1, p1);
158 } else {
159 fit.appendNew<CubicBezier>(p0+_beta*dx, p1-_beta*dx, p1);
160 }
161 }
162 return fit;
163 };
164
165 void setBeta(double beta) {
166 _beta = beta;
167 }
168
169 double _beta;
170
171private:
172 CubicBezierSmooth(const CubicBezierSmooth&);
173 CubicBezierSmooth& operator=(const CubicBezierSmooth&);
174};
137175
138class SpiroInterpolator : public Interpolator {176class SpiroInterpolator : public Interpolator {
139public:177public:
@@ -261,6 +299,8 @@
261 return new Geom::Interpolate::CubicBezierJohan();299 return new Geom::Interpolate::CubicBezierJohan();
262 case INTERP_SPIRO:300 case INTERP_SPIRO:
263 return new Geom::Interpolate::SpiroInterpolator();301 return new Geom::Interpolate::SpiroInterpolator();
302 case INTERP_CUBICBEZIER_SMOOTH:
303 return new Geom::Interpolate::CubicBezierSmooth();
264 case INTERP_CENTRIPETAL_CATMULLROM:304 case INTERP_CENTRIPETAL_CATMULLROM:
265 return new Geom::Interpolate::CentripetalCatmullRomInterpolator();305 return new Geom::Interpolate::CentripetalCatmullRomInterpolator();
266 default:306 default:
267307
=== modified file 'src/live_effects/lpe-powerstroke.cpp'
--- src/live_effects/lpe-powerstroke.cpp 2014-08-23 16:39:36 +0000
+++ src/live_effects/lpe-powerstroke.cpp 2014-09-07 17:02:32 +0000
@@ -15,6 +15,11 @@
1515
16#include "sp-shape.h"16#include "sp-shape.h"
17#include "style.h"17#include "style.h"
18#include "xml/repr.h"
19#include "sp-paint-server.h"
20#include "svg/svg-color.h"
21#include "desktop-style.h"
22#include "svg/css-ostringstream.h"
18#include "display/curve.h"23#include "display/curve.h"
1924
20#include <2geom/path.h>25#include <2geom/path.h>
@@ -185,6 +190,7 @@
185namespace LivePathEffect {190namespace LivePathEffect {
186191
187static const Util::EnumData<unsigned> InterpolatorTypeData[] = {192static const Util::EnumData<unsigned> InterpolatorTypeData[] = {
193 {Geom::Interpolate::INTERP_CUBICBEZIER_SMOOTH, N_("CubicBezierSmooth"), "CubicBezierSmooth"},
188 {Geom::Interpolate::INTERP_LINEAR , N_("Linear"), "Linear"},194 {Geom::Interpolate::INTERP_LINEAR , N_("Linear"), "Linear"},
189 {Geom::Interpolate::INTERP_CUBICBEZIER , N_("CubicBezierFit"), "CubicBezierFit"},195 {Geom::Interpolate::INTERP_CUBICBEZIER , N_("CubicBezierFit"), "CubicBezierFit"},
190 {Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN , N_("CubicBezierJohan"), "CubicBezierJohan"},196 {Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN , N_("CubicBezierJohan"), "CubicBezierJohan"},
@@ -223,9 +229,7 @@
223 {LINEJOIN_EXTRP_MITER, N_("Extrapolated"), "extrapolated"},229 {LINEJOIN_EXTRP_MITER, N_("Extrapolated"), "extrapolated"},
224 {LINEJOIN_MITER, N_("Miter"), "miter"},230 {LINEJOIN_MITER, N_("Miter"), "miter"},
225 {LINEJOIN_SPIRO, N_("Spiro"), "spiro"},231 {LINEJOIN_SPIRO, N_("Spiro"), "spiro"},
226#ifdef LPE_ENABLE_TEST_EFFECTS
227 {LINEJOIN_EXTRP_MITER_ARC, N_("Extrapolated arc"), "extrp_arc"}, 232 {LINEJOIN_EXTRP_MITER_ARC, N_("Extrapolated arc"), "extrp_arc"},
228#endif
229};233};
230static const Util::EnumDataConverter<unsigned> LineJoinTypeConverter(LineJoinTypeData, sizeof(LineJoinTypeData)/sizeof(*LineJoinTypeData));234static const Util::EnumDataConverter<unsigned> LineJoinTypeConverter(LineJoinTypeData, sizeof(LineJoinTypeData)/sizeof(*LineJoinTypeData));
231235
@@ -233,12 +237,12 @@
233 Effect(lpeobject),237 Effect(lpeobject),
234 offset_points(_("Offset points"), _("Offset points"), "offset_points", &wr, this),238 offset_points(_("Offset points"), _("Offset points"), "offset_points", &wr, this),
235 sort_points(_("Sort points"), _("Sort offset points according to their time value along the curve"), "sort_points", &wr, this, true),239 sort_points(_("Sort points"), _("Sort offset points according to their time value along the curve"), "sort_points", &wr, this, true),
236 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),240 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),
237 interpolator_beta(_("Smoothness:"), _("Sets the smoothness for the CubicBezierJohan interpolator; 0 = linear interpolation, 1 = smooth"), "interpolator_beta", &wr, this, 0.2),241 interpolator_beta(_("Smoothness:"), _("Sets the smoothness for the CubicBezierJohan interpolator; 0 = linear interpolation, 1 = smooth"), "interpolator_beta", &wr, this, 0.2),
238 start_linecap_type(_("Start cap:"), _("Determines the shape of the path's start"), "start_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ROUND),242 start_linecap_type(_("Start cap:"), _("Determines the shape of the path's start"), "start_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_BUTT),
239 linejoin_type(_("Join:"), _("Determines the shape of the path's corners"), "linejoin_type", LineJoinTypeConverter, &wr, this, LINEJOIN_ROUND),243 linejoin_type(_("Join:"), _("Determines the shape of the path's corners"), "linejoin_type", LineJoinTypeConverter, &wr, this, LINEJOIN_EXTRP_MITER_ARC),
240 miter_limit(_("Miter limit:"), _("Maximum length of the miter (in units of stroke width)"), "miter_limit", &wr, this, 4.),244 miter_limit(_("Miter limit:"), _("Maximum length of the miter (in units of stroke width)"), "miter_limit", &wr, this, 4.),
241 end_linecap_type(_("End cap:"), _("Determines the shape of the path's end"), "end_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ROUND)245 end_linecap_type(_("End cap:"), _("Determines the shape of the path's end"), "end_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_BUTT)
242{246{
243 show_orig_path = true;247 show_orig_path = true;
244248
@@ -267,20 +271,52 @@
267LPEPowerStroke::doOnApply(SPLPEItem const* lpeitem)271LPEPowerStroke::doOnApply(SPLPEItem const* lpeitem)
268{272{
269 if (SP_IS_SHAPE(lpeitem)) {273 if (SP_IS_SHAPE(lpeitem)) {
274 SPLPEItem* item = const_cast<SPLPEItem*>(lpeitem);
270 std::vector<Geom::Point> points;275 std::vector<Geom::Point> points;
271 Geom::PathVector const &pathv = SP_SHAPE(lpeitem)->_curve->get_pathvector();276 Geom::PathVector const &pathv = SP_SHAPE(lpeitem)->_curve->get_pathvector();
272 double width = (lpeitem && lpeitem->style) ? lpeitem->style->stroke_width.computed : 1.;277 double width = (lpeitem && lpeitem->style) ? lpeitem->style->stroke_width.computed / 2 : 1.;
278
279 SPCSSAttr *css = sp_repr_css_attr_new ();
280 if (true) {
281 if (lpeitem->style->stroke.isPaintserver()) {
282 SPPaintServer * server = lpeitem->style->getStrokePaintServer();
283 if (server) {
284 Glib::ustring str;
285 str += "url(#";
286 str += server->getId();
287 str += ")";
288 sp_repr_css_set_property (css, "fill", str.c_str());
289 }
290 } else if (lpeitem->style->stroke.isColor()) {
291 gchar c[64];
292 sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value)));
293 sp_repr_css_set_property (css, "fill", c);
294 } else {
295 sp_repr_css_set_property (css, "fill", "none");
296 }
297 } else {
298 sp_repr_css_unset_property (css, "fill");
299 }
300
301 sp_repr_css_set_property(css, "stroke", "none");
302
303 sp_desktop_apply_css_recursive(item, css, true);
304 sp_repr_css_attr_unref (css);
305
306 item->updateRepr();
273 if (pathv.empty()) {307 if (pathv.empty()) {
274 points.push_back( Geom::Point(0.,width) );308 points.push_back( Geom::Point(0.2,width) );
275 points.push_back( Geom::Point(0.5,width) );309 points.push_back( Geom::Point(0.5,width) );
276 points.push_back( Geom::Point(1.,width) );310 points.push_back( Geom::Point(0.8,width) );
277 } else {311 } else {
278 Geom::Path const &path = pathv.front();312 Geom::Path const &path = pathv.front();
279 Geom::Path::size_type const size = path.size_default();313 Geom::Path::size_type const size = path.size_default();
280 points.push_back( Geom::Point(0.,width) );314 if (!path.closed()) {
315 points.push_back( Geom::Point(0.2,width) );
316 }
281 points.push_back( Geom::Point(0.5*size,width) );317 points.push_back( Geom::Point(0.5*size,width) );
282 if (!path.closed()) {318 if (!path.closed()) {
283 points.push_back( Geom::Point(size,width) );319 points.push_back( Geom::Point(size - 0.2,width) );
284 }320 }
285 }321 }
286 offset_points.param_set_and_write_new_value(points);322 offset_points.param_set_and_write_new_value(points);
@@ -289,6 +325,45 @@
289 }325 }
290}326}
291327
328void LPEPowerStroke::doOnRemove(SPLPEItem const* lpeitem)
329{
330 if (SP_IS_SHAPE(lpeitem)) {
331 SPLPEItem *item = const_cast<SPLPEItem*>(lpeitem);
332 SPCSSAttr *css = sp_repr_css_attr_new ();
333 if (true) {
334 if (lpeitem->style->fill.isPaintserver()) {
335 SPPaintServer * server = lpeitem->style->getFillPaintServer();
336 if (server) {
337 Glib::ustring str;
338 str += "url(#";
339 str += server->getId();
340 str += ")";
341 sp_repr_css_set_property (css, "stroke", str.c_str());
342 }
343 } else if (lpeitem->style->fill.isColor()) {
344 gchar c[64];
345 sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value)));
346 sp_repr_css_set_property (css, "stroke", c);
347 } else {
348 sp_repr_css_set_property (css, "stroke", "none");
349 }
350 } else {
351 sp_repr_css_unset_property (css, "stroke");
352 }
353
354 Inkscape::CSSOStringStream os;
355 os << offset_points.median_width() * 2;
356 sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
357
358 sp_repr_css_set_property(css, "fill", "none");
359
360 sp_desktop_apply_css_recursive(item, css, true);
361 sp_repr_css_attr_unref (css);
362
363 item->updateRepr();
364 }
365}
366
292void367void
293LPEPowerStroke::adjustForNewPath(std::vector<Geom::Path> const & path_in)368LPEPowerStroke::adjustForNewPath(std::vector<Geom::Path> const & path_in)
294{369{
@@ -588,6 +663,9 @@
588 if (Geom::Interpolate::CubicBezierJohan *johan = dynamic_cast<Geom::Interpolate::CubicBezierJohan*>(interpolator)) {663 if (Geom::Interpolate::CubicBezierJohan *johan = dynamic_cast<Geom::Interpolate::CubicBezierJohan*>(interpolator)) {
589 johan->setBeta(interpolator_beta);664 johan->setBeta(interpolator_beta);
590 }665 }
666 if (Geom::Interpolate::CubicBezierSmooth *smooth = dynamic_cast<Geom::Interpolate::CubicBezierSmooth*>(interpolator)) {
667 smooth->setBeta(interpolator_beta);
668 }
591 Geom::Path strokepath = interpolator->interpolateToPath(ts);669 Geom::Path strokepath = interpolator->interpolateToPath(ts);
592 delete interpolator;670 delete interpolator;
593671
594672
=== modified file 'src/live_effects/lpe-powerstroke.h'
--- src/live_effects/lpe-powerstroke.h 2014-03-27 01:33:44 +0000
+++ src/live_effects/lpe-powerstroke.h 2014-09-07 17:02:32 +0000
@@ -25,9 +25,11 @@
25 LPEPowerStroke(LivePathEffectObject *lpeobject);25 LPEPowerStroke(LivePathEffectObject *lpeobject);
26 virtual ~LPEPowerStroke();26 virtual ~LPEPowerStroke();
2727
28
28 virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);29 virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
2930
30 virtual void doOnApply(SPLPEItem const* lpeitem);31 virtual void doOnApply(SPLPEItem const* lpeitem);
32 virtual void doOnRemove(SPLPEItem const* lpeitem);
3133
32 // methods called by path-manipulator upon edits34 // methods called by path-manipulator upon edits
33 void adjustForNewPath(std::vector<Geom::Path> const & path_in);35 void adjustForNewPath(std::vector<Geom::Path> const & path_in);
3436
=== modified file 'src/live_effects/lpe-tangent_to_curve.cpp'
--- src/live_effects/lpe-tangent_to_curve.cpp 2014-03-27 01:33:44 +0000
+++ src/live_effects/lpe-tangent_to_curve.cpp 2014-09-07 17:02:32 +0000
@@ -16,8 +16,6 @@
16#include <glibmm/i18n.h>16#include <glibmm/i18n.h>
1717
18#include "live_effects/lpe-tangent_to_curve.h"18#include "live_effects/lpe-tangent_to_curve.h"
19// FIXME: The following are only needed to convert the path's SPCurve* to pwd2.
20// There must be a more convenient way to achieve this.
21#include "sp-path.h"19#include "sp-path.h"
22#include "display/curve.h"20#include "display/curve.h"
2321
@@ -108,13 +106,13 @@
108 {106 {
109 KnotHolderEntity *e = new TtC::KnotHolderEntityLeftEnd(this);107 KnotHolderEntity *e = new TtC::KnotHolderEntityLeftEnd(this);
110 e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN,108 e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN,
111 _("Adjust the \"left\" end of the tangent") );109 _("Adjust the <b>left</b> end of the tangent") );
112 knotholder->add(e);110 knotholder->add(e);
113 }111 }
114 {112 {
115 KnotHolderEntity *e = new TtC::KnotHolderEntityRightEnd(this);113 KnotHolderEntity *e = new TtC::KnotHolderEntityRightEnd(this);
116 e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN,114 e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN,
117 _("Adjust the \"right\" end of the tangent") );115 _("Adjust the <b>right</b> end of the tangent") );
118 knotholder->add(e);116 knotholder->add(e);
119 }117 }
120};118};
@@ -130,14 +128,13 @@
130128
131 Geom::Point const s = snap_knot_position(p, state);129 Geom::Point const s = snap_knot_position(p, state);
132130
133 // FIXME: There must be a better way of converting the path's SPCurve* to pwd2.131 if ( !SP_IS_SHAPE(lpe->sp_lpe_item) ) {
134 SPCurve *curve = SP_PATH(item)->get_curve_for_edit();132 //lpe->t_attach.param_set_value(0);
135 Geom::PathVector pathv = curve->get_pathvector();133 g_warning("LPEItem is not a path! %s:%d\n", __FILE__, __LINE__);
136 Piecewise<D2<SBasis> > pwd2;134 return;
137 for (unsigned int i=0; i < pathv.size(); i++) {
138 pwd2.concat(pathv[i].toPwSb());
139 }135 }
140136 Piecewise<D2<SBasis> > pwd2 = paths_to_pw( lpe->pathvector_before_effect );
137
141 double t0 = nearest_point(s, pwd2);138 double t0 = nearest_point(s, pwd2);
142 lpe->t_attach.param_set_value(t0);139 lpe->t_attach.param_set_value(t0);
143140
144141
=== modified file 'src/live_effects/lpe-tangent_to_curve.h'
--- src/live_effects/lpe-tangent_to_curve.h 2012-04-07 14:09:58 +0000
+++ src/live_effects/lpe-tangent_to_curve.h 2014-09-07 17:02:32 +0000
@@ -34,7 +34,6 @@
34public:34public:
35 LPETangentToCurve(LivePathEffectObject *lpeobject);35 LPETangentToCurve(LivePathEffectObject *lpeobject);
36 virtual ~LPETangentToCurve();36 virtual ~LPETangentToCurve();
37
38 virtual Geom::Piecewise<Geom::D2<Geom::SBasis> >37 virtual Geom::Piecewise<Geom::D2<Geom::SBasis> >
39 doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in);38 doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in);
4039
4140
=== added file 'src/live_effects/lpe-taperstroke.cpp'
--- src/live_effects/lpe-taperstroke.cpp 1970-01-01 00:00:00 +0000
+++ src/live_effects/lpe-taperstroke.cpp 2014-09-07 17:02:32 +0000
@@ -0,0 +1,630 @@
1/**
2 * @file
3 * Taper Stroke path effect, provided as an alternative to Power Strokes
4 * for otherwise constant-width paths.
5 *
6 * Authors:
7 * Liam P White <inkscapebrony@gmail.com>
8 *
9 * Copyright (C) 2014 Authors
10 *
11 * Released under GNU GPL, read the file 'COPYING' for more information
12 */
13
14#include "live_effects/lpe-taperstroke.h"
15
16#include <2geom/path.h>
17#include <2geom/shape.h>
18#include <2geom/path.h>
19#include <2geom/circle.h>
20#include <2geom/sbasis-to-bezier.h>
21#include "pathoutlineprovider.h"
22#include "display/curve.h"
23#include "sp-shape.h"
24#include "style.h"
25#include "xml/repr.h"
26#include "sp-paint-server.h"
27#include "svg/svg-color.h"
28#include "desktop-style.h"
29#include "svg/css-ostringstream.h"
30#include "svg/svg.h"
31
32//#include <glibmm/i18n.h>
33
34#include "knot-holder-entity.h"
35#include "knotholder.h"
36
37template<typename T>
38inline bool withinRange(T value, T low, T high) {
39 return (value > low && value < high);
40}
41
42namespace Inkscape {
43namespace LivePathEffect {
44
45namespace TpS {
46 class KnotHolderEntityAttachBegin : public LPEKnotHolderEntity {
47 public:
48 KnotHolderEntityAttachBegin(LPETaperStroke * effect) : LPEKnotHolderEntity(effect) {}
49 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
50 virtual Geom::Point knot_get() const;
51 };
52
53 class KnotHolderEntityAttachEnd : public LPEKnotHolderEntity {
54 public:
55 KnotHolderEntityAttachEnd(LPETaperStroke * effect) : LPEKnotHolderEntity(effect) {}
56 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
57 virtual Geom::Point knot_get() const;
58 };
59} // TpS
60
61static const Util::EnumData<unsigned> JoinType[] = {
62 {LINEJOIN_STRAIGHT, N_("Beveled"), "bevel"},
63 {LINEJOIN_ROUND, N_("Rounded"), "round"},
64 {LINEJOIN_REFLECTED, N_("Reflected"), "reflected"},
65 {LINEJOIN_POINTY, N_("Miter"), "miter"},
66 {LINEJOIN_EXTRAPOLATED, N_("Extrapolated"), "extrapolated"}
67};
68
69static const Util::EnumDataConverter<unsigned> JoinTypeConverter(JoinType, sizeof (JoinType)/sizeof(*JoinType));
70
71LPETaperStroke::LPETaperStroke(LivePathEffectObject *lpeobject) :
72 Effect(lpeobject),
73 line_width(_("Stroke width"), _("The (non-tapered) width of the path"), "stroke_width", &wr, this, 1.),
74 attach_start(_("Start offset"), _("Taper distance from path start"), "attach_start", &wr, this, 0.2),
75 attach_end(_("End offset"), _("The ending position of the taper"), "end_offset", &wr, this, 0.2),
76 smoothing(_("Taper smoothing"), _("Amount of smoothing to apply to the tapers"), "smoothing", &wr, this, 0.5),
77 join_type(_("Join type"), _("Join type for non-smooth nodes"), "jointype", JoinTypeConverter, &wr, this, LINEJOIN_EXTRAPOLATED),
78 miter_limit(_("Miter limit"), _("Limit for miter joins"), "miter_limit", &wr, this, 100.)
79{
80 show_orig_path = true;
81 _provides_knotholder_entities = true;
82
83 attach_start.param_set_digits(3);
84 attach_end.param_set_digits(3);
85
86
87 registerParameter( dynamic_cast<Parameter *>(&line_width) );
88 registerParameter( dynamic_cast<Parameter *>(&attach_start) );
89 registerParameter( dynamic_cast<Parameter *>(&attach_end) );
90 registerParameter( dynamic_cast<Parameter *>(&smoothing) );
91 registerParameter( dynamic_cast<Parameter *>(&join_type) );
92 registerParameter( dynamic_cast<Parameter *>(&miter_limit) );
93}
94
95LPETaperStroke::~LPETaperStroke()
96{
97
98}
99
100//from LPEPowerStroke -- sets fill if stroke color because we will
101//be converting to a fill to make the new join.
102
103void LPETaperStroke::doOnApply(SPLPEItem const* lpeitem)
104{
105 if (SP_IS_SHAPE(lpeitem)) {
106 SPLPEItem* item = const_cast<SPLPEItem*>(lpeitem);
107 double width = (lpeitem && lpeitem->style) ? lpeitem->style->stroke_width.computed : 1.;
108
109 SPCSSAttr *css = sp_repr_css_attr_new ();
110 if (true) {
111 if (lpeitem->style->stroke.isPaintserver()) {
112 SPPaintServer * server = lpeitem->style->getStrokePaintServer();
113 if (server) {
114 Glib::ustring str;
115 str += "url(#";
116 str += server->getId();
117 str += ")";
118 sp_repr_css_set_property (css, "fill", str.c_str());
119 }
120 } else if (lpeitem->style->stroke.isColor()) {
121 gchar c[64];
122 sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value)));
123 sp_repr_css_set_property (css, "fill", c);
124 } else {
125 sp_repr_css_set_property (css, "fill", "none");
126 }
127 } else {
128 sp_repr_css_unset_property (css, "fill");
129 }
130
131 sp_repr_css_set_property(css, "stroke", "none");
132
133 sp_desktop_apply_css_recursive(item, css, true);
134 sp_repr_css_attr_unref (css);
135
136 line_width.param_set_value(width);
137 } else {
138 g_warning("LPE Join Type can only be applied to paths (not groups).");
139 }
140}
141
142//from LPEPowerStroke -- sets stroke color from existing fill color
143
144void LPETaperStroke::doOnRemove(SPLPEItem const* lpeitem)
145{
146
147 if (SP_IS_SHAPE(lpeitem)) {
148 SPLPEItem *item = const_cast<SPLPEItem*>(lpeitem);
149
150 SPCSSAttr *css = sp_repr_css_attr_new ();
151 if (true) {
152 if (lpeitem->style->fill.isPaintserver()) {
153 SPPaintServer * server = lpeitem->style->getFillPaintServer();
154 if (server) {
155 Glib::ustring str;
156 str += "url(#";
157 str += server->getId();
158 str += ")";
159 sp_repr_css_set_property (css, "stroke", str.c_str());
160 }
161 } else if (lpeitem->style->fill.isColor()) {
162 gchar c[64];
163 sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value)));
164 sp_repr_css_set_property (css, "stroke", c);
165 } else {
166 sp_repr_css_set_property (css, "stroke", "none");
167 }
168 } else {
169 sp_repr_css_unset_property (css, "stroke");
170 }
171
172 Inkscape::CSSOStringStream os;
173 os << fabs(line_width);
174 sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
175
176 sp_repr_css_set_property(css, "fill", "none");
177
178 sp_desktop_apply_css_recursive(item, css, true);
179 sp_repr_css_attr_unref (css);
180 item->updateRepr();
181 }
182}
183
184//actual effect impl here
185
186Geom::Path return_at_first_cusp (Geom::Path const & path_in, double /*smooth_tolerance*/ = 0.05)
187{
188 return Geom::split_at_cusps(path_in)[0];
189}
190
191Geom::Piecewise<Geom::D2<Geom::SBasis> > stretch_along(Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in, Geom::Path pattern, double width);
192
193//references to pointers, because magic
194void subdivideCurve(Geom::Curve * curve_in, Geom::Coord t, Geom::Curve *& val_first, Geom::Curve *& val_second);
195
196Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in)
197{
198 Geom::Path first_cusp = return_at_first_cusp(path_in[0]);
199 Geom::Path last_cusp = return_at_first_cusp(path_in[0].reverse());
200
201 bool zeroStart = false;
202 bool zeroEnd = false;
203 bool metInMiddle = false;
204
205 //there is a pretty good chance that people will try to drag the knots
206 //on top of each other, so block it
207
208 unsigned size = path_in[0].size();
209 if (size == first_cusp.size()) {
210 //check to see if the knots were dragged over each other
211 //if so, reset the end offset, but still allow the start offset.
212 if ( attach_start >= (size - attach_end) ) {
213 attach_end.param_set_value( size - attach_start );
214 metInMiddle = true;
215 }
216 }
217
218 if (attach_start == size - attach_end) {
219 metInMiddle = true;
220 }
221 if (attach_end == size - attach_start) {
222 metInMiddle = true;
223 }
224
225 //don't let it be integer
226 {
227 if (double(unsigned(attach_start)) == attach_start) {
228 attach_start.param_set_value(attach_start - 0.00001);
229 }
230 if (double(unsigned(attach_end)) == attach_end) {
231 attach_end.param_set_value(attach_end - 0.00001);
232 }
233 }
234
235 unsigned allowed_start = first_cusp.size();
236 unsigned allowed_end = last_cusp.size();
237
238 //don't let the knots be farther than they are allowed to be
239 {
240 if ((unsigned)attach_start >= allowed_start) {
241 attach_start.param_set_value((double)allowed_start - 0.00001);
242 }
243 if ((unsigned)attach_end >= allowed_end) {
244 attach_end.param_set_value((double)allowed_end - 0.00001);
245 }
246 }
247
248 //don't let it be zero
249 if (attach_start < 0.0000001 || withinRange(double(attach_start), 0.00000001, 0.000001)) {
250 attach_start.param_set_value( 0.0000001 );
251 zeroStart = true;
252 }
253 if (attach_end < 0.0000001 || withinRange(double(attach_end), 0.00000001, 0.000001)) {
254 attach_end.param_set_value( 0.0000001 );
255 zeroEnd = true;
256 }
257
258 //remember, Path::operator () means get point at time t
259 start_attach_point = first_cusp(attach_start);
260 end_attach_point = last_cusp(attach_end);
261 Geom::PathVector pathv_out;
262
263 //the following function just splits it up into three pieces.
264 pathv_out = doEffect_simplePath(path_in);
265
266 //now for the actual tapering. We use the stretch_along method to get this done.
267
268 Geom::PathVector real_pathv;
269 Geom::Path real_path;
270 Geom::PathVector pat_vec;
271 Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2;
272 Geom::Path throwaway_path;
273
274 if (!zeroStart) {
275 //Construct the pattern (pat_str stands for pattern string) (and yes, this is easier, trust me)
276 std::stringstream pat_str;
277 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";
278
279 pat_vec = sp_svg_read_pathv(pat_str.str().c_str());
280 pwd2.concat(stretch_along(pathv_out[0].toPwSb(), pat_vec[0], -fabs(line_width)));
281 throwaway_path = Geom::path_from_piecewise(pwd2, LPE_CONVERSION_TOLERANCE)[0];
282
283 real_path.append(throwaway_path);
284 }
285
286 if (!metInMiddle) {
287 //append the outside outline of the path (with direction)
288 throwaway_path = Outline::PathOutsideOutline(pathv_out[1],
289 -fabs(line_width), static_cast<LineJoinType>(join_type.get_value()), miter_limit);
290 if (!zeroStart && real_path.size() >= 1 && throwaway_path.size() >= 1) {
291 if (Geom::distance(real_path.finalPoint(), throwaway_path.initialPoint()) > 0.0000001) {
292 real_path.appendNew<Geom::LineSegment>(throwaway_path.initialPoint());
293 } else {
294 real_path.setFinal(throwaway_path.initialPoint());
295 }
296 }
297 real_path.append(throwaway_path);
298 }
299
300 if (!zeroEnd) {
301 //append the ending taper
302 std::stringstream pat_str_1;
303 pat_str_1 << "M 0,1 C " << (double)smoothing << ",1 1,0.5 1,0.5 1,0.5 " << double(smoothing) << ",0 0,0";
304 pat_vec = sp_svg_read_pathv(pat_str_1.str().c_str());
305
306 pwd2 = Geom::Piecewise<Geom::D2<Geom::SBasis> > ();
307 pwd2.concat(stretch_along(pathv_out[2].toPwSb(), pat_vec[0], -fabs(line_width)));
308
309 throwaway_path = Geom::path_from_piecewise(pwd2, LPE_CONVERSION_TOLERANCE)[0];
310 if (Geom::distance(real_path.finalPoint(), throwaway_path.initialPoint()) > 0.0000001 && real_path.size() >= 1) {
311 real_path.appendNew<Geom::LineSegment>(throwaway_path.initialPoint());
312 } else {
313 real_path.setFinal(throwaway_path.initialPoint());
314 }
315 real_path.append(throwaway_path);
316 }
317
318 if (!metInMiddle) {
319 //append the inside outline of the path (against direction)
320 throwaway_path = Outline::PathOutsideOutline(pathv_out[1].reverse(),
321 -fabs(line_width), static_cast<LineJoinType>(join_type.get_value()), miter_limit);
322
323 if (Geom::distance(real_path.finalPoint(), throwaway_path.initialPoint()) > 0.0000001 && real_path.size() >= 1) {
324 real_path.appendNew<Geom::LineSegment>(throwaway_path.initialPoint());
325 } else {
326 real_path.setFinal(throwaway_path.initialPoint());
327 }
328 real_path.append(throwaway_path);
329 }
330
331 if (Geom::distance(real_path.finalPoint(), real_path.initialPoint()) > 0.0000001) {
332 real_path.appendNew<Geom::LineSegment>(real_path.initialPoint());
333 } else {
334 real_path.setFinal(real_path.initialPoint());
335 }
336 real_path.close();
337
338 real_pathv.push_back(real_path);
339
340 return real_pathv;
341}
342
343//in all cases, this should return a PathVector with three elements.
344Geom::PathVector LPETaperStroke::doEffect_simplePath(Geom::PathVector const & path_in)
345{
346 unsigned size = path_in[0].size();
347
348 //do subdivision and get out
349 unsigned loc = (unsigned)attach_start;
350 Geom::Curve * curve_start = path_in[0] [loc].duplicate();
351
352 std::vector<Geom::Path> pathv_out;
353 Geom::Path path_out = Geom::Path();
354
355 Geom::Path trimmed_start = Geom::Path();
356 Geom::Path trimmed_end = Geom::Path();
357
358 for (unsigned i = 0; i < loc; i++) {
359 trimmed_start.append(path_in[0] [i]);
360 }
361
362 Geom::Curve * temp;
363 subdivideCurve(curve_start, attach_start - loc, temp, curve_start);
364 trimmed_start.append(*temp);
365 if (temp) delete temp; temp = 0;
366
367 //special case: path is one segment long
368 //special case: what if the two knots occupy the same segment?
369 if ((size == 1) || ( size - unsigned(attach_end) - 1 == loc )) {
370 Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_start);
371
372 //it is just a dumb segment
373 //we have to do some shifting here because the value changed when we reduced the length
374 //of the previous segment.
375
376 subdivideCurve(curve_start, t, curve_start, temp);
377 trimmed_end.append(*temp);
378 if (temp) delete temp; temp = 0;
379
380 for (unsigned j = (size - attach_end) + 1; j < size; j++) {
381 trimmed_end.append(path_in[0] [j]);
382 }
383
384 path_out.append(*curve_start);
385 pathv_out.push_back(trimmed_start);
386 pathv_out.push_back(path_out);
387 pathv_out.push_back(trimmed_end);
388 return pathv_out;
389 }
390
391 pathv_out.push_back(trimmed_start);
392
393 //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)
394 path_out.append(*curve_start);
395
396 for (unsigned k = loc + 1; k < (size - unsigned(attach_end)) - 1; k++) {
397 path_out.append(path_in[0] [k]);
398 }
399
400 //deal with the last segment in a very similar fashion to the first
401 loc = size - attach_end;
402
403 Geom::Curve * curve_end = path_in[0] [loc].duplicate();
404
405 Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_end);
406
407 subdivideCurve(curve_end, t, curve_end, temp);
408 trimmed_end.append(*temp);
409 if (temp) delete temp; temp = 0;
410
411 for (unsigned j = (size - attach_end) + 1; j < size; j++) {
412 trimmed_end.append(path_in[0] [j]);
413 }
414
415 path_out.append(*curve_end);
416 pathv_out.push_back(path_out);
417
418 pathv_out.push_back(trimmed_end);
419
420 if (curve_end) delete curve_end;
421 if (curve_start) delete curve_start;
422 return pathv_out;
423}
424
425
426//most of the below code is verbatim from Pattern Along Path. However, it needed a little
427//tweaking to get it to work right in this case.
428Geom::Piecewise<Geom::D2<Geom::SBasis> > stretch_along(Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in, Geom::Path pattern, double prop_scale)
429{
430 using namespace Geom;
431
432 // Don't allow empty path parameter:
433 if ( pattern.empty() ) {
434 return pwd2_in;
435 }
436
437 /* Much credit should go to jfb and mgsloan of lib2geom development for the code below! */
438 Piecewise<D2<SBasis> > output;
439 std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > pre_output;
440
441 D2<Piecewise<SBasis> > patternd2 = make_cuts_independent(pattern.toPwSb());
442 Piecewise<SBasis> x0 = Piecewise<SBasis>(patternd2[0]);
443 Piecewise<SBasis> y0 = Piecewise<SBasis>(patternd2[1]);
444 OptInterval pattBndsX = bounds_exact(x0);
445 OptInterval pattBndsY = bounds_exact(y0);
446 if (pattBndsX && pattBndsY) {
447 x0 -= pattBndsX->min();
448 y0 -= pattBndsY->middle();
449
450 double xspace = 0;
451 double noffset = 0;
452 double toffset = 0;
453 //Prevent more than 90% overlap...
454 if (xspace < -pattBndsX->extent()*.9) {
455 xspace = -pattBndsX->extent()*.9;
456 }
457
458 y0+=noffset;
459
460 std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > paths_in;
461 paths_in = split_at_discontinuities(pwd2_in);
462
463 for (unsigned idx = 0; idx < paths_in.size(); idx++) {
464 Geom::Piecewise<Geom::D2<Geom::SBasis> > path_i = paths_in[idx];
465 Piecewise<SBasis> x = x0;
466 Piecewise<SBasis> y = y0;
467 Piecewise<D2<SBasis> > uskeleton = arc_length_parametrization(path_i,2,.1);
468 uskeleton = remove_short_cuts(uskeleton,.01);
469 Piecewise<D2<SBasis> > n = rot90(derivative(uskeleton));
470 n = force_continuity(remove_short_cuts(n,.1));
471
472 int nbCopies = 0;
473 double scaling = 1;
474 nbCopies = 1;
475 scaling = (uskeleton.domain().extent() - toffset)/pattBndsX->extent();
476
477 double pattWidth = pattBndsX->extent() * scaling;
478
479 if (scaling != 1.0) {
480 x*=scaling;
481 }
482 if ( false ) {
483 y*=(scaling*prop_scale);
484 } else {
485 if (prop_scale != 1.0) y *= prop_scale;
486 }
487 x += toffset;
488
489 double offs = 0;
490 for (int i=0; i<nbCopies; i++) {
491 if (false) {
492 Geom::Piecewise<Geom::D2<Geom::SBasis> > output_piece = compose(uskeleton,x+offs)+y*compose(n,x+offs);
493 std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > splited_output_piece = split_at_discontinuities(output_piece);
494 pre_output.insert(pre_output.end(), splited_output_piece.begin(), splited_output_piece.end() );
495 } else {
496 output.concat(compose(uskeleton,x+offs)+y*compose(n,x+offs));
497 }
498 offs+=pattWidth;
499 }
500 }
501 return output;
502 } else {
503 return pwd2_in;
504 }
505}
506
507void subdivideCurve(Geom::Curve * curve_in, Geom::Coord t, Geom::Curve *& val_first, Geom::Curve *& val_second)
508{
509 if (Geom::LineSegment* linear = dynamic_cast<Geom::LineSegment*>(curve_in)) {
510 //special case for line segments
511 std::pair<Geom::LineSegment, Geom::LineSegment> seg_pair = linear->subdivide(t);
512 val_first = seg_pair.first.duplicate();
513 val_second = seg_pair.second.duplicate();
514 } else {
515 //all other cases:
516 Geom::CubicBezier cubic = Geom::sbasis_to_cubicbezier(curve_in->toSBasis());
517 std::pair<Geom::CubicBezier, Geom::CubicBezier> cubic_pair = cubic.subdivide(t);
518 val_first = cubic_pair.first.duplicate();
519 val_second = cubic_pair.second.duplicate();
520 }
521}
522
523
524void LPETaperStroke::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item)
525{
526 {
527 KnotHolderEntity *e = new TpS::KnotHolderEntityAttachBegin(this);
528 e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN,
529 _("Start point of the taper"), SP_KNOT_SHAPE_CIRCLE );
530 knotholder->add(e);
531 }
532 {
533 KnotHolderEntity *e = new TpS::KnotHolderEntityAttachEnd(this);
534 e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN,
535 _("End point of the taper"), SP_KNOT_SHAPE_CIRCLE );
536 knotholder->add(e);
537 }
538}
539
540namespace TpS {
541void KnotHolderEntityAttachBegin::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
542{
543 using namespace Geom;
544
545 LPETaperStroke* lpe = dynamic_cast<LPETaperStroke *>(_effect);
546
547 Geom::Point const s = snap_knot_position(p, state);
548
549 if (!SP_IS_SHAPE(lpe->sp_lpe_item) ) {
550 g_warning("LPEItem is not a path! %s:%d\n", __FILE__, __LINE__);
551 return;
552 }
553
554 SPCurve* curve;
555 if ( !(curve = SP_SHAPE(lpe->sp_lpe_item)->getCurve()) ) {
556 //oops
557 //lpe->attach_start.param_set_value(0);
558 return;
559 }
560 //in case you are wondering, the above are simply sanity checks. we never want to actually
561 //use that object.
562
563 Geom::PathVector pathv = lpe->pathvector_before_effect;
564
565 Piecewise<D2<SBasis> > pwd2;
566 Geom::Path p_in = return_at_first_cusp(pathv[0]);
567 pwd2.concat(p_in.toPwSb());
568
569 double t0 = nearest_point(s, pwd2);
570 lpe->attach_start.param_set_value(t0);
571
572 // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating.
573 sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true);
574}
575void KnotHolderEntityAttachEnd::knot_set(Geom::Point const &p, Geom::Point const& /*origin*/, guint state)
576{
577 using namespace Geom;
578
579 LPETaperStroke* lpe = dynamic_cast<LPETaperStroke *>(_effect);
580
581 Geom::Point const s = snap_knot_position(p, state);
582
583 if (!SP_IS_SHAPE(lpe->sp_lpe_item) ) {
584 g_warning("LPEItem is not a path! %s:%d\n", __FILE__, __LINE__);
585 return;
586 }
587
588 SPCurve* curve;
589 if ( !(curve = SP_SHAPE(lpe->sp_lpe_item)->getCurve()) ) {
590 //oops
591 //lpe->attach_end.param_set_value(0);
592 return;
593 }
594 Geom::PathVector pathv = lpe->pathvector_before_effect;
595 Geom::Path p_in = return_at_first_cusp(pathv[0].reverse());
596 Piecewise<D2<SBasis> > pwd2 = p_in.toPwSb();
597
598 double t0 = nearest_point(s, pwd2);
599 lpe->attach_end.param_set_value(t0);
600
601 sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true);
602}
603Geom::Point KnotHolderEntityAttachBegin::knot_get() const
604{
605 LPETaperStroke const * lpe = dynamic_cast<LPETaperStroke const*> (_effect);
606 return lpe->start_attach_point;
607}
608Geom::Point KnotHolderEntityAttachEnd::knot_get() const
609{
610 LPETaperStroke const * lpe = dynamic_cast<LPETaperStroke const*> (_effect);
611 return lpe->end_attach_point;
612}
613}
614
615
616/* ######################## */
617
618} //namespace LivePathEffect
619} /* namespace Inkscape */
620
621/*
622 Local Variables:
623 mode:c++
624 c-file-style:"stroustrup"
625 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
626 indent-tabs-mode:nil
627 fill-column:99
628 End:
629*/
630// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
0631
=== added file 'src/live_effects/lpe-taperstroke.h'
--- src/live_effects/lpe-taperstroke.h 1970-01-01 00:00:00 +0000
+++ src/live_effects/lpe-taperstroke.h 2014-09-07 17:02:32 +0000
@@ -0,0 +1,72 @@
1/** @file
2 * @brief Taper Stroke path effect (meant as a replacement for using Power Strokes for tapering)
3 */
4/* Authors:
5 * Liam P White <inkscapebrony@gmail.com>
6 * Copyright (C) 2014 Authors
7 *
8 * Released under GNU GPL, read the file 'COPYING' for more information
9 */
10
11#ifndef INKSCAPE_LPE_TAPERSTROKE_H
12#define INKSCAPE_LPE_TAPERSTROKE_H
13
14#include "live_effects/parameter/enum.h"
15#include "live_effects/effect.h"
16#include "live_effects/parameter/parameter.h"
17#include "live_effects/parameter/vector.h"
18
19namespace Inkscape {
20namespace LivePathEffect {
21
22namespace TpS {
23// we need a separate namespace to avoid clashes with other LPEs
24class KnotHolderEntityAttachBegin;
25class KnotHolderEntityAttachEnd;
26}
27
28class LPETaperStroke : public Effect {
29public:
30 LPETaperStroke(LivePathEffectObject *lpeobject);
31 virtual ~LPETaperStroke();
32
33 virtual void doOnApply(SPLPEItem const* lpeitem);
34 virtual void doOnRemove(SPLPEItem const* lpeitem);
35
36 virtual Geom::PathVector doEffect_path (Geom::PathVector const& path_in);
37 Geom::PathVector doEffect_simplePath(Geom::PathVector const& path_in);
38
39 virtual void addKnotHolderEntities(KnotHolder * knotholder, SPDesktop * desktop, SPItem * item);
40
41 friend class TpS::KnotHolderEntityAttachBegin;
42 friend class TpS::KnotHolderEntityAttachEnd;
43private:
44 ScalarParam line_width;
45 ScalarParam attach_start;
46 ScalarParam attach_end;
47 ScalarParam smoothing;
48 EnumParam<unsigned> join_type;
49 ScalarParam miter_limit;
50
51 Geom::Point start_attach_point;
52 Geom::Point end_attach_point;
53
54 LPETaperStroke(const LPETaperStroke&);
55 LPETaperStroke& operator=(const LPETaperStroke&);
56};
57
58} //namespace LivePathEffect
59} //namespace Inkscape
60
61#endif
62
63/*
64 Local Variables:
65 mode:c++
66 c-file-style:"stroustrup"
67 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
68 indent-tabs-mode:nil
69 fill-column:99
70 End:
71*/
72// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
073
=== modified file 'src/live_effects/parameter/Makefile_insert'
--- src/live_effects/parameter/Makefile_insert 2014-07-02 11:14:35 +0000
+++ src/live_effects/parameter/Makefile_insert 2014-09-07 17:02:32 +0000
@@ -20,12 +20,16 @@
20 live_effects/parameter/path.h \20 live_effects/parameter/path.h \
21 live_effects/parameter/originalpath.cpp \21 live_effects/parameter/originalpath.cpp \
22 live_effects/parameter/originalpath.h \22 live_effects/parameter/originalpath.h \
23 live_effects/parameter/originalpatharray.cpp \
24 live_effects/parameter/originalpatharray.h \
23 live_effects/parameter/powerstrokepointarray.cpp \25 live_effects/parameter/powerstrokepointarray.cpp \
24 live_effects/parameter/powerstrokepointarray.h \26 live_effects/parameter/powerstrokepointarray.h \
25 live_effects/parameter/filletchamferpointarray.cpp \27 live_effects/parameter/filletchamferpointarray.cpp \
26 live_effects/parameter/filletchamferpointarray.h \28 live_effects/parameter/filletchamferpointarray.h \
27 live_effects/parameter/text.cpp \29 live_effects/parameter/text.cpp \
28 live_effects/parameter/text.h \30 live_effects/parameter/text.h \
31 live_effects/parameter/transformedpoint.cpp \
32 live_effects/parameter/transformedpoint.h \
29 live_effects/parameter/togglebutton.cpp \33 live_effects/parameter/togglebutton.cpp \
30 live_effects/parameter/togglebutton.h \34 live_effects/parameter/togglebutton.h \
31 live_effects/parameter/unit.cpp \35 live_effects/parameter/unit.cpp \
3236
=== modified file 'src/live_effects/parameter/filletchamferpointarray.cpp'
--- src/live_effects/parameter/filletchamferpointarray.cpp 2014-08-24 10:37:18 +0000
+++ src/live_effects/parameter/filletchamferpointarray.cpp 2014-09-07 17:02:32 +0000
@@ -8,11 +8,14 @@
8 * Released under GNU GPL, read the file 'COPYING' for more information8 * Released under GNU GPL, read the file 'COPYING' for more information
9 */9 */
1010
11#include <glibmm.h>
12
11#include "ui/dialog/lpe-fillet-chamfer-properties.h"13#include "ui/dialog/lpe-fillet-chamfer-properties.h"
12#include "live_effects/parameter/filletchamferpointarray.h"14#include "live_effects/parameter/filletchamferpointarray.h"
13#include <2geom/piecewise.h>15#include <2geom/piecewise.h>
14#include <2geom/sbasis-to-bezier.h>16#include <2geom/sbasis-to-bezier.h>
15#include <2geom/sbasis-geometric.h>17#include <2geom/sbasis-geometric.h>
18#include <gtkmm.h>
1619
17#include "live_effects/effect.h"20#include "live_effects/effect.h"
18#include "svg/svg.h"21#include "svg/svg.h"
1922
=== added file 'src/live_effects/parameter/originalpatharray.cpp'
--- src/live_effects/parameter/originalpatharray.cpp 1970-01-01 00:00:00 +0000
+++ src/live_effects/parameter/originalpatharray.cpp 2014-09-07 17:02:32 +0000
@@ -0,0 +1,497 @@
1/*
2 * Copyright (C) Johan Engelen 2008 <j.b.c.engelen@utwente.nl>
3 *
4 * Released under GNU GPL, read the file 'COPYING' for more information
5 */
6
7#ifdef HAVE_CONFIG_H
8# include "config.h"
9#endif
10
11#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H
12#include <glibmm/threads.h>
13#endif
14
15#include "live_effects/parameter/originalpatharray.h"
16
17#include <gtkmm/widget.h>
18#include <gtkmm/icontheme.h>
19#include <gtkmm/imagemenuitem.h>
20#include <gtkmm/separatormenuitem.h>
21#include <gtkmm/scrolledwindow.h>
22
23#include <glibmm/i18n.h>
24
25#include "inkscape.h"
26#include "icon-size.h"
27#include "widgets/icon.h"
28#include "ui/clipboard.h"
29#include "svg/svg.h"
30#include "svg/stringstream.h"
31#include "originalpath.h"
32#include "uri.h"
33#include "display/curve.h"
34
35#include <2geom/coord.h>
36#include <2geom/point.h>
37#include "sp-shape.h"
38#include "sp-text.h"
39#include "live_effects/effect.h"
40
41#include "verbs.h"
42#include "document-undo.h"
43#include "document.h"
44
45namespace Inkscape {
46
47namespace LivePathEffect {
48
49class OriginalPathArrayParam::ModelColumns : public Gtk::TreeModel::ColumnRecord
50{
51public:
52
53 ModelColumns()
54 {
55 add(_colObject);
56 add(_colLabel);
57 add(_colReverse);
58 }
59 virtual ~ModelColumns() {}
60
61 Gtk::TreeModelColumn<PathAndDirection*> _colObject;
62 Gtk::TreeModelColumn<Glib::ustring> _colLabel;
63 Gtk::TreeModelColumn<bool> _colReverse;
64};
65
66OriginalPathArrayParam::OriginalPathArrayParam( const Glib::ustring& label,
67 const Glib::ustring& tip,
68 const Glib::ustring& key,
69 Inkscape::UI::Widget::Registry* wr,
70 Effect* effect )
71: Parameter(label, tip, key, wr, effect),
72 _vector(),
73 _tree(),
74 _text_renderer(),
75 _toggle_renderer(),
76 _scroller()
77{
78 _model = new ModelColumns();
79 _store = Gtk::TreeStore::create(*_model);
80 _tree.set_model(_store);
81
82 _tree.set_reorderable(true);
83 _tree.enable_model_drag_dest (Gdk::ACTION_MOVE);
84
85 _text_renderer = manage(new Gtk::CellRendererText());
86 int nameColNum = _tree.append_column(_("Name"), *_text_renderer) - 1;
87 _name_column = _tree.get_column(nameColNum);
88 _name_column->add_attribute(_text_renderer->property_text(), _model->_colLabel);
89
90 _tree.set_expander_column( *_tree.get_column(nameColNum) );
91 _tree.set_search_column(_model->_colLabel);
92
93 Gtk::CellRendererToggle * _toggle_renderer = manage(new Gtk::CellRendererToggle());
94 int toggleColNum = _tree.append_column(_("Reverse"), *_toggle_renderer) - 1;
95 Gtk::TreeViewColumn* col = _tree.get_column(toggleColNum);
96 _toggle_renderer->set_activatable(true);
97 _toggle_renderer->signal_toggled().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_reverse_toggled));
98 col->add_attribute(_toggle_renderer->property_active(), _model->_colReverse);
99
100 //quick little hack -- newer versions of gtk gave the item zero space allotment
101 _scroller.set_size_request(-1, 120);
102
103 _scroller.add(_tree);
104 _scroller.set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC );
105 //_scroller.set_shadow_type(Gtk::SHADOW_IN);
106
107 oncanvas_editable = true;
108
109}
110
111OriginalPathArrayParam::~OriginalPathArrayParam()
112{
113 while (!_vector.empty()) {
114 PathAndDirection *w = _vector.back();
115 _vector.pop_back();
116 unlink(w);
117 delete w;
118 }
119 delete _model;
120}
121
122void OriginalPathArrayParam::on_reverse_toggled(const Glib::ustring& path)
123{
124 Gtk::TreeModel::iterator iter = _store->get_iter(path);
125 Gtk::TreeModel::Row row = *iter;
126 PathAndDirection *w = row[_model->_colObject];
127 row[_model->_colReverse] = !row[_model->_colReverse];
128 w->reversed = row[_model->_colReverse];
129
130 gchar * full = param_getSVGValue();
131 param_write_to_repr(full);
132 g_free(full);
133 DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
134 _("Link path parameter to path"));
135}
136
137void OriginalPathArrayParam::param_set_default()
138{
139
140}
141
142Gtk::Widget* OriginalPathArrayParam::param_newWidget()
143{
144 Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox());
145 Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
146
147 vbox->pack_start(_scroller, Gtk::PACK_EXPAND_WIDGET);
148
149
150 { // Paste path to link button
151 Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( GTK_STOCK_PASTE, Inkscape::ICON_SIZE_BUTTON) );
152 Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
153 pButton->set_relief(Gtk::RELIEF_NONE);
154 pIcon->show();
155 pButton->add(*pIcon);
156 pButton->show();
157 pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_link_button_click));
158 hbox->pack_start(*pButton, Gtk::PACK_SHRINK);
159 pButton->set_tooltip_text(_("Link to path"));
160 }
161
162 { // Remove linked path
163 Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( GTK_STOCK_REMOVE, Inkscape::ICON_SIZE_BUTTON) );
164 Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
165 pButton->set_relief(Gtk::RELIEF_NONE);
166 pIcon->show();
167 pButton->add(*pIcon);
168 pButton->show();
169 pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_remove_button_click));
170 hbox->pack_start(*pButton, Gtk::PACK_SHRINK);
171 pButton->set_tooltip_text(_("Remove Path"));
172 }
173
174 { // Move Down
175 Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( GTK_STOCK_GO_DOWN, Inkscape::ICON_SIZE_BUTTON) );
176 Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
177 pButton->set_relief(Gtk::RELIEF_NONE);
178 pIcon->show();
179 pButton->add(*pIcon);
180 pButton->show();
181 pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_down_button_click));
182 hbox->pack_end(*pButton, Gtk::PACK_SHRINK);
183 pButton->set_tooltip_text(_("Move Down"));
184 }
185
186 { // Move Down
187 Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( GTK_STOCK_GO_UP, Inkscape::ICON_SIZE_BUTTON) );
188 Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
189 pButton->set_relief(Gtk::RELIEF_NONE);
190 pIcon->show();
191 pButton->add(*pIcon);
192 pButton->show();
193 pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_up_button_click));
194 hbox->pack_end(*pButton, Gtk::PACK_SHRINK);
195 pButton->set_tooltip_text(_("Move Up"));
196 }
197
198 vbox->pack_end(*hbox, Gtk::PACK_SHRINK);
199
200 vbox->show_all_children(true);
201
202 return vbox;
203}
204
205bool OriginalPathArrayParam::_selectIndex(const Gtk::TreeIter& iter, int* i)
206{
207 if ((*i)-- <= 0) {
208 _tree.get_selection()->select(iter);
209 return true;
210 }
211 return false;
212}
213
214void OriginalPathArrayParam::on_up_button_click()
215{
216 Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected();
217 if (iter) {
218 Gtk::TreeModel::Row row = *iter;
219
220 int i = -1;
221 std::vector<PathAndDirection*>::iterator piter = _vector.begin();
222 for (std::vector<PathAndDirection*>::iterator iter = _vector.begin(); iter != _vector.end(); piter = iter, i++, iter++) {
223 if (*iter == row[_model->_colObject]) {
224 _vector.erase(iter);
225 _vector.insert(piter, row[_model->_colObject]);
226 break;
227 }
228 }
229
230 gchar * full = param_getSVGValue();
231 param_write_to_repr(full);
232 g_free(full);
233
234 DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
235 _("Move path up"));
236
237 _store->foreach_iter(sigc::bind<int*>(sigc::mem_fun(*this, &OriginalPathArrayParam::_selectIndex), &i));
238 }
239}
240
241void OriginalPathArrayParam::on_down_button_click()
242{
243 Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected();
244 if (iter) {
245 Gtk::TreeModel::Row row = *iter;
246
247 int i = 0;
248 for (std::vector<PathAndDirection*>::iterator iter = _vector.begin(); iter != _vector.end(); i++, iter++) {
249 if (*iter == row[_model->_colObject]) {
250 std::vector<PathAndDirection*>::iterator niter = _vector.erase(iter);
251 if (niter != _vector.end()) {
252 niter++;
253 i++;
254 }
255 _vector.insert(niter, row[_model->_colObject]);
256 break;
257 }
258 }
259
260 gchar * full = param_getSVGValue();
261 param_write_to_repr(full);
262 g_free(full);
263
264 DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
265 _("Move path down"));
266
267 _store->foreach_iter(sigc::bind<int*>(sigc::mem_fun(*this, &OriginalPathArrayParam::_selectIndex), &i));
268 }
269}
270
271void OriginalPathArrayParam::on_remove_button_click()
272{
273 Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected();
274 if (iter) {
275 Gtk::TreeModel::Row row = *iter;
276 remove_link(row[_model->_colObject]);
277
278 gchar * full = param_getSVGValue();
279 param_write_to_repr(full);
280 g_free(full);
281
282 DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
283 _("Remove path"));
284 }
285
286}
287
288void
289OriginalPathArrayParam::on_link_button_click()
290{
291 Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
292 Glib::ustring pathid = cm->getShapeOrTextObjectId(SP_ACTIVE_DESKTOP);
293
294 if (pathid == "") {
295 return;
296 }
297 // add '#' at start to make it an uri.
298 pathid.insert(pathid.begin(), '#');
299
300 Inkscape::SVGOStringStream os;
301 bool foundOne = false;
302 for (std::vector<PathAndDirection*>::const_iterator iter = _vector.begin(); iter != _vector.end(); iter++) {
303 if (foundOne) {
304 os << "|";
305 } else {
306 foundOne = true;
307 }
308 os << (*iter)->href << "," << ((*iter)->reversed ? "1" : "0");
309 }
310
311 if (foundOne) {
312 os << "|";
313 }
314
315 os << pathid.c_str() << ",0";
316
317 param_write_to_repr(os.str().c_str());
318 DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
319 _("Link path parameter to path"));
320}
321
322void OriginalPathArrayParam::unlink(PathAndDirection* to)
323{
324 to->linked_modified_connection.disconnect();
325 to->linked_delete_connection.disconnect();
326 to->ref.detach();
327 to->_pathvector = Geom::PathVector();
328 if (to->href) {
329 g_free(to->href);
330 to->href = NULL;
331 }
332}
333
334void OriginalPathArrayParam::remove_link(PathAndDirection* to)
335{
336 unlink(to);
337 for (std::vector<PathAndDirection*>::iterator iter = _vector.begin(); iter != _vector.end(); iter++) {
338 if (*iter == to) {
339 PathAndDirection *w = *iter;
340 _vector.erase(iter);
341 delete w;
342 return;
343 }
344 }
345}
346
347void OriginalPathArrayParam::linked_delete(SPObject */*deleted*/, PathAndDirection* to)
348{
349 //remove_link(to);
350
351 gchar * full = param_getSVGValue();
352 param_write_to_repr(full);
353 g_free(full);
354}
355
356bool OriginalPathArrayParam::_updateLink(const Gtk::TreeIter& iter, PathAndDirection* pd)
357{
358 Gtk::TreeModel::Row row = *iter;
359 if (row[_model->_colObject] == pd) {
360 SPObject *obj = pd->ref.getObject();
361 row[_model->_colLabel] = obj && obj->getId() ? ( obj->label() ? obj->label() : obj->getId() ) : pd->href;
362 return true;
363 }
364 return false;
365}
366
367void OriginalPathArrayParam::linked_changed(SPObject */*old_obj*/, SPObject *new_obj, PathAndDirection* to)
368{
369 to->linked_delete_connection.disconnect();
370 to->linked_modified_connection.disconnect();
371 to->linked_transformed_connection.disconnect();
372
373 if (new_obj && SP_IS_ITEM(new_obj)) {
374 to->linked_delete_connection = new_obj->connectDelete(sigc::bind<PathAndDirection*>(sigc::mem_fun(*this, &OriginalPathArrayParam::linked_delete), to));
375 to->linked_modified_connection = new_obj->connectModified(sigc::bind<PathAndDirection*>(sigc::mem_fun(*this, &OriginalPathArrayParam::linked_modified), to));
376 to->linked_transformed_connection = SP_ITEM(new_obj)->connectTransformed(sigc::bind<PathAndDirection*>(sigc::mem_fun(*this, &OriginalPathArrayParam::linked_transformed), to));
377
378 linked_modified(new_obj, SP_OBJECT_MODIFIED_FLAG, to);
379 } else {
380 to->_pathvector = Geom::PathVector();
381 SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG);
382 _store->foreach_iter(sigc::bind<PathAndDirection*>(sigc::mem_fun(*this, &OriginalPathArrayParam::_updateLink), to));
383 }
384}
385
386void OriginalPathArrayParam::linked_transformed(Geom::Affine const *mp, SPItem* original, PathAndDirection* to)
387{
388
389}
390
391void OriginalPathArrayParam::setPathVector(SPObject *linked_obj, guint flags, PathAndDirection* to)
392{
393 if (!to) {
394 return;
395 }
396 SPCurve *curve = NULL;
397 if (SP_IS_SHAPE(linked_obj)) {
398 curve = SP_SHAPE(linked_obj)->getCurveBeforeLPE();
399 }
400 if (SP_IS_TEXT(linked_obj)) {
401 curve = SP_TEXT(linked_obj)->getNormalizedBpath();
402 }
403
404 if (curve == NULL) {
405 // curve invalid, set empty pathvector
406 to->_pathvector = Geom::PathVector();
407 } else {
408 to->_pathvector = curve->get_pathvector();
409 curve->unref();
410 }
411}
412
413void OriginalPathArrayParam::linked_modified(SPObject *linked_obj, guint flags, PathAndDirection* to)
414{
415 if (!to) {
416 return;
417 }
418 setPathVector(linked_obj, flags, to);
419 SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG);
420 _store->foreach_iter(sigc::bind<PathAndDirection*>(sigc::mem_fun(*this, &OriginalPathArrayParam::_updateLink), to));
421}
422
423//void PathParam::linked_transformed(Geom::Affine const *rel_transf, SPItem *moved_item)
424//{
425// linked_transformed_callback(rel_transf, moved_item);
426//}
427
428bool OriginalPathArrayParam::param_readSVGValue(const gchar* strvalue)
429{
430 if (strvalue) {
431 while (!_vector.empty()) {
432 PathAndDirection *w = _vector.back();
433 unlink(w);
434 _vector.pop_back();
435 delete w;
436 }
437 _store->clear();
438
439 gchar ** strarray = g_strsplit(strvalue, "|", 0);
440 for (gchar ** iter = strarray; *iter != NULL; iter++) {
441 if ((*iter)[0] == '#') {
442 gchar ** substrarray = g_strsplit(*iter, ",", 0);
443 PathAndDirection* w = new PathAndDirection((SPObject *)param_effect->getLPEObj());
444 w->href = g_strdup(*substrarray);
445 w->reversed = *(substrarray+1) != NULL && (*(substrarray+1))[0] == '1';
446
447 w->linked_changed_connection = w->ref.changedSignal().connect(sigc::bind<PathAndDirection *>(sigc::mem_fun(*this, &OriginalPathArrayParam::linked_changed), w));
448 w->ref.attach(URI(w->href));
449
450 _vector.push_back(w);
451
452 Gtk::TreeModel::iterator iter = _store->append();
453 Gtk::TreeModel::Row row = *iter;
454 SPObject *obj = w->ref.getObject();
455
456 row[_model->_colObject] = w;
457 row[_model->_colLabel] = obj ? ( obj->label() ? obj->label() : obj->getId() ) : w->href;
458 row[_model->_colReverse] = w->reversed;
459 g_strfreev (substrarray);
460 }
461 }
462 g_strfreev (strarray);
463 return true;
464 }
465 return false;
466}
467
468gchar * OriginalPathArrayParam::param_getSVGValue() const
469{
470 Inkscape::SVGOStringStream os;
471 bool foundOne = false;
472 for (std::vector<PathAndDirection*>::const_iterator iter = _vector.begin(); iter != _vector.end(); iter++) {
473 if (foundOne) {
474 os << "|";
475 } else {
476 foundOne = true;
477 }
478 os << (*iter)->href << "," << ((*iter)->reversed ? "1" : "0");
479 }
480 gchar * str = g_strdup(os.str().c_str());
481 return str;
482}
483
484} /* namespace LivePathEffect */
485
486} /* namespace Inkscape */
487
488/*
489 Local Variables:
490 mode:c++
491 c-file-style:"stroustrup"
492 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
493 indent-tabs-mode:nil
494 fill-column:99
495 End:
496*/
497// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
0498
=== added file 'src/live_effects/parameter/originalpatharray.h'
--- src/live_effects/parameter/originalpatharray.h 1970-01-01 00:00:00 +0000
+++ src/live_effects/parameter/originalpatharray.h 2014-09-07 17:02:32 +0000
@@ -0,0 +1,123 @@
1#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINALPATHARRAY_H
2#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINALPATHARRAY_H
3
4/*
5 * Inkscape::LivePathEffectParameters
6 *
7* Copyright (C) Johan Engelen 2008 <j.b.c.engelen@utwente.nl>
8 *
9 * Released under GNU GPL, read the file 'COPYING' for more information
10 */
11
12#include <vector>
13
14#include <gtkmm/box.h>
15#include <gtkmm/treeview.h>
16#include <gtkmm/treestore.h>
17#include <gtkmm/scrolledwindow.h>
18
19#include "live_effects/parameter/parameter.h"
20#include "live_effects/parameter/path-reference.h"
21
22#include "svg/svg.h"
23#include "svg/stringstream.h"
24#include "path-reference.h"
25#include "sp-object.h"
26
27namespace Inkscape {
28
29namespace LivePathEffect {
30
31class PathAndDirection {
32public:
33 PathAndDirection(SPObject *owner)
34 : href(NULL),
35 ref(owner),
36 _pathvector(Geom::PathVector()),
37 reversed(false)
38 {
39
40 }
41 gchar *href;
42 URIReference ref;
43 //SPItem *obj;
44 std::vector<Geom::Path> _pathvector;
45 bool reversed;
46
47 sigc::connection linked_changed_connection;
48 sigc::connection linked_delete_connection;
49 sigc::connection linked_modified_connection;
50 sigc::connection linked_transformed_connection;
51};
52
53class OriginalPathArrayParam : public Parameter {
54public:
55 class ModelColumns;
56
57 OriginalPathArrayParam( const Glib::ustring& label,
58 const Glib::ustring& tip,
59 const Glib::ustring& key,
60 Inkscape::UI::Widget::Registry* wr,
61 Effect* effect);
62
63 virtual ~OriginalPathArrayParam();
64
65 virtual Gtk::Widget * param_newWidget();
66 virtual bool param_readSVGValue(const gchar * strvalue);
67 virtual gchar * param_getSVGValue() const;
68 virtual void param_set_default();
69
70 /** Disable the canvas indicators of parent class by overriding this method */
71 virtual void param_editOncanvas(SPItem * /*item*/, SPDesktop * /*dt*/) {};
72 /** Disable the canvas indicators of parent class by overriding this method */
73 virtual void addCanvasIndicators(SPLPEItem const* /*lpeitem*/, std::vector<Geom::PathVector> & /*hp_vec*/) {};
74
75 std::vector<PathAndDirection*> _vector;
76
77protected:
78 bool _updateLink(const Gtk::TreeIter& iter, PathAndDirection* pd);
79 bool _selectIndex(const Gtk::TreeIter& iter, int* i);
80 void unlink(PathAndDirection* to);
81 void remove_link(PathAndDirection* to);
82 void setPathVector(SPObject *linked_obj, guint flags, PathAndDirection* to);
83
84 void linked_changed(SPObject *old_obj, SPObject *new_obj, PathAndDirection* to);
85 void linked_modified(SPObject *linked_obj, guint flags, PathAndDirection* to);
86 void linked_transformed(Geom::Affine const *mp, SPItem *original, PathAndDirection* to);
87 void linked_delete(SPObject *deleted, PathAndDirection* to);
88
89 ModelColumns *_model;
90 Glib::RefPtr<Gtk::TreeStore> _store;
91 Gtk::TreeView _tree;
92 Gtk::CellRendererText *_text_renderer;
93 Gtk::CellRendererToggle *_toggle_renderer;
94 Gtk::TreeView::Column *_name_column;
95 Gtk::ScrolledWindow _scroller;
96
97 void on_link_button_click();
98 void on_remove_button_click();
99 void on_up_button_click();
100 void on_down_button_click();
101 void on_reverse_toggled(const Glib::ustring& path);
102
103private:
104 OriginalPathArrayParam(const OriginalPathArrayParam&);
105 OriginalPathArrayParam& operator=(const OriginalPathArrayParam&);
106};
107
108} //namespace LivePathEffect
109
110} //namespace Inkscape
111
112#endif
113
114/*
115 Local Variables:
116 mode:c++
117 c-file-style:"stroustrup"
118 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
119 indent-tabs-mode:nil
120 fill-column:99
121 End:
122*/
123// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
0124
=== modified file 'src/live_effects/parameter/powerstrokepointarray.cpp'
--- src/live_effects/parameter/powerstrokepointarray.cpp 2014-07-27 01:17:50 +0000
+++ src/live_effects/parameter/powerstrokepointarray.cpp 2014-09-07 17:02:32 +0000
@@ -4,8 +4,7 @@
4 * Released under GNU GPL, read the file 'COPYING' for more information4 * Released under GNU GPL, read the file 'COPYING' for more information
5 */5 */
66
7#include <glibmm/i18n.h>7#include "ui/dialog/lpe-powerstroke-properties.h"
8
9#include "live_effects/parameter/powerstrokepointarray.h"8#include "live_effects/parameter/powerstrokepointarray.h"
109
11#include "live_effects/effect.h"10#include "live_effects/effect.h"
@@ -21,6 +20,8 @@
21#include "desktop.h"20#include "desktop.h"
22#include "live_effects/lpeobject.h"21#include "live_effects/lpeobject.h"
2322
23#include <glibmm/i18n.h>
24
24namespace Inkscape {25namespace Inkscape {
2526
26namespace LivePathEffect {27namespace LivePathEffect {
@@ -102,6 +103,23 @@
102 }103 }
103}104}
104105
106float PowerStrokePointArrayParam::median_width()
107{
108 size_t size = _vector.size();
109 if (size > 0)
110 {
111 if (size % 2 == 0)
112 {
113 return (_vector[size / 2 - 1].y() + _vector[size / 2].y()) / 2;
114 }
115 else
116 {
117 return _vector[size / 2].y();
118 }
119 }
120 return 1;
121}
122
105void123void
106PowerStrokePointArrayParam::set_pwd2(Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in, Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_normal_in)124PowerStrokePointArrayParam::set_pwd2(Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in, Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_normal_in)
107{125{
@@ -117,7 +135,7 @@
117 knot_mode = mode;135 knot_mode = mode;
118 knot_color = color;136 knot_color = color;
119}137}
120138/*
121class PowerStrokePointArrayParamKnotHolderEntity : public KnotHolderEntity {139class PowerStrokePointArrayParamKnotHolderEntity : public KnotHolderEntity {
122public:140public:
123 PowerStrokePointArrayParamKnotHolderEntity(PowerStrokePointArrayParam *p, unsigned int index);141 PowerStrokePointArrayParamKnotHolderEntity(PowerStrokePointArrayParam *p, unsigned int index);
@@ -127,7 +145,7 @@
127 virtual Geom::Point knot_get() const;145 virtual Geom::Point knot_get() const;
128 virtual void knot_click(guint state);146 virtual void knot_click(guint state);
129147
130 /** Checks whether the index falls within the size of the parameter's vector */148 // Checks whether the index falls within the size of the parameter's vector
131 bool valid_index(unsigned int index) const {149 bool valid_index(unsigned int index) const {
132 return (_pparam->_vector.size() > index);150 return (_pparam->_vector.size() > index);
133 };151 };
@@ -135,7 +153,7 @@
135private:153private:
136 PowerStrokePointArrayParam *_pparam;154 PowerStrokePointArrayParam *_pparam;
137 unsigned int _index;155 unsigned int _index;
138};156};*/
139157
140PowerStrokePointArrayParamKnotHolderEntity::PowerStrokePointArrayParamKnotHolderEntity(PowerStrokePointArrayParam *p, unsigned int index) 158PowerStrokePointArrayParamKnotHolderEntity::PowerStrokePointArrayParamKnotHolderEntity(PowerStrokePointArrayParam *p, unsigned int index)
141 : _pparam(p), 159 : _pparam(p),
@@ -184,6 +202,12 @@
184 return canvas_point;202 return canvas_point;
185}203}
186204
205void PowerStrokePointArrayParamKnotHolderEntity::knot_set_offset(Geom::Point offset)
206{
207 _pparam->_vector.at(_index) = Geom::Point(offset.x(), offset.y() / 2);
208 this->parent_holder->knot_ungrabbed_handler(this->knot, 0);
209}
210
187void211void
188PowerStrokePointArrayParamKnotHolderEntity::knot_click(guint state)212PowerStrokePointArrayParamKnotHolderEntity::knot_click(guint state)
189{213{
@@ -226,10 +250,15 @@
226 // add knot to knotholder250 // add knot to knotholder
227 PowerStrokePointArrayParamKnotHolderEntity *e = new PowerStrokePointArrayParamKnotHolderEntity(_pparam, _index+1);251 PowerStrokePointArrayParamKnotHolderEntity *e = new PowerStrokePointArrayParamKnotHolderEntity(_pparam, _index+1);
228 e->create( this->desktop, this->item, parent_holder, Inkscape::CTRL_TYPE_UNKNOWN,252 e->create( this->desktop, this->item, parent_holder, Inkscape::CTRL_TYPE_UNKNOWN,
229 _("<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."),253 _("<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."),
230 _pparam->knot_shape, _pparam->knot_mode, _pparam->knot_color);254 _pparam->knot_shape, _pparam->knot_mode, _pparam->knot_color);
231 parent_holder->add(e);255 parent_holder->add(e);
232 }256 }
257 }
258 else if ((state & GDK_MOD1_MASK) || (state & GDK_SHIFT_MASK))
259 {
260 Geom::Point offset = Geom::Point(_pparam->_vector.at(_index).x(), _pparam->_vector.at(_index).y() * 2);
261 Inkscape::UI::Dialogs::PowerstrokePropertiesDialog::showDialog(this->desktop, offset, this);
233 } 262 }
234}263}
235264
@@ -238,7 +267,7 @@
238 for (unsigned int i = 0; i < _vector.size(); ++i) {267 for (unsigned int i = 0; i < _vector.size(); ++i) {
239 PowerStrokePointArrayParamKnotHolderEntity *e = new PowerStrokePointArrayParamKnotHolderEntity(this, i);268 PowerStrokePointArrayParamKnotHolderEntity *e = new PowerStrokePointArrayParamKnotHolderEntity(this, i);
240 e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN,269 e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN,
241 _("<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."),270 _("<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."),
242 knot_shape, knot_mode, knot_color);271 knot_shape, knot_mode, knot_color);
243 knotholder->add(e);272 knotholder->add(e);
244 }273 }
245274
=== modified file 'src/live_effects/parameter/powerstrokepointarray.h'
--- src/live_effects/parameter/powerstrokepointarray.h 2014-03-27 01:33:44 +0000
+++ src/live_effects/parameter/powerstrokepointarray.h 2014-09-07 17:02:32 +0000
@@ -20,8 +20,6 @@
2020
21namespace LivePathEffect {21namespace LivePathEffect {
2222
23class PowerStrokePointArrayParamKnotHolderEntity;
24
25class PowerStrokePointArrayParam : public ArrayParam<Geom::Point> {23class PowerStrokePointArrayParam : public ArrayParam<Geom::Point> {
26public:24public:
27 PowerStrokePointArrayParam( const Glib::ustring& label,25 PowerStrokePointArrayParam( const Glib::ustring& label,
@@ -37,6 +35,8 @@
3735
38 void set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color);36 void set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color);
3937
38 float median_width();
39
40 virtual bool providesKnotHolderEntities() const { return true; }40 virtual bool providesKnotHolderEntities() const { return true; }
41 virtual void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item);41 virtual void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item);
4242
@@ -60,6 +60,25 @@
60 Geom::Piecewise<Geom::D2<Geom::SBasis> > last_pwd2_normal;60 Geom::Piecewise<Geom::D2<Geom::SBasis> > last_pwd2_normal;
61};61};
6262
63class PowerStrokePointArrayParamKnotHolderEntity : public KnotHolderEntity {
64public:
65 PowerStrokePointArrayParamKnotHolderEntity(PowerStrokePointArrayParam *p, unsigned int index);
66 virtual ~PowerStrokePointArrayParamKnotHolderEntity() {}
67
68 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
69 virtual Geom::Point knot_get() const;
70 virtual void knot_set_offset(Geom::Point offset);
71 virtual void knot_click(guint state);
72
73 /** Checks whether the index falls within the size of the parameter's vector */
74 bool valid_index(unsigned int index) const {
75 return (_pparam->_vector.size() > index);
76 };
77
78private:
79 PowerStrokePointArrayParam *_pparam;
80 unsigned int _index;
81};
6382
64} //namespace LivePathEffect83} //namespace LivePathEffect
6584
6685
=== added file 'src/live_effects/parameter/transformedpoint.cpp'
--- src/live_effects/parameter/transformedpoint.cpp 1970-01-01 00:00:00 +0000
+++ src/live_effects/parameter/transformedpoint.cpp 2014-09-07 17:02:32 +0000
@@ -0,0 +1,182 @@
1/*
2 * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
3 *
4 * Released under GNU GPL, read the file 'COPYING' for more information
5 */
6
7#include "ui/widget/registered-widget.h"
8#include "live_effects/parameter/transformedpoint.h"
9#include "sp-lpe-item.h"
10#include "knotholder.h"
11#include "svg/svg.h"
12#include "svg/stringstream.h"
13
14#include "live_effects/effect.h"
15#include "desktop.h"
16#include "verbs.h"
17
18#include <glibmm/i18n.h>
19
20namespace Inkscape {
21
22namespace LivePathEffect {
23
24TransformedPointParam::TransformedPointParam( const Glib::ustring& label, const Glib::ustring& tip,
25 const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
26 Effect* effect, Geom::Point default_vector,
27 bool dontTransform)
28 : Parameter(label, tip, key, wr, effect),
29 defvalue(default_vector),
30 origin(0.,0.),
31 vector(default_vector),
32 noTransform(dontTransform)
33{
34 vec_knot_shape = SP_KNOT_SHAPE_DIAMOND;
35 vec_knot_mode = SP_KNOT_MODE_XOR;
36 vec_knot_color = 0xffffb500;
37}
38
39TransformedPointParam::~TransformedPointParam()
40{
41
42}
43
44void
45TransformedPointParam::param_set_default()
46{
47 setOrigin(Geom::Point(0.,0.));
48 setVector(defvalue);
49}
50
51bool
52TransformedPointParam::param_readSVGValue(const gchar * strvalue)
53{
54 gchar ** strarray = g_strsplit(strvalue, ",", 4);
55 if (!strarray) {
56 return false;
57 }
58 double val[4];
59 unsigned int i = 0;
60 while (i < 4 && strarray[i]) {
61 if (sp_svg_number_read_d(strarray[i], &val[i]) != 0) {
62 i++;
63 } else {
64 break;
65 }
66 }
67 g_strfreev (strarray);
68 if (i == 4) {
69 setOrigin( Geom::Point(val[0], val[1]) );
70 setVector( Geom::Point(val[2], val[3]) );
71 return true;
72 }
73 return false;
74}
75
76gchar *
77TransformedPointParam::param_getSVGValue() const
78{
79 Inkscape::SVGOStringStream os;
80 os << origin << " , " << vector;
81 gchar * str = g_strdup(os.str().c_str());
82 return str;
83}
84
85Gtk::Widget *
86TransformedPointParam::param_newWidget()
87{
88 Inkscape::UI::Widget::RegisteredVector * pointwdg = Gtk::manage(
89 new Inkscape::UI::Widget::RegisteredVector( param_label,
90 param_tooltip,
91 param_key,
92 *param_wr,
93 param_effect->getRepr(),
94 param_effect->getSPDoc() ) );
95 pointwdg->setPolarCoords();
96 pointwdg->setValue( vector, origin );
97 pointwdg->clearProgrammatically();
98 pointwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change vector parameter"));
99
100 Gtk::HBox * hbox = Gtk::manage( new Gtk::HBox() );
101 static_cast<Gtk::HBox*>(hbox)->pack_start(*pointwdg, true, true);
102 static_cast<Gtk::HBox*>(hbox)->show_all_children();
103
104 return dynamic_cast<Gtk::Widget *> (hbox);
105}
106
107void
108TransformedPointParam::set_and_write_new_values(Geom::Point const &new_origin, Geom::Point const &new_vector)
109{
110 setValues(new_origin, new_vector);
111 gchar * str = param_getSVGValue();
112 param_write_to_repr(str);
113 g_free(str);
114}
115
116void
117TransformedPointParam::param_transform_multiply(Geom::Affine const& postmul, bool /*set*/)
118{
119 if (!noTransform) {
120 set_and_write_new_values( origin * postmul, vector * postmul.withoutTranslation() );
121 }
122}
123
124
125void
126TransformedPointParam::set_vector_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color)
127{
128 vec_knot_shape = shape;
129 vec_knot_mode = mode;
130 vec_knot_color = color;
131}
132
133void
134TransformedPointParam::set_oncanvas_color(guint32 color)
135{
136 vec_knot_color = color;
137}
138
139class TransformedPointParamKnotHolderEntity_Vector : public KnotHolderEntity {
140public:
141 TransformedPointParamKnotHolderEntity_Vector(TransformedPointParam *p) : param(p) { }
142 virtual ~TransformedPointParamKnotHolderEntity_Vector() {}
143
144 virtual void knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint /*state*/) {
145 Geom::Point const s = p - param->origin;
146 /// @todo implement angle snapping when holding CTRL
147 param->setVector(s);
148 sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
149 };
150 virtual Geom::Point knot_get() const{
151 return param->origin + param->vector;
152 };
153 virtual void knot_click(guint /*state*/){
154 g_print ("This is the vector handle associated to parameter '%s'\n", param->param_key.c_str());
155 };
156
157private:
158 TransformedPointParam *param;
159};
160
161void
162TransformedPointParam::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item)
163{
164 TransformedPointParamKnotHolderEntity_Vector *vector_e = new TransformedPointParamKnotHolderEntity_Vector(this);
165 vector_e->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, handleTip(), vec_knot_shape, vec_knot_mode, vec_knot_color);
166 knotholder->add(vector_e);
167}
168
169} /* namespace LivePathEffect */
170
171} /* namespace Inkscape */
172
173/*
174 Local Variables:
175 mode:c++
176 c-file-style:"stroustrup"
177 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
178 indent-tabs-mode:nil
179 fill-column:99
180 End:
181*/
182// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
0183
=== added file 'src/live_effects/parameter/transformedpoint.h'
--- src/live_effects/parameter/transformedpoint.h 1970-01-01 00:00:00 +0000
+++ src/live_effects/parameter/transformedpoint.h 2014-09-07 17:02:32 +0000
@@ -0,0 +1,87 @@
1#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_TRANSFORMED_POINT_H
2#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_TRANSFORMED_POINT_H
3
4/*
5 * Inkscape::LivePathEffectParameters
6 *
7 * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
8 *
9 * Released under GNU GPL, read the file 'COPYING' for more information
10 */
11
12#include <glib.h>
13#include <2geom/point.h>
14
15#include "live_effects/parameter/parameter.h"
16
17#include "knot-holder-entity.h"
18
19namespace Inkscape {
20
21namespace LivePathEffect {
22
23
24class TransformedPointParam : public Parameter {
25public:
26 TransformedPointParam( const Glib::ustring& label,
27 const Glib::ustring& tip,
28 const Glib::ustring& key,
29 Inkscape::UI::Widget::Registry* wr,
30 Effect* effect,
31 Geom::Point default_vector = Geom::Point(1,0),
32 bool dontTransform = false);
33 virtual ~TransformedPointParam();
34
35 virtual Gtk::Widget * param_newWidget();
36 inline const gchar *handleTip() const { return param_tooltip.c_str(); }
37
38 virtual bool param_readSVGValue(const gchar * strvalue);
39 virtual gchar * param_getSVGValue() const;
40
41 Geom::Point getVector() const { return vector; };
42 Geom::Point getOrigin() const { return origin; };
43 void setValues(Geom::Point const &new_origin, Geom::Point const &new_vector) { setVector(new_vector); setOrigin(new_origin); };
44 void setVector(Geom::Point const &new_vector) { vector = new_vector; };
45 void setOrigin(Geom::Point const &new_origin) { origin = new_origin; };
46 virtual void param_set_default();
47
48 void set_and_write_new_values(Geom::Point const &new_origin, Geom::Point const &new_vector);
49
50 virtual void param_transform_multiply(Geom::Affine const &postmul, bool set);
51
52 void set_vector_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color);
53 //void set_origin_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color);
54 void set_oncanvas_color(guint32 color);
55
56 virtual bool providesKnotHolderEntities() const { return true; }
57 virtual void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item);
58
59private:
60 TransformedPointParam(const TransformedPointParam&);
61 TransformedPointParam& operator=(const TransformedPointParam&);
62
63 Geom::Point defvalue;
64
65 Geom::Point origin;
66 Geom::Point vector;
67
68 bool noTransform;
69
70 /// The looks of the vector and origin knots oncanvas
71 SPKnotShapeType vec_knot_shape;
72 SPKnotModeType vec_knot_mode;
73 guint32 vec_knot_color;
74// SPKnotShapeType ori_knot_shape;
75// SPKnotModeType ori_knot_mode;
76// guint32 ori_knot_color;
77
78// friend class VectorParamKnotHolderEntity_Origin;
79 friend class TransformedPointParamKnotHolderEntity_Vector;
80};
81
82
83} //namespace LivePathEffect
84
85} //namespace Inkscape
86
87#endif
088
=== added file 'src/live_effects/pathoutlineprovider.cpp'
--- src/live_effects/pathoutlineprovider.cpp 1970-01-01 00:00:00 +0000
+++ src/live_effects/pathoutlineprovider.cpp 2014-09-07 17:02:32 +0000
@@ -0,0 +1,795 @@
1#include <glib.h> //g_critical
2
3#include "pathoutlineprovider.h"
4#include "livarot/path-description.h"
5#include <2geom/angle.h>
6#include <2geom/path.h>
7#include <2geom/circle.h>
8#include <2geom/sbasis-to-bezier.h>
9#include <2geom/shape.h>
10#include <2geom/transforms.h>
11#include <2geom/path-sink.h>
12#include "helper/geom-nodetype.h"
13#include <svg/svg.h>
14
15namespace Geom {
16/**
17* Refer to: Weisstein, Eric W. "Circle-Circle Intersection."
18 From MathWorld--A Wolfram Web Resource.
19 http://mathworld.wolfram.com/Circle-CircleIntersection.html
20*
21* @return 0 if no intersection
22* @return 1 if one circle is contained in the other
23* @return 2 if intersections are found (they are written to p0 and p1)
24*/
25static int circle_circle_intersection(Circle const &circle0, Circle const &circle1,
26 Point & p0, Point & p1)
27{
28 Point X0 = circle0.center();
29 double r0 = circle0.ray();
30 Point X1 = circle1.center();
31 double r1 = circle1.ray();
32
33 /* dx and dy are the vertical and horizontal distances between
34 * the circle centers.
35 */
36 Point D = X1 - X0;
37
38 /* Determine the straight-line distance between the centers. */
39 double d = L2(D);
40
41 /* Check for solvability. */
42 if (d > (r0 + r1)) {
43 /* no solution. circles do not intersect. */
44 return 0;
45 }
46 if (d <= fabs(r0 - r1)) {
47 /* no solution. one circle is contained in the other */
48 return 1;
49 }
50
51 /* 'point 2' is the point where the line through the circle
52 * intersection points crosses the line between the circle
53 * centers.
54 */
55
56 /* Determine the distance from point 0 to point 2. */
57 double a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ;
58
59 /* Determine the coordinates of point 2. */
60 Point p2 = X0 + D * (a/d);
61
62 /* Determine the distance from point 2 to either of the
63 * intersection points.
64 */
65 double h = std::sqrt((r0*r0) - (a*a));
66
67 /* Now determine the offsets of the intersection points from
68 * point 2.
69 */
70 Point r = (h/d)*rot90(D);
71
72 /* Determine the absolute intersection points. */
73 p0 = p2 + r;
74 p1 = p2 - r;
75
76 return 2;
77}
78/**
79* Find circle that touches inside of the curve, with radius matching the curvature, at time value \c t.
80* Because this method internally uses unitTangentAt, t should be smaller than 1.0 (see unitTangentAt).
81*/
82static Circle touching_circle( D2<SBasis> const &curve, double t, double tol=0.01 )
83{
84 D2<SBasis> dM=derivative(curve);
85 if ( are_near(L2sq(dM(t)),0.) ) {
86 dM=derivative(dM);
87 }
88 if ( are_near(L2sq(dM(t)),0.) ) { // try second time
89 dM=derivative(dM);
90 }
91 Piecewise<D2<SBasis> > unitv = unitVector(dM,tol);
92 Piecewise<SBasis> dMlength = dot(Piecewise<D2<SBasis> >(dM),unitv);
93 Piecewise<SBasis> k = cross(derivative(unitv),unitv);
94 k = divide(k,dMlength,tol,3);
95 double curv = k(t); // note that this value is signed
96
97 Geom::Point normal = unitTangentAt(curve, t).cw();
98 double radius = 1/curv;
99 Geom::Point center = curve(t) + radius*normal;
100 return Geom::Circle(center, fabs(radius));
101}
102
103std::vector<Geom::Path> split_at_cusps(const Geom::Path& in)
104{
105 PathVector out = PathVector();
106 Path temp = Path();
107
108 for (unsigned i = 0; i < in.size(); i++) {
109 temp.append(in[i]);
110 if ( get_nodetype(in[i], in[i + 1]) != Geom::NODE_SMOOTH ) {
111 out.push_back(temp);
112 temp = Path();
113 }
114 }
115 if (temp.size() > 0) {
116 out.push_back(temp);
117 }
118 return out;
119}
120
121Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2<Geom::SBasis> const & sbasis_in)
122{
123 std::vector<Geom::Point> temp;
124 sbasis_to_bezier(temp, sbasis_in, 4);
125 return Geom::CubicBezier( temp );
126}
127
128static 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)
129{
130 Geom::Coord denom = cross(vector_b, vector_a);
131 if (!Geom::are_near(denom,0.)) {
132 Geom::Coord t = (cross(origin_a,vector_b) + cross(vector_b,origin_b)) / denom;
133 return origin_a + t * vector_a;
134 }
135 return boost::none;
136}
137
138} // namespace Geom
139
140namespace Outline {
141
142typedef Geom::D2<Geom::SBasis> D2SB;
143typedef Geom::Piecewise<D2SB> PWD2;
144
145// UTILITY
146
147unsigned bezierOrder (const Geom::Curve* curve_in)
148{
149 using namespace Geom;
150 if ( const BezierCurve* bz = dynamic_cast<const BezierCurve*>(curve_in) ) {
151 return bz->order();
152 }
153 return 0;
154}
155
156/**
157 * @return true if the angle formed by the curves and their handles is greater than 180 degrees clockwise, otherwise false.
158 */
159bool outside_angle (const Geom::Curve& cbc1, const Geom::Curve& cbc2)
160{
161 Geom::Point start_point;
162 Geom::Point cross_point = cbc1.finalPoint();
163 Geom::Point end_point;
164
165 if (cross_point != cbc2.initialPoint()) {
166 g_warning("Non-contiguous path in Outline::outside_angle()");
167 return false;
168 }
169
170 Geom::CubicBezier cubicBezier = Geom::sbasis_to_cubicbezier(cbc1.toSBasis());
171 start_point = cubicBezier [2];
172
173 /*
174 * Because the node editor does not yet support true quadratics, paths are converted to
175 * cubic beziers in the node tool with degenerate handles on one side.
176 */
177
178 if (are_near(start_point, cross_point, 0.0000001)) {
179 start_point = cubicBezier [1];
180 }
181 cubicBezier = Geom::sbasis_to_cubicbezier(cbc2.toSBasis());
182 end_point = cubicBezier [1];
183 if (are_near(end_point, cross_point, 0.0000001)) {
184 end_point = cubicBezier [2];
185 }
186
187 // got our three points, now let's see what their clockwise angle is
188
189 // Definition of a Graham scan
190
191 /********************************************************************
192 # Three points are a counter-clockwise turn if ccw > 0, clockwise if
193 # ccw < 0, and collinear if ccw = 0 because ccw is a determinant that
194 # gives the signed area of the triangle formed by p1, p2 and p3.
195 function ccw(p1, p2, p3):
196 return (p2.x - p1.x)*(p3.y - p1.y) - (p2.y - p1.y)*(p3.x - p1.x)
197 *********************************************************************/
198
199 double ccw = ( (cross_point.x() - start_point.x()) * (end_point.y() - start_point.y()) ) -
200 ( (cross_point.y() - start_point.y()) * (end_point.x() - start_point.x()) );
201 return ccw > 0;
202}
203
204// LINE JOINS
205
206typedef Geom::BezierCurveN<1u> BezierLine;
207
208/**
209 * Removes the crossings on an interior join.
210 * @param path_builder Contains the incoming segment; result is appended to this
211 * @param outgoing The outgoing segment
212 */
213void joinInside(Geom::Path& path_builder, Geom::Curve const& outgoing) {
214 Geom::Curve const& incoming = path_builder.back();
215
216 // Using Geom::crossings to find intersections between two curves
217 Geom::Crossings cross = Geom::crossings(incoming, outgoing);
218 if (!cross.empty()) {
219 // Crossings found, create the join
220 Geom::CubicBezier cubic = Geom::sbasis_to_cubicbezier(incoming.toSBasis());
221 cubic = cubic.subdivide(cross[0].ta).first;
222 // erase the last segment, as we're going to overwrite it now
223 path_builder.erase_last();
224 path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS);
225
226 cubic = Geom::sbasis_to_cubicbezier(outgoing.toSBasis());
227 cubic = cubic.subdivide(cross[0].tb).second;
228 path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS);
229 } else {
230 // No crossings occurred, or Geom::crossings() failed; default to bevel
231 if (Geom::are_near(incoming.finalPoint(), outgoing.initialPoint())) {
232 path_builder.appendNew<BezierLine>(outgoing.initialPoint());
233 } else {
234 path_builder.setFinal(outgoing.initialPoint());
235 }
236 }
237}
238
239/**
240 * Try to create a miter join. Falls back to bevel if no miter can be created.
241 * @param path_builder Path to append curves to; back() is the incoming curve
242 * @param outgoing Outgoing curve.
243 * @param miter_limit When mitering, don't exceed this length
244 * @param line_width The thickness of the line.
245 */
246void miter_curves(Geom::Path& path_builder, Geom::Curve const& outgoing, double miter_limit, double line_width) {
247 using namespace Geom;
248 Curve const& incoming = path_builder.back();
249 Point tang1 = unitTangentAt(Geom::reverse(incoming.toSBasis()), 0.);
250 Point tang2 = unitTangentAt(outgoing.toSBasis(), 0);
251
252 boost::optional <Point> p = intersection_point (incoming.finalPoint(), tang1, outgoing.initialPoint(), tang2);
253 if (p) {
254 // check size of miter
255 Point point_on_path = incoming.finalPoint() - rot90(tang1) * line_width;
256 Coord len = distance(*p, point_on_path);
257 if (len <= miter_limit) {
258 // miter OK
259 path_builder.appendNew<BezierLine>(*p);
260 }
261 }
262 path_builder.appendNew<BezierLine>(outgoing.initialPoint());
263}
264
265/**
266 * Smoothly extrapolate curves along a circular route. Falls back to miter if necessary.
267 * @param path_builder Path to append curves to; back() is the incoming curve
268 * @param outgoing Outgoing curve.
269 * @param miter_limit When mitering, don't exceed this length
270 * @param line_width The thickness of the line. Used for miter fallback.
271 */
272void extrapolate_curves(Geom::Path& path_builder, Geom::Curve const& outgoing, double miter_limit, double line_width) {
273 Geom::Curve const& incoming = path_builder.back();
274 Geom::Point endPt = outgoing.initialPoint();
275
276 // The method used when extrapolating curves fails to work when either side of the join to be extrapolated
277 // is a line segment. When this situation is encountered, fall back to a regular miter join.
278 bool lineProblem = (dynamic_cast<const BezierLine *>(&incoming)) || (dynamic_cast<const BezierLine *>(&outgoing));
279 if (lineProblem == false) {
280 // Geom::Point tang1 = Geom::unitTangentAt(Geom::reverse(incoming.toSBasis()), 0.);
281 Geom::Point tang2 = Geom::unitTangentAt(outgoing.toSBasis(), 0);
282
283 Geom::Circle circle1 = Geom::touching_circle(Geom::reverse(incoming.toSBasis()), 0.);
284 Geom::Circle circle2 = Geom::touching_circle(outgoing.toSBasis(), 0);
285
286 Geom::Point points[2];
287 int solutions = Geom::circle_circle_intersection(circle1, circle2, points[0], points[1]);
288 if (solutions == 2) {
289 Geom::Point sol(0,0);
290 if ( dot(tang2,points[0]-endPt) > 0 ) {
291 // points[0] is bad, choose points[1]
292 sol = points[1];
293 } else if ( dot(tang2,points[1]-endPt) > 0 ) { // points[0] could be good, now check points[1]
294 // points[1] is bad, choose points[0]
295 sol = points[0];
296 } else {
297 // both points are good, choose nearest
298 sol = ( distanceSq(endPt, points[0]) < distanceSq(endPt, points[1]) ) ? points[0] : points[1];
299 }
300
301 Geom::EllipticalArc *arc0 = circle1.arc(incoming.finalPoint(), 0.5*(incoming.finalPoint()+sol), sol, true);
302 Geom::EllipticalArc *arc1 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true);
303 try {
304 if (arc0) {
305 path_builder.append (arc0->toSBasis());
306 delete arc0;
307 arc0 = NULL;
308 } else {
309 throw std::exception();
310 }
311
312 if (arc1) {
313 path_builder.append (arc1->toSBasis());
314 delete arc1;
315 arc1 = NULL;
316 } else {
317 throw std::exception();
318 }
319
320 } catch (std::exception const & ex) {
321 g_warning("Error extrapolating line join: %s\n", ex.what());
322 path_builder.appendNew<Geom::LineSegment>(endPt);
323 }
324 } else {
325 // 1 or no solutions found, default to miter
326 miter_curves(path_builder, outgoing, miter_limit, line_width);
327 }
328 } else {
329 // Line segments exist
330 miter_curves(path_builder, outgoing, miter_limit, line_width);
331 }
332}
333
334/**
335 * Extrapolate curves by reflecting them along the line that would be given by beveling the join.
336 * @param path_builder Path to append curves to; back() is the incoming curve
337 * @param outgoing Outgoing curve.
338 * @param miter_limit When mitering, don't exceed this length
339 * @param line_width The thickness of the line. Used for miter fallback.
340 */
341void reflect_curves(Geom::Path& path_builder, Geom::Curve const& outgoing, double miter_limit, double line_width)
342{
343 using namespace Geom;
344 Curve const& incoming = path_builder.back();
345 // On the outside, we'll take the incoming curve, the outgoing curve, and
346 // reflect them over the line formed by taking the unit tangent vector at times
347 // 0 and 1, respectively, rotated by 90 degrees.
348 Crossings cross;
349
350 // reflect curves along the line that would be given by beveling the join
351 Point tang1 = unitTangentAt(reverse(incoming.toSBasis()), 0.);
352 D2SB newcurve1 = incoming.toSBasis() * reflection(-rot90(tang1), incoming.finalPoint());
353 CubicBezier bzr1 = sbasis_to_cubicbezier(reverse(newcurve1));
354
355 Point tang2 = Geom::unitTangentAt(outgoing.toSBasis(), 0.);
356 D2SB newcurve2 = outgoing.toSBasis() * reflection(-rot90(tang2), outgoing.initialPoint());
357 CubicBezier bzr2 = sbasis_to_cubicbezier(reverse(newcurve2));
358
359 cross = crossings(bzr1, bzr2);
360 if (cross.empty()) {
361 // paths don't cross, fall back to miter
362 miter_curves(path_builder, outgoing, miter_limit, line_width);
363 } else {
364 // reflected join
365 std::pair<CubicBezier, CubicBezier> sub1 = bzr1.subdivide(cross[0].ta);
366 std::pair<CubicBezier, CubicBezier> sub2 = bzr2.subdivide(cross[0].tb);
367
368 // TODO it seems as if a bug in 2geom sometimes doesn't catch the first
369 // crossing of paths, but the second instead; but only sometimes.
370 path_builder.appendNew <CubicBezier> (sub1.first[1], sub1.first[2], sub2.second[0]);
371 path_builder.appendNew <CubicBezier> (sub2.second[1], sub2.second[2], outgoing.initialPoint());
372 }
373}
374
375// Ideal function pointer we want to pass
376typedef void JoinFunc(Geom::Path& /*path_builder*/, Geom::Curve const& /*outgoing*/, double /*miter_limit*/, double /*line_width*/);
377
378/**
379 * Helper function for repeated logic in outlineHalf.
380 */
381static void outlineHelper(Geom::Path& path_builder, Geom::PathVector* path_vec, bool outside, double width, double miter, JoinFunc func) {
382 Geom::Curve * cbc2 = path_vec->front()[0].duplicate();
383
384 if (outside) {
385 func(path_builder, *cbc2, miter, width);
386 } else {
387 joinInside(path_builder, *cbc2);
388 }
389
390 // store it
391 Geom::Path temp_path = path_vec->front();
392 if (!outside) {
393 // erase the first segment since the inside join code already appended it
394 temp_path.erase(temp_path.begin());
395 }
396
397 if (temp_path.initialPoint() != path_builder.finalPoint()) {
398 temp_path.setInitial(path_builder.finalPoint());
399 }
400
401 path_builder.append(temp_path);
402
403 delete cbc2;
404}
405
406/**
407 * Offsets exactly one half of a bezier spline (path).
408 * @param path_in The input path to use. (To create the other side use path_in.reverse() )
409 * @param line_width the line width to use (usually you want to divide this by 2)
410 * @param miter_limit the miter parameter
411 * @param func Join function to apply at each join.
412 */
413
414Geom::Path outlineHalf(const Geom::Path& path_in, double line_width, double miter_limit, JoinFunc func) {
415 // NOTE: it is important to notice the distinction between a Geom::Path and a livarot ::Path here!
416 // if you do not see "Geom::" there is a different function set!
417
418 Geom::PathVector pv = split_at_cusps(path_in);
419
420 ::Path to_outline;
421 ::Path outlined_result;
422
423 Geom::Path path_builder = Geom::Path(); // the path to store the result in
424 Geom::PathVector* path_vec; // needed because livarot returns a pointer (TODO make this not a pointer)
425
426 // Do two curves at a time for efficiency, since the join function needs to know the outgoing curve as well
427 const size_t k = pv.size();
428 for (size_t u = 0; u < k; u += 2) {
429 to_outline = Path();
430 outlined_result = Path();
431
432 to_outline.LoadPath(pv[u], Geom::identity(), false, false);
433 to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10);
434 // now a curve has been outside outlined and loaded into outlined_result
435
436 // get the Geom::Path
437 path_vec = outlined_result.MakePathVector();
438
439 // on the first run through, there is no join
440 if (u == 0) {
441 path_builder.start(path_vec->front().initialPoint());
442 path_builder.append(path_vec->front());
443 } else {
444 outlineHelper(path_builder, path_vec, outside_angle(pv[u-1][pv[u-1].size()-1], pv[u][0]), line_width, miter_limit, func);
445 }
446
447 // outline the next segment, but don't store it yet
448 if (path_vec)
449 delete path_vec;
450 path_vec = NULL;
451
452 // odd number of paths
453 if (u < k - 1) {
454 outlined_result = Path();
455 to_outline = Path();
456
457 to_outline.LoadPath(pv[u+1], Geom::Affine(), false, false);
458 to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10);
459
460 path_vec = outlined_result.MakePathVector();
461 outlineHelper(path_builder, path_vec, outside_angle(pv[u][pv[u].size()-1], pv[u+1][0]), line_width, miter_limit, func);
462
463 if (path_vec)
464 delete path_vec;
465 path_vec = NULL;
466 }
467 }
468
469 if (path_in.closed()) {
470 Geom::Curve * cbc1;
471 Geom::Curve * cbc2;
472
473 if ( path_in[path_in.size()].isDegenerate() ) {
474 // handle case for last segment curved
475 outlined_result = Path();
476 to_outline = Path();
477
478 Geom::Path oneCurve; oneCurve.append(path_in[0]);
479
480 to_outline.LoadPath(oneCurve, Geom::Affine(), false, false);
481 to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10);
482
483 path_vec = outlined_result.MakePathVector();
484
485 cbc1 = path_builder[path_builder.size() - 1].duplicate();
486 cbc2 = path_vec->front()[0].duplicate();
487
488 delete path_vec;
489 } else {
490 // handle case for last segment straight
491 // since the path doesn't actually give us access to it, we'll do it ourselves
492 outlined_result = Path();
493 to_outline = Path();
494
495 Geom::Path oneCurve; oneCurve.append(Geom::LineSegment(path_in.finalPoint(), path_in.initialPoint()));
496
497 to_outline.LoadPath(oneCurve, Geom::Affine(), false, false);
498 to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10);
499
500 path_vec = outlined_result.MakePathVector();
501
502 cbc1 = path_builder[path_builder.size() - 1].duplicate();
503 cbc2 = (*path_vec)[0] [0].duplicate();
504
505 outlineHelper(path_builder, path_vec, outside_angle(path_in[path_in.size()-1], oneCurve[0]), line_width, miter_limit, func);
506
507 delete cbc1;
508 cbc1 = cbc2->duplicate();
509 delete path_vec;
510
511 oneCurve = Geom::Path(); oneCurve.append(path_in[0]);
512
513 to_outline.LoadPath(oneCurve, Geom::Affine(), false, false);
514 to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10);
515
516 path_vec = outlined_result.MakePathVector();
517 delete cbc2; cbc2 = (*path_vec)[0] [0].duplicate();
518 delete path_vec;
519 }
520
521 Geom::Path temporary;
522 temporary.append(*cbc1);
523
524 Geom::Curve const & prev_curve = path_in[path_in.size()].isDegenerate() ? path_in[path_in.size() - 1] : path_in[path_in.size()];
525 Geom::Path isStraight;
526 isStraight.append(prev_curve);
527 isStraight.append(path_in[0]);
528 // does closing path require a join?
529 if (Geom::split_at_cusps(isStraight).size() > 1) {
530 bool outside = outside_angle(prev_curve, path_in[0]);
531 if (outside) {
532 func(temporary, *cbc2, miter_limit, line_width);
533 } else {
534 joinInside(temporary, *cbc2);
535 path_builder.erase(path_builder.begin());
536 }
537
538 // extract the appended curves
539 path_builder.erase_last();
540 if (Geom::are_near(path_builder.finalPoint(), temporary.initialPoint())) {
541 path_builder.setFinal(temporary.initialPoint());
542 } else {
543 path_builder.appendNew<BezierLine>(temporary.initialPoint());
544 }
545 path_builder.append(temporary);
546 } else {
547 // closing path does not require a join
548 path_builder.setFinal(path_builder.initialPoint());
549 }
550 path_builder.close();
551
552 if (cbc1) delete cbc1;
553 if (cbc2) delete cbc2;
554 }
555
556 return path_builder;
557}
558
559Geom::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)
560{
561 Geom::PathVector path_out;
562
563 unsigned pv_size = path_in.size();
564 for (unsigned i = 0; i < pv_size; i++) {
565
566 if (path_in[i].size() > 1) {
567 Geom::Path with_direction;
568 Geom::Path against_direction;
569
570 with_direction = Outline::outlineHalf(path_in[i], -line_width, miter_lim, extrapolate ? extrapolate_curves : reflect_curves);
571 against_direction = Outline::outlineHalf(path_in[i].reverse(), -line_width, miter_lim, extrapolate ? extrapolate_curves : reflect_curves);
572
573 Geom::PathBuilder pb;
574
575 pb.moveTo(with_direction.initialPoint());
576 pb.append(with_direction);
577
578 //add in our line caps
579 if (!path_in[i].closed()) {
580 switch (butt) {
581 case BUTT_STRAIGHT:
582 pb.lineTo(against_direction.initialPoint());
583 break;
584 case BUTT_ROUND:
585 pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, against_direction.initialPoint() );
586 break;
587 case BUTT_POINTY: {
588 Geom::Point end_deriv = -Geom::unitTangentAt(Geom::reverse(path_in[i].back().toSBasis()), 0.);
589 double radius = 0.5 * Geom::distance(with_direction.finalPoint(), against_direction.initialPoint());
590 Geom::Point midpoint = 0.5 * (with_direction.finalPoint() + against_direction.initialPoint()) + radius*end_deriv;
591 pb.lineTo(midpoint);
592 pb.lineTo(against_direction.initialPoint());
593 break;
594 }
595 case BUTT_SQUARE: {
596 Geom::Point end_deriv = -Geom::unitTangentAt(Geom::reverse(path_in[i].back().toSBasis()), 0.);
597 double radius = 0.5 * Geom::distance(with_direction.finalPoint(), against_direction.initialPoint());
598 pb.lineTo(with_direction.finalPoint() + radius*end_deriv);
599 pb.lineTo(against_direction.initialPoint() + radius*end_deriv);
600 pb.lineTo(against_direction.initialPoint());
601 break;
602 }
603 case BUTT_LEANED: {
604 Geom::Point end_deriv = -Geom::unitTangentAt(Geom::reverse(path_in[i].back().toSBasis()), 0.);
605 double maxRadius = (end_lean+0.5) * Geom::distance(with_direction.finalPoint(), against_direction.initialPoint());
606 double minRadius = ((end_lean*-1)+0.5) * Geom::distance(with_direction.finalPoint(), against_direction.initialPoint());
607 pb.lineTo(with_direction.finalPoint() + maxRadius*end_deriv);
608 pb.lineTo(against_direction.initialPoint() + minRadius*end_deriv);
609 pb.lineTo(against_direction.initialPoint());
610 break;
611 }
612 }
613 } else {
614 pb.moveTo(against_direction.initialPoint());
615 }
616
617 pb.append(against_direction);
618
619 //cap (if necessary)
620 if (!path_in[i].closed()) {
621 switch (butt) {
622 case BUTT_STRAIGHT:
623 pb.lineTo(with_direction.initialPoint());
624 break;
625 case BUTT_ROUND:
626 pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, with_direction.initialPoint() );
627 break;
628 case BUTT_POINTY: {
629 Geom::Point end_deriv = -Geom::unitTangentAt(path_in[i].front().toSBasis(), 0.);
630 double radius = 0.5 * Geom::distance(against_direction.finalPoint(), with_direction.initialPoint());
631 Geom::Point midpoint = 0.5 * (against_direction.finalPoint() + with_direction.initialPoint()) + radius*end_deriv;
632 pb.lineTo(midpoint);
633 pb.lineTo(with_direction.initialPoint());
634 break;
635 }
636 case BUTT_SQUARE: {
637 Geom::Point end_deriv = -Geom::unitTangentAt(path_in[i].front().toSBasis(), 0.);
638 double radius = 0.5 * Geom::distance(against_direction.finalPoint(), with_direction.initialPoint());
639 pb.lineTo(against_direction.finalPoint() + radius*end_deriv);
640 pb.lineTo(with_direction.initialPoint() + radius*end_deriv);
641 pb.lineTo(with_direction.initialPoint());
642 break;
643 }
644 case BUTT_LEANED: {
645 Geom::Point end_deriv = -Geom::unitTangentAt(path_in[i].front().toSBasis(), 0.);
646 double maxRadius = (start_lean+0.5) * Geom::distance(against_direction.finalPoint(), with_direction.initialPoint());
647 double minRadius = ((start_lean*-1)+0.5) * Geom::distance(against_direction.finalPoint(), with_direction.initialPoint());
648 pb.lineTo(against_direction.finalPoint() + minRadius*end_deriv);
649 pb.lineTo(with_direction.initialPoint() + maxRadius*end_deriv);
650 pb.lineTo(with_direction.initialPoint());
651 break;
652 }
653 }
654 }
655 pb.flush();
656 path_out.push_back(pb.peek()[0]);
657 if (path_in[i].closed()) {
658 path_out.push_back(pb.peek()[1]);
659 }
660 } else {
661 Path p = Path();
662 Path outlinepath = Path();
663 ButtType original_butt;
664 switch (butt) {
665 case BUTT_STRAIGHT:
666 original_butt = butt_straight;
667 break;
668 case BUTT_ROUND:
669 original_butt = butt_round;
670 break;
671 case butt_pointy: {
672 original_butt = butt_pointy;
673 break;
674 }
675 case BUTT_SQUARE: {
676 original_butt = butt_square;
677 break;
678 }
679 case BUTT_LEANED: {
680 original_butt = butt_straight;
681 break;
682 }
683 }
684 p.LoadPath(path_in[i], Geom::Affine(), false, false);
685 p.Outline(&outlinepath, line_width / 2, static_cast<join_typ>(join), original_butt, miter_lim);
686 Geom::PathVector *pv_p = outlinepath.MakePathVector();
687 //somewhat hack-ish
688 path_out.push_back( (*pv_p)[0].reverse() );
689 if (pv_p) delete pv_p;
690 }
691 }
692 return path_out;
693}
694
695Geom::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)
696{
697 std::vector<Geom::Path> path_out = std::vector<Geom::Path>();
698 if (path_in.empty()) {
699 return path_out;
700 }
701 Path p = Path();
702 Path outlinepath = Path();
703 for (unsigned i = 0; i < path_in.size(); i++) {
704 p.LoadPath(path_in[i], Geom::Affine(), false, ( (i==0) ? false : true));
705 }
706
707#define miter_lim fabs(line_width * miter_limit)
708
709 //magic!
710 ButtType original_butt;
711 switch (linecap_type) {
712 case BUTT_STRAIGHT:
713 original_butt = butt_straight;
714 break;
715 case BUTT_ROUND:
716 original_butt = butt_round;
717 break;
718 case butt_pointy: {
719 original_butt = butt_pointy;
720 break;
721 }
722 case BUTT_SQUARE: {
723 original_butt = butt_square;
724 break;
725 }
726 case BUTT_LEANED: {
727 original_butt = butt_straight;
728 break;
729 }
730 }
731 if (linejoin_type <= LINEJOIN_POINTY) {
732 p.Outline(&outlinepath, line_width / 2, static_cast<join_typ>(linejoin_type),
733 original_butt, miter_lim);
734 // fix memory leak
735 std::vector<Geom::Path> *pv_p = outlinepath.MakePathVector();
736 path_out = *pv_p;
737 delete pv_p;
738
739 } else if (linejoin_type == LINEJOIN_REFLECTED) {
740 // reflected arc join
741 path_out = outlinePath(path_in, line_width, static_cast<LineJoinType>(linejoin_type),
742 linecap_type , miter_lim, false, start_lean, end_lean);
743
744 } else if (linejoin_type == LINEJOIN_EXTRAPOLATED) {
745 // extrapolated arc join
746 path_out = outlinePath(path_in, line_width, LINEJOIN_STRAIGHT, linecap_type, miter_lim, true, start_lean, end_lean);
747 }
748
749#undef miter_lim
750 return path_out;
751}
752
753Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, LineJoinType linejoin_type, double miter_limit)
754{
755
756#define miter_lim fabs(line_width * miter_limit)
757
758 Geom::Path path_out;
759
760 if (linejoin_type <= LINEJOIN_POINTY || path_in.size() <= 1) {
761
762 Geom::PathVector * pathvec;
763
764 Path path_tangent = Path();
765 Path path_outline = Path();
766 path_outline.LoadPath(path_in, Geom::Affine(), false, false);
767 path_outline.OutsideOutline(&path_tangent, line_width / 2, static_cast<join_typ>(linejoin_type), butt_straight, miter_lim);
768
769 pathvec = path_tangent.MakePathVector();
770 path_out = pathvec->front();
771 delete pathvec;
772 return path_out;
773 } else if (linejoin_type == LINEJOIN_REFLECTED) {
774 path_out = outlineHalf(path_in, line_width, miter_lim, reflect_curves);
775 return path_out;
776 } else if (linejoin_type == LINEJOIN_EXTRAPOLATED) {
777 path_out = outlineHalf(path_in, line_width, miter_lim, extrapolate_curves);
778 return path_out;
779 }
780#undef miter_lim
781 return path_out;
782}
783
784} // namespace Outline
785
786/*
787 Local Variables:
788 mode:c++
789 c-file-style:"stroustrup"
790 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
791 indent-tabs-mode:nil
792 fill-column:99
793 End:
794*/
795// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8 :
0796
=== added file 'src/live_effects/pathoutlineprovider.h'
--- src/live_effects/pathoutlineprovider.h 1970-01-01 00:00:00 +0000
+++ src/live_effects/pathoutlineprovider.h 2014-09-07 17:02:32 +0000
@@ -0,0 +1,55 @@
1#ifndef SEEN_PATH_OUTLINE_H
2#define SEEN_PATH_OUTLINE_H
3
4/* Author:
5 * Liam P. White <inkscapebrony@gmail.com>
6 *
7 * Copyright (C) 2014 Author
8 *
9 * Released under GNU GPL, read the file 'COPYING' for more information
10 */
11
12#include <livarot/Path.h>
13#include <livarot/LivarotDefs.h>
14
15enum LineJoinType {
16 LINEJOIN_STRAIGHT,
17 LINEJOIN_ROUND,
18 LINEJOIN_POINTY,
19 LINEJOIN_REFLECTED,
20 LINEJOIN_EXTRAPOLATED
21};
22enum ButtTypeMod {
23 BUTT_STRAIGHT,
24 BUTT_ROUND,
25 BUTT_SQUARE,
26 BUTT_POINTY,
27 BUTT_LEANED
28};
29
30namespace Geom
31{
32 Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2<Geom::SBasis> const & sbasis_in);
33 std::vector<Geom::Path> split_at_cusps(const Geom::Path& in);
34}
35
36namespace Outline
37{
38 unsigned bezierOrder (const Geom::Curve* curve_in);
39 std::vector<Geom::Path> PathVectorOutline(std::vector<Geom::Path> const & path_in, double line_width, ButtTypeMod linecap_type,
40 LineJoinType linejoin_type, double miter_limit, double start_lean = 0, double end_lean = 0);
41 Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, LineJoinType linejoin_type, double miter_limit);
42}
43
44#endif // SEEN_PATH_OUTLINE_H
45
46/*
47 Local Variables:
48 mode:c++
49 c-file-style:"stroustrup"
50 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
51 indent-tabs-mode:nil
52 fill-column:99
53 End:
54*/
55// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 :
056
=== modified file 'src/menus-skeleton.h'
--- src/menus-skeleton.h 2014-03-30 22:08:13 +0000
+++ src/menus-skeleton.h 2014-09-07 17:02:32 +0000
@@ -178,6 +178,9 @@
178" <verb verb-id=\"DialogLayers\" />\n"178" <verb verb-id=\"DialogLayers\" />\n"
179" </submenu>\n"179" </submenu>\n"
180" <submenu name=\"" N_("_Object") "\">\n"180" <submenu name=\"" N_("_Object") "\">\n"
181" <verb verb-id=\"DialogObjects\" />\n"
182" <verb verb-id=\"DialogTags\" />\n"
183" <separator/>\n"
181" <verb verb-id=\"DialogFillStroke\" />\n"184" <verb verb-id=\"DialogFillStroke\" />\n"
182" <verb verb-id=\"DialogObjectProperties\" />\n"185" <verb verb-id=\"DialogObjectProperties\" />\n"
183" <verb verb-id=\"DialogSymbols\" />\n"186" <verb verb-id=\"DialogSymbols\" />\n"
184187
=== modified file 'src/path-chemistry.cpp'
--- src/path-chemistry.cpp 2014-03-27 01:33:44 +0000
+++ src/path-chemistry.cpp 2014-09-07 17:02:32 +0000
@@ -22,6 +22,7 @@
22#include "xml/repr.h"22#include "xml/repr.h"
23#include "svg/svg.h"23#include "svg/svg.h"
24#include "display/curve.h"24#include "display/curve.h"
25#include "color.h"
25#include <glib.h>26#include <glib.h>
26#include <glibmm/i18n.h>27#include <glibmm/i18n.h>
27#include "sp-path.h"28#include "sp-path.h"
@@ -433,6 +434,10 @@
433 gchar *title = item->title();434 gchar *title = item->title();
434 // remember description435 // remember description
435 gchar *desc = item->desc();436 gchar *desc = item->desc();
437 // remember highlight color
438 guint32 highlight_color = 0;
439 if (item->isHighlightSet())
440 highlight_color = item->highlight_color();
436441
437 // It's going to resurrect, so we delete without notifying listeners.442 // It's going to resurrect, so we delete without notifying listeners.
438 item->deleteObject(false);443 item->deleteObject(false);
@@ -450,6 +455,9 @@
450 newObj->setDesc(desc);455 newObj->setDesc(desc);
451 g_free(desc);456 g_free(desc);
452 }457 }
458 if (highlight_color && newObj) {
459 SP_ITEM(newObj)->setHighlightColor( highlight_color );
460 }
453461
454 // move to the saved position462 // move to the saved position
455 repr->setPosition(pos > 0 ? pos : 0);463 repr->setPosition(pos > 0 ? pos : 0);
456464
=== modified file 'src/selection-chemistry.cpp'
--- src/selection-chemistry.cpp 2014-08-17 14:46:20 +0000
+++ src/selection-chemistry.cpp 2014-09-07 17:02:32 +0000
@@ -2792,54 +2792,53 @@
2792 if (desktop == NULL) {2792 if (desktop == NULL) {
2793 return;2793 return;
2794 }2794 }
27952795
2796 Inkscape::Selection *selection = sp_desktop_selection(desktop);2796 Inkscape::SVGOStringStream os;
2797 SPItem *item = selection->singleItem();2797 SPObject * firstItem = NULL;
2798 if (g_slist_length(const_cast<GSList *>(selection->itemList())) != 1 || !item) {2798 for (const GSList * item = desktop->selection->itemList(); item != NULL; item = item->next) {
2799 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>one</b> path to clone."));2799 if (SP_IS_SHAPE(item->data) || SP_IS_TEXT(item->data)) {
2800 return;2800 if (firstItem) {
2801 }2801 os << "|";
2802 if ( !(SP_IS_SHAPE(item) || SP_IS_TEXT(item)) ) {2802 } else {
2803 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select one <b>path</b> to clone."));2803 firstItem = SP_ITEM(item->data);
2804 return;2804 }
2805 }2805 os << "#" << SP_ITEM(item->data)->getId() << ",0";
28062806 }
2807 Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();2807 }
2808 Inkscape::XML::Node *parent = item->getRepr()->parent();2808 if (firstItem) {
28092809 Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
2810 // create the LPE2810 SPObject *parent = firstItem->parent;
2811 Inkscape::XML::Node *lpe_repr = xml_doc->createElement("inkscape:path-effect");2811
2812 {2812 // create the LPE
2813 lpe_repr->setAttribute("effect", "clone_original");2813 Inkscape::XML::Node *lpe_repr = xml_doc->createElement("inkscape:path-effect");
2814 gchar *href = g_strdup_printf("#%s", item->getRepr()->attribute("id"));2814 {
2815 lpe_repr->setAttribute("linkedpath", href);2815 lpe_repr->setAttribute("effect", "fill_between_many");
2816 g_free(href);2816 lpe_repr->setAttribute("linkedpaths", os.str().c_str());
2817 desktop->doc()->getDefs()->getRepr()->addChild(lpe_repr, NULL); // adds to <defs> and assigns the 'id' attribute2817 desktop->doc()->getDefs()->getRepr()->addChild(lpe_repr, NULL); // adds to <defs> and assigns the 'id' attribute
2818 }2818 }
2819 const gchar * lpe_id = lpe_repr->attribute("id");2819 const gchar * lpe_id = lpe_repr->attribute("id");
2820 Inkscape::GC::release(lpe_repr);2820 Inkscape::GC::release(lpe_repr);
28212821
2822 // create the new path2822 // create the new path
2823 Inkscape::XML::Node *clone = xml_doc->createElement("svg:path");2823 Inkscape::XML::Node *clone = xml_doc->createElement("svg:path");
2824 {2824 {
2825 clone->setAttribute("d", "M 0 0", false);2825 clone->setAttribute("d", "M 0 0", false);
2826 // add the new clone to the top of the original's parent2826 // add the new clone to the top of the original's parent
2827 parent->appendChild(clone);2827 parent->appendChildRepr(clone);
2828 SPObject *clone_obj = desktop->doc()->getObjectById(clone->attribute("id"));2828 SPObject *clone_obj = desktop->doc()->getObjectById(clone->attribute("id"));
2829 if (SP_IS_LPE_ITEM(clone_obj)) {2829 if (SP_IS_LPE_ITEM(clone_obj)) {
2830 gchar *href = g_strdup_printf("#%s", lpe_id);2830 gchar *href = g_strdup_printf("#%s", lpe_id);
2831 SP_LPE_ITEM(clone_obj)->addPathEffect( href, false );2831 //sp_lpe_item_add_path_effect( SP_LPE_ITEM(clone_obj), href, false );
2832 g_free(href);2832 SP_LPE_ITEM(clone_obj)->addPathEffect(href, false);
2833 }2833 g_free(href);
2834 }2834 }
28352835 }
2836 DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_EDIT_CLONE_ORIGINAL_PATH_LPE,2836
2837 _("Clone original path"));2837 DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_EDIT_CLONE_ORIGINAL_PATH_LPE,
28382838 _("Fill between strokes"));
2839 // select the new object:2839 } else {
2840 selection->set(clone);2840 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select path(s) to fill."));
28412841 }
2842 Inkscape::GC::release(clone);
2843}2842}
28442843
2845void sp_selection_to_marker(SPDesktop *desktop, bool apply)2844void sp_selection_to_marker(SPDesktop *desktop, bool apply)
@@ -3663,6 +3662,118 @@
3663 g_free(filepath);3662 g_free(filepath);
3664}3663}
36653664
3665/* Creates a mask or clipPath from selection.
3666 * What is a clip group?
3667 * A clip group is a tangled mess of XML that allows an object inside a group
3668 * to clip the entire group using a few <use>s and generally irritating me.
3669 */
3670
3671void sp_selection_set_clipgroup(SPDesktop *desktop)
3672{
3673 if (desktop == NULL) {
3674 return;
3675 }
3676 SPDocument* doc = sp_desktop_document(desktop);
3677 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
3678
3679 Inkscape::Selection *selection = sp_desktop_selection(desktop);
3680 if (selection->isEmpty()) {
3681 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to create clippath or mask from."));
3682 return;
3683 }
3684
3685 GSList const *l = const_cast<GSList *>(selection->reprList());
3686
3687 GSList *p = g_slist_copy(const_cast<GSList *>(l));
3688
3689 p = g_slist_sort(p, (GCompareFunc) sp_repr_compare_position);
3690
3691 selection->clear();
3692
3693 gint topmost = (static_cast<Inkscape::XML::Node *>(g_slist_last(p)->data))->position();
3694 Inkscape::XML::Node *topmost_parent = (static_cast<Inkscape::XML::Node *>(g_slist_last(p)->data))->parent();
3695
3696 Inkscape::XML::Node *inner = xml_doc->createElement("svg:g");
3697 inner->setAttribute("inkscape:label", "Clip");
3698
3699 while (p) {
3700 Inkscape::XML::Node *current = static_cast<Inkscape::XML::Node *>(p->data);
3701
3702 if (current->parent() == topmost_parent) {
3703 Inkscape::XML::Node *spnew = current->duplicate(xml_doc);
3704 sp_repr_unparent(current);
3705 inner->appendChild(spnew);
3706 Inkscape::GC::release(spnew);
3707 topmost --; // only reduce count for those items deleted from topmost_parent
3708 } else { // move it to topmost_parent first
3709 GSList *temp_clip = NULL;
3710
3711 // At this point, current may already have no item, due to its being a clone whose original is already moved away
3712 // So we copy it artificially calculating the transform from its repr->attr("transform") and the parent transform
3713 gchar const *t_str = current->attribute("transform");
3714 Geom::Affine item_t(Geom::identity());
3715 if (t_str)
3716 sp_svg_transform_read(t_str, &item_t);
3717 item_t *= SP_ITEM(doc->getObjectByRepr(current->parent()))->i2doc_affine();
3718 // FIXME: when moving both clone and original from a transformed group (either by
3719 // grouping into another parent, or by cut/paste) the transform from the original's
3720 // parent becomes embedded into original itself, and this affects its clones. Fix
3721 // this by remembering the transform diffs we write to each item into an array and
3722 // then, if this is clone, looking up its original in that array and pre-multiplying
3723 // it by the inverse of that original's transform diff.
3724
3725 sp_selection_copy_one(current, item_t, &temp_clip, xml_doc);
3726 sp_repr_unparent(current);
3727
3728 // paste into topmost_parent (temporarily)
3729 GSList *copied = sp_selection_paste_impl(doc, doc->getObjectByRepr(topmost_parent), &temp_clip);
3730 if (temp_clip) g_slist_free(temp_clip);
3731 if (copied) { // if success,
3732 // take pasted object (now in topmost_parent)
3733 Inkscape::XML::Node *in_topmost = static_cast<Inkscape::XML::Node *>(copied->data);
3734 // make a copy
3735 Inkscape::XML::Node *spnew = in_topmost->duplicate(xml_doc);
3736 // remove pasted
3737 sp_repr_unparent(in_topmost);
3738 // put its copy into group
3739 inner->appendChild(spnew);
3740 Inkscape::GC::release(spnew);
3741 g_slist_free(copied);
3742 }
3743 }
3744 p = g_slist_remove(p, current);
3745 }
3746
3747 Inkscape::XML::Node *outer = xml_doc->createElement("svg:g");
3748 outer->appendChild(inner);
3749 topmost_parent->appendChild(outer);
3750 outer->setPosition(topmost + 1);
3751
3752 Inkscape::XML::Node *clone = xml_doc->createElement("svg:use");
3753 clone->setAttribute("x", "0", false);
3754 clone->setAttribute("y", "0", false);
3755 clone->setAttribute("xlink:href", g_strdup_printf("#%s", inner->attribute("id")), false);
3756
3757 clone->setAttribute("inkscape:transform-center-x", inner->attribute("inkscape:transform-center-x"), false);
3758 clone->setAttribute("inkscape:transform-center-y", inner->attribute("inkscape:transform-center-y"), false);
3759
3760 const Geom::Affine maskTransform(Geom::Affine::identity());
3761 GSList *templist = NULL;
3762
3763 templist = g_slist_append(templist, clone);
3764 // add the new clone to the top of the original's parent
3765 gchar const *mask_id = SPClipPath::create(templist, doc, &maskTransform);
3766
3767 g_slist_free(templist);
3768
3769 outer->setAttribute("clip-path", g_strdup_printf("url(#%s)", mask_id));
3770
3771 Inkscape::GC::release(clone);
3772
3773 selection->set(outer);
3774 DocumentUndo::done(doc, SP_VERB_OBJECT_SET_CLIPPATH, _("Create Clip Group"));
3775}
3776
3666/**3777/**
3667 * Creates a mask or clipPath from selection.3778 * Creates a mask or clipPath from selection.
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches