Code

UI fixes (a.o. Bug #560751 )
[inkscape.git] / src / live_effects / lpe-curvestitch.cpp
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"
18 #include "sp-item.h"
19 #include "sp-path.h"
20 #include "svg/svg.h"
21 #include "xml/repr.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>
32 #include "ui/widget/scalar.h"
33 #include "libnr/nr-values.h"
35 namespace Inkscape {
36 namespace LivePathEffect {
38 using namespace Geom;
40 LPECurveStitch::LPECurveStitch(LivePathEffectObject *lpeobject) :
41     Effect(lpeobject),
42     strokepath(_("Stitch path:"), _("The path that will be used as stitch."), "strokepath", &wr, this, "M0,0 L1,0"),
43     nrofpaths(_("Number of paths:"), _("The number of paths that will be generated."), "count", &wr, this, 5),
44     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),
45     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),
46     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),
47     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),
48     prop_scale(_("Scale width:"), _("Scale the width of the stitch path"), "prop_scale", &wr, this, 1),
49     scale_y_rel(_("Scale width relative to length"), _("Scale the width of the stitch path relative to its length"), "scale_y_rel", &wr, this, false)
50 {
51     registerParameter( dynamic_cast<Parameter *>(&nrofpaths) );
52     registerParameter( dynamic_cast<Parameter *>(&startpoint_edge_variation) );
53     registerParameter( dynamic_cast<Parameter *>(&startpoint_spacing_variation) );
54     registerParameter( dynamic_cast<Parameter *>(&endpoint_edge_variation) );
55     registerParameter( dynamic_cast<Parameter *>(&endpoint_spacing_variation) );
56     registerParameter( dynamic_cast<Parameter *>(&strokepath) );
57     registerParameter( dynamic_cast<Parameter *>(&prop_scale) );
58     registerParameter( dynamic_cast<Parameter *>(&scale_y_rel) );
60     nrofpaths.param_make_integer();
61     nrofpaths.param_set_range(2, NR_HUGE);
63     prop_scale.param_set_digits(3);
64     prop_scale.param_set_increments(0.01, 0.10);
65 }
67 LPECurveStitch::~LPECurveStitch()
68 {
70 }
72 std::vector<Geom::Path>
73 LPECurveStitch::doEffect_path (std::vector<Geom::Path> const & path_in)
74 {
75     if (path_in.size() >= 2) {
76         startpoint_edge_variation.resetRandomizer();
77         endpoint_edge_variation.resetRandomizer();
78         startpoint_spacing_variation.resetRandomizer();
79         endpoint_spacing_variation.resetRandomizer();
81         D2<Piecewise<SBasis> > stroke = make_cuts_independent(strokepath.get_pwd2());
82         OptInterval bndsStroke = bounds_exact(stroke[0]);
83         OptInterval bndsStrokeY = bounds_exact(stroke[1]);
84         if (!bndsStroke && !bndsStrokeY) {
85             return path_in;
86         }
87         gdouble scaling = bndsStroke->max() - bndsStroke->min();
88         Point stroke_origin(bndsStroke->min(), (bndsStrokeY->max()+bndsStrokeY->min())/2);
90         std::vector<Geom::Path> path_out;
92         // do this for all permutations (ii,jj) if there are more than 2 paths? realllly cool!
93         for (unsigned ii = 0   ; ii < path_in.size() - 1; ii++)
94         for (unsigned jj = ii+1; jj < path_in.size(); jj++)
95         {
96             Piecewise<D2<SBasis> > A = arc_length_parametrization(Piecewise<D2<SBasis> >(path_in[ii].toPwSb()),2,.1);
97             Piecewise<D2<SBasis> > B = arc_length_parametrization(Piecewise<D2<SBasis> >(path_in[jj].toPwSb()),2,.1);
98             Interval bndsA = A.domain();
99             Interval bndsB = B.domain();
100             gdouble incrementA = (bndsA.max()-bndsA.min()) / (nrofpaths-1);
101             gdouble incrementB = (bndsB.max()-bndsB.min()) / (nrofpaths-1);
102             gdouble tA = bndsA.min();
103             gdouble tB = bndsB.min();
104             gdouble tAclean = tA; // the tA without spacing_variation
105             gdouble tBclean = tB; // the tB without spacing_variation
107             for (int i = 0; i < nrofpaths; i++) {
108                 Point start = A(tA);
109                 Point end = B(tB);
110                 if (startpoint_edge_variation.get_value() != 0)
111                     start = start + (startpoint_edge_variation - startpoint_edge_variation.get_value()/2) * (end - start);
112                 if (endpoint_edge_variation.get_value() != 0)
113                     end = end + (endpoint_edge_variation - endpoint_edge_variation.get_value()/2)* (end - start);
114         
115                 if (!Geom::are_near(start,end)) {
116                     gdouble scaling_y = 1.0;
117                     if (scale_y_rel.get_value()) {
118                         scaling_y = (L2(end-start)/scaling)*prop_scale;
119                     } else {
120                         scaling_y = prop_scale;
121                     }
123                     Matrix transform;
124                     transform.setXAxis( (end-start) / scaling );
125                     transform.setYAxis( rot90(unit_vector(end-start)) * scaling_y);
126                     transform.setTranslation( start );
127                     Piecewise<D2<SBasis> > pwd2_out = (strokepath.get_pwd2()-stroke_origin) * transform;
129                     // add stuff to one big pw<d2<sbasis> > and then outside the loop convert to path?
130                     // No: this way, the separate result paths are kept separate which might come in handy some time!
131                     std::vector<Geom::Path> result = Geom::path_from_piecewise(pwd2_out, LPE_CONVERSION_TOLERANCE);
132                     path_out.push_back(result[0]);
133                 }
134                 gdouble svA = startpoint_spacing_variation - startpoint_spacing_variation.get_value()/2;
135                 gdouble svB = endpoint_spacing_variation - endpoint_spacing_variation.get_value()/2;
136                 tAclean += incrementA;
137                 tBclean += incrementB;
138                 tA = tAclean + incrementA * svA;
139                 tB = tBclean + incrementB * svB;
140                 if (tA > bndsA.max())
141                     tA = bndsA.max();
142                 if (tB > bndsB.max())
143                     tB = bndsB.max();
144             }
145         }
147         return path_out;
148     } else {
149         return path_in;
150     }
153 void
154 LPECurveStitch::resetDefaults(SPItem * item)
156     Effect::resetDefaults(item);
158     if (!SP_IS_PATH(item)) return;
160     using namespace Geom;
162     // set the stroke path to run horizontally in the middle of the bounding box of the original path
163     
164     // calculate bounding box:  (isn't there a simpler way?)
165     Piecewise<D2<SBasis> > pwd2;
166     std::vector<Geom::Path> temppath = sp_svg_read_pathv( SP_OBJECT_REPR(item)->attribute("inkscape:original-d"));
167     for (unsigned int i=0; i < temppath.size(); i++) {
168         pwd2.concat( temppath[i].toPwSb() );
169     }
170     D2<Piecewise<SBasis> > d2pw = make_cuts_independent(pwd2);
171     OptInterval bndsX = bounds_exact(d2pw[0]);
172     OptInterval bndsY = bounds_exact(d2pw[1]);
173     if (bndsX && bndsY) {
174         Point start(bndsX->min(), (bndsY->max()+bndsY->min())/2);
175         Point end(bndsX->max(), (bndsY->max()+bndsY->min())/2);
176         if ( !Geom::are_near(start,end) ) {
177             Geom::Path path;
178             path.start( start );
179             path.appendNew<Geom::LineSegment>( end );
180             strokepath.set_new_value( path.toPwSb(), true );
181         } else {
182             // bounding box is too small to make decent path. set to default default. :-)
183             strokepath.param_set_and_write_default();
184         }
185     } else {
186         // bounding box is non-existent. set to default default. :-)
187         strokepath.param_set_and_write_default();
188     }
192 /** /todo check whether this special case is necessary. It seems to "bug" editing behavior:
193  * scaling an object with transforms preserved behaves differently from scaling with
194  * transforms optimized (difference caused by this special method).
195  * special casing is probably needed, because rotation should not be propagated to the strokepath.
196  */
197 void
198 LPECurveStitch::transform_multiply(Geom::Matrix const& postmul, bool set)
200     // only take translations into account
201     if (postmul.isTranslation()) {
202         strokepath.param_transform_multiply(postmul, set);
203     } else if (!scale_y_rel.get_value()) {
204   // this basically means that for this transformation, the result should be the same as normal scaling the result path
205   // don't know how to do this yet.
206 //        Geom::Matrix new_postmul;
207         //new_postmul.setIdentity();
208 //        new_postmul.setTranslation(postmul.translation());
209 //        Effect::transform_multiply(new_postmul, set);
210     }
213 } //namespace LivePathEffect
214 } /* namespace Inkscape */
216 /*
217   Local Variables:
218   mode:c++
219   c-file-style:"stroustrup"
220   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
221   indent-tabs-mode:nil
222   fill-column:99
223   End:
224 */
225 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :