1
#define INKSCAPE_LPE_CURVESTITCH_CPP
3
* LPE Curve Stitching implementation, used as an example for a base starting class
4
* when implementing new LivePathEffects.
11
* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
13
* Released under GNU GPL, read the file 'COPYING' for more information
16
#include "live_effects/lpe-curvestitch.h"
17
#include "display/curve.h"
18
#include <libnr/n-art-bpath.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>
30
#include <2geom/matrix.h>
33
#include "ui/widget/scalar.h"
34
#include "libnr/nr-values.h"
37
namespace LivePathEffect {
41
LPECurveStitch::LPECurveStitch(LivePathEffectObject *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)
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);
68
LPECurveStitch::~LPECurveStitch()
73
std::vector<Geom::Path>
74
LPECurveStitch::doEffect_path (std::vector<Geom::Path> & path_in)
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;
90
// do this for all permutations (ii,jj) if there are more than 2 paths? realllly cool!
91
for (int ii = 0 ; ii < path_in.size() - 1; ii++)
92
for (int jj = ii+1; jj < path_in.size(); jj++)
94
Piecewise<D2<SBasis> > A = arc_length_parametrization(Piecewise<D2<SBasis> >(path_in[ii].toPwSb()),2,.1);
95
Piecewise<D2<SBasis> > B = arc_length_parametrization(Piecewise<D2<SBasis> >(path_in[jj].toPwSb()),2,.1);
96
Interval bndsA = A.domain();
97
Interval bndsB = B.domain();
98
gdouble incrementA = (bndsA.max()-bndsA.min()) / (nrofpaths-1);
99
gdouble incrementB = (bndsB.max()-bndsB.min()) / (nrofpaths-1);
100
gdouble tA = bndsA.min();
101
gdouble tB = bndsB.min();
102
gdouble tAclean = tA; // the tA without spacing_variation
103
gdouble tBclean = tB; // the tB without spacing_variation
105
for (int i = 0; i < nrofpaths; i++) {
108
if (startpoint_edge_variation.get_value() != 0)
109
start = start + (startpoint_edge_variation - startpoint_edge_variation.get_value()/2) * (end - start);
110
if (endpoint_edge_variation.get_value() != 0)
111
end = end + (endpoint_edge_variation - endpoint_edge_variation.get_value()/2)* (end - start);
113
if (!Geom::are_near(start,end)) {
114
gdouble scaling_y = 1.0;
115
if (scale_y_rel.get_value()) {
116
scaling_y = (L2(end-start)/scaling)*prop_scale;
118
scaling_y = prop_scale;
122
transform.setXAxis( (end-start) / scaling );
123
transform.setYAxis( rot90(unit_vector(end-start)) * scaling_y);
124
transform.setTranslation( start );
125
Piecewise<D2<SBasis> > pwd2_out = (strokepath-stroke_origin) * transform;
127
// add stuff to one big pw<d2<sbasis> > and then outside the loop convert to path?
128
// No: this way, the separate result paths are kept separate which might come in handy some time!
129
std::vector<Geom::Path> result = Geom::path_from_piecewise(pwd2_out, LPE_CONVERSION_TOLERANCE);
130
path_out.push_back(result[0]);
132
gdouble svA = startpoint_spacing_variation - startpoint_spacing_variation.get_value()/2;
133
gdouble svB = endpoint_spacing_variation - endpoint_spacing_variation.get_value()/2;
134
tAclean += incrementA;
135
tBclean += incrementB;
136
tA = tAclean + incrementA * svA;
137
tB = tBclean + incrementB * svB;
138
if (tA > bndsA.max())
140
if (tB > bndsB.max())
152
LPECurveStitch::resetDefaults(SPItem * item)
154
if (!SP_IS_PATH(item)) return;
156
using namespace Geom;
158
// set the stroke path to run horizontally in the middle of the bounding box of the original path
159
Piecewise<D2<SBasis> > pwd2;
160
std::vector<Geom::Path> temppath = SVGD_to_2GeomPath( SP_OBJECT_REPR(item)->attribute("inkscape:original-d"));
161
for (unsigned int i=0; i < temppath.size(); i++) {
162
pwd2.concat( temppath[i].toPwSb() );
165
D2<Piecewise<SBasis> > d2pw = make_cuts_independant(pwd2);
166
Interval bndsX = bounds_exact(d2pw[0]);
167
Interval bndsY = bounds_exact(d2pw[1]);
168
Point start(bndsX.min(), (bndsY.max()+bndsY.min())/2);
169
Point end(bndsX.max(), (bndsY.max()+bndsY.min())/2);
171
if ( !Geom::are_near(start,end) ) {
174
path.appendNew<Geom::LineSegment>( end );
175
strokepath.param_set_and_write_new_value( path.toPwSb() );
177
// bounding box is too small to make decent path. set to default default. :-)
178
strokepath.param_set_and_write_default();
183
LPECurveStitch::transform_multiply(Geom::Matrix const& postmul, bool set)
185
// only take translations into account
186
if (postmul.isTranslation()) {
187
strokepath.param_transform_multiply(postmul, set);
188
} else if (!scale_y_rel.get_value()) {
189
// this basically means that for this transformation, the result should be the same as normal scaling the result path
190
// don't know how to do this yet.
191
// Geom::Matrix new_postmul;
192
//new_postmul.setIdentity();
193
// new_postmul.setTranslation(postmul.translation());
194
// Effect::transform_multiply(new_postmul, set);
198
} //namespace LivePathEffect
199
} /* namespace Inkscape */
204
c-file-style:"stroustrup"
205
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
210
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :