98814eab0ef09d0853b31d5aa32e4d63362aa8ff
1 #define INKSCAPE_LPE_CURVESTITCH_CPP
2 /** \file
3 * LPE Curve Stitching implementation, used as an example for a base starting class
4 * when implementing new LivePathEffects.
5 *
6 */
7 /*
8 * Authors:
9 * Johan Engelen
10 *
11 * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
16 #include "live_effects/lpe-curvestitch.h"
17 #include "display/curve.h"
18 #include <libnr/n-art-bpath.h>
19 #include "sp-item.h"
20 #include "sp-path.h"
21 #include "live_effects/n-art-bpath-2geom.h"
23 #include <2geom/path.h>
24 #include <2geom/piecewise.h>
25 #include <2geom/sbasis.h>
26 #include <2geom/sbasis-geometric.h>
27 #include <2geom/bezier-to-sbasis.h>
28 #include <2geom/sbasis-to-bezier.h>
29 #include <2geom/d2.h>
30 #include <2geom/matrix.h>
33 #include "ui/widget/scalar.h"
34 #include "libnr/nr-values.h"
36 namespace Inkscape {
37 namespace LivePathEffect {
39 using namespace Geom;
41 LPECurveStitch::LPECurveStitch(LivePathEffectObject *lpeobject) :
42 Effect(lpeobject),
43 strokepath(_("Stroke path"), _("The path that will be used as stitch."), "strokepath", &wr, this, "M0,0 L1,0"),
44 nrofpaths(_("Number of paths"), _("The number of paths that will be generated."), "count", &wr, this, 5),
45 startpoint_edge_variation(_("Start edge variance"), _("The amount of random jitter to move the start points of the stitches inside & outside the guide path"), "startpoint_edge_variation", &wr, this, 0),
46 startpoint_spacing_variation(_("Start spacing variance"), _("The amount of random shifting to move the start points of the stitches back & forth along the guide path"), "startpoint_spacing_variation", &wr, this, 0),
47 endpoint_edge_variation(_("End edge variance"), _("The amount of randomness that moves the end points of the stitches inside & outside the guide path"), "endpoint_edge_variation", &wr, this, 0),
48 endpoint_spacing_variation(_("End spacing variance"), _("The amount of random shifting to move the end points of the stitches back & forth along the guide path"), "endpoint_spacing_variation", &wr, this, 0),
49 prop_scale(_("Scale width"), _("Scaling of the width of the stroke path"), "prop_scale", &wr, this, 1),
50 scale_y_rel(_("Scale width relative"), _("Scale the width of the stroke path relative to its length"), "scale_y_rel", &wr, this, false)
51 {
52 registerParameter( dynamic_cast<Parameter *>(&nrofpaths) );
53 registerParameter( dynamic_cast<Parameter *>(&startpoint_edge_variation) );
54 registerParameter( dynamic_cast<Parameter *>(&startpoint_spacing_variation) );
55 registerParameter( dynamic_cast<Parameter *>(&endpoint_edge_variation) );
56 registerParameter( dynamic_cast<Parameter *>(&endpoint_spacing_variation) );
57 registerParameter( dynamic_cast<Parameter *>(&strokepath) );
58 registerParameter( dynamic_cast<Parameter *>(&prop_scale) );
59 registerParameter( dynamic_cast<Parameter *>(&scale_y_rel) );
61 nrofpaths.param_make_integer();
62 nrofpaths.param_set_range(2, NR_HUGE);
64 prop_scale.param_set_digits(3);
65 prop_scale.param_set_increments(0.01, 0.10);
66 }
68 LPECurveStitch::~LPECurveStitch()
69 {
71 }
73 std::vector<Geom::Path>
74 LPECurveStitch::doEffect_path (std::vector<Geom::Path> & path_in)
75 {
76 if (path_in.size() >= 2) {
77 startpoint_edge_variation.resetRandomizer();
78 endpoint_edge_variation.resetRandomizer();
79 startpoint_spacing_variation.resetRandomizer();
80 endpoint_spacing_variation.resetRandomizer();
82 D2<Piecewise<SBasis> > stroke = make_cuts_independant(strokepath);
83 Interval bndsStroke = bounds_exact(stroke[0]);
84 gdouble scaling = bndsStroke.max() - bndsStroke.min();
85 Interval bndsStrokeY = bounds_exact(stroke[1]);
86 Point stroke_origin(bndsStroke.min(), (bndsStrokeY.max()+bndsStrokeY.min())/2);
88 std::vector<Geom::Path> path_out (nrofpaths);
90 // do this for all permutations if there are more than 2 paths? realllly cool!
91 Piecewise<D2<SBasis> > A = arc_length_parametrization(Piecewise<D2<SBasis> >(path_in[0].toPwSb()),2,.1);
92 Piecewise<D2<SBasis> > B = arc_length_parametrization(Piecewise<D2<SBasis> >(path_in[1].toPwSb()),2,.1);
93 Interval bndsA = A.domain();
94 Interval bndsB = B.domain();
95 gdouble incrementA = (bndsA.max()-bndsA.min()) / (nrofpaths-1);
96 gdouble incrementB = (bndsB.max()-bndsB.min()) / (nrofpaths-1);
97 gdouble tA = bndsA.min();
98 gdouble tB = bndsB.min();
99 gdouble tAclean = tA; // the tA without spacing_variation
100 gdouble tBclean = tB; // the tB without spacing_variation
101 for (int i = 0; i < nrofpaths; i++) {
102 Point start = A(tA);
103 Point end = B(tB);
104 if (startpoint_edge_variation.get_value() != 0)
105 start = start + (startpoint_edge_variation - startpoint_edge_variation.get_value()/2) * (end - start);
106 if (endpoint_edge_variation.get_value() != 0)
107 end = end + (endpoint_edge_variation - endpoint_edge_variation.get_value()/2)* (end - start);
109 gdouble scaling_y = 1.0;
110 if (scale_y_rel.get_value()) {
111 scaling_y = (L2(end-start)/scaling)*prop_scale;
112 } else {
113 scaling_y = prop_scale;
114 }
116 Matrix transform;
117 transform.setXAxis( (end-start) / scaling );
118 transform.setYAxis( rot90(unit_vector(end-start)) * scaling_y);
119 transform.setTranslation( start );
120 Piecewise<D2<SBasis> > pwd2_out = (strokepath-stroke_origin) * transform;
121 // add stuff to one big pw<d2<sbasis> > and then outside the loop convert to path?
122 std::vector<Geom::Path> result = Geom::path_from_piecewise(pwd2_out, LPE_CONVERSION_TOLERANCE);
123 path_out[i] = result[0];
124 gdouble svA = startpoint_spacing_variation - startpoint_spacing_variation.get_value()/2;
125 gdouble svB = endpoint_spacing_variation - endpoint_spacing_variation.get_value()/2;
126 tAclean += incrementA;
127 tBclean += incrementB;
128 tA = tAclean + incrementA * svA;
129 tB = tBclean + incrementB * svB;
130 if (tA > bndsA.max())
131 tA = bndsA.max();
132 if (tB > bndsB.max())
133 tB = bndsB.max();
134 }
136 return path_out;
137 } else {
138 return path_in;
139 }
140 }
142 void
143 LPECurveStitch::resetDefaults(SPItem * item)
144 {
145 if (!SP_IS_PATH(item)) return;
147 using namespace Geom;
149 // set the stroke path to run horizontally in the middle of the bounding box of the original path
150 Piecewise<D2<SBasis> > pwd2;
151 std::vector<Geom::Path> temppath = SVGD_to_2GeomPath( SP_OBJECT_REPR(item)->attribute("inkscape:original-d"));
152 for (unsigned int i=0; i < temppath.size(); i++) {
153 pwd2.concat( temppath[i].toPwSb() );
154 }
156 D2<Piecewise<SBasis> > d2pw = make_cuts_independant(pwd2);
157 Interval bndsX = bounds_exact(d2pw[0]);
158 Interval bndsY = bounds_exact(d2pw[1]);
159 Point start(bndsX.min(), (bndsY.max()+bndsY.min())/2);
160 Point end(bndsX.max(), (bndsY.max()+bndsY.min())/2);
162 Geom::Path path;
163 path.start( start );
164 path.appendNew<Geom::LineSegment>( end );
165 strokepath.param_set_and_write_new_value( path.toPwSb() );
166 }
168 void
169 LPECurveStitch::transform_multiply(Geom::Matrix const& postmul, bool set)
170 {
171 // only take translations into account
172 if (postmul.isTranslation()) {
173 strokepath.param_transform_multiply(postmul, set);
174 } else if (!scale_y_rel.get_value()) {
175 // this basically means that for this transformation, the result should be the same as normal scaling the result path
176 // don't know how to do this yet.
177 // Geom::Matrix new_postmul;
178 //new_postmul.setIdentity();
179 // new_postmul.setTranslation(postmul.translation());
180 // Effect::transform_multiply(new_postmul, set);
181 }
182 }
184 } //namespace LivePathEffect
185 } /* namespace Inkscape */
187 /*
188 Local Variables:
189 mode:c++
190 c-file-style:"stroustrup"
191 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
192 indent-tabs-mode:nil
193 fill-column:99
194 End:
195 */
196 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :