Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-dynastroke.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/*
6 * Authors:
7 * JF Barraud
8 *
9 * Copyright (C) JF Barraud 2007 <jf.barraud@gmail.com>
10 *
11 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12 */
13
15#include "display/curve.h"
16//# include <libnr/n-art-bpath.h>
17
19#include <2geom/sbasis-math.h>
20// TODO due to internal breakage in glibmm headers, this must be last:
21#include <glibmm/i18n.h>
22
23namespace Inkscape {
24namespace LivePathEffect {
25//TODO: growfor/fadefor can be expressed in unit of width.
26//TODO: make round/sharp end choices independent for start and end.
27//TODO: define more styles like in calligtool.
28//TODO: allow fancy ends.
29
31 {DSM_ELLIPTIC_PEN, N_("Elliptic Pen"), "elliptic_pen"},
32 {DSM_THICKTHIN_FAST, N_("Thick-Thin strokes (fast)"), "thickthin_fast"},
33 {DSM_THICKTHIN_SLOW, N_("Thick-Thin strokes (slow)"), "thickthin_slow"}
34};
36
38 {DSCT_SHARP, N_("Sharp"), "sharp"},
39 {DSCT_ROUND, N_("Round"), "round"},
40};
42
44 Effect(lpeobject),
45 // initialise your parameters here:
46 method(_("Method:"), _("Choose pen type"), "method", DSMethodConverter, &wr, this, DSM_THICKTHIN_FAST),
47 width(_("Pen width:"), _("Maximal stroke width"), "width", &wr, this, 25),
48 roundness(_("Pen roundness:"), _("Min/Max width ratio"), "roundness", &wr, this, .2),
49 angle(_("Angle:"), _("direction of thickest strokes (opposite = thinnest)"), "angle", &wr, this, 45),
50// modulo_pi(_("modulo pi"), _("Give forward and backward moves in one direction the same thickness "), "modulo_pi", &wr, this, false),
51 start_cap(_("Start:"), _("Choose start capping type"), "start_cap", DSCTConverter, &wr, this, DSCT_SHARP),
52 end_cap(_("End:"), _("Choose end capping type"), "end_cap", DSCTConverter, &wr, this, DSCT_SHARP),
53 growfor(_("Grow for:"), _("Make the stroke thinner near it's start"), "growfor", &wr, this, 100),
54 fadefor(_("Fade for:"), _("Make the stroke thinner near it's end"), "fadefor", &wr, this, 100),
55 round_ends(_("Round ends"), _("Strokes end with a round end"), "round_ends", &wr, this, false),
56 capping(_("Capping:"), _("left capping"), "capping", &wr, this, "M 100,5 C 50,5 0,0 0,0 0,0 50,-5 100,-5")
57{
58
63 //registerParameter(&modulo_pi) );
70
71 width.param_set_range(0,std::numeric_limits<double>::max());
73 angle.param_set_range(-360, 360);
74 growfor.param_set_range(0, std::numeric_limits<double>::max());
75 fadefor.param_set_range(0, std::numeric_limits<double>::max());
76
77 show_orig_path = true;
78}
79
81
84{
85 using namespace Geom;
86
87// std::cout<<"do effect: debut\n";
88
89 Piecewise<D2<SBasis> > output;
90 Piecewise<D2<SBasis> > m = pwd2_in;
93 n = rot90(n);
95
96// for (unsigned i=0; i<n.size(); i++){
97// std::cout<<n[i][X]<<"\n";
98// }
99// return m + unitVector(v);
100
101#if 0
103 OptInterval mag = bounds_exact(k);
104 //TODO test if mag is non empty...
105 k = (k-mag->min())*width/mag->extent() + (roundness*width);
106 Piecewise<D2<SBasis> > left = m + k*n;
107 Piecewise<D2<SBasis> > right = m - k*n;
108 right = compose(right,Linear(right.cuts.back(),right.cuts.front()));
109 D2<SBasis> line;
110 line[X] = Linear(left.lastValue()[X],right.firstValue()[X]);
111 line[Y] = Linear(left.lastValue()[Y],right.firstValue()[Y]);
112 output = left;
113 output.concat(Piecewise<D2<SBasis> >(line));
114 output.concat(right);
115 line[X] = Linear(right.lastValue()[X],left.firstValue()[X]);
116 line[Y] = Linear(right.lastValue()[Y],left.firstValue()[Y]);
117 output.concat(Piecewise<D2<SBasis> >(line));
118 return output;
119#else
120
121 double angle_rad = angle*M_PI/180.;//TODO: revert orientation?...
123
124 // std::vector<double> corners = find_corners(m);
125
126 DynastrokeMethod stroke_method = method.get_value();
127 if (roundness==1.) {
128// std::cout<<"round pen.\n";
129 n1 = n*double(width);
130 n2 =-n1;
131 }else{
132 switch(stroke_method) {
133 case DSM_ELLIPTIC_PEN:{
134// std::cout<<"ellptic pen\n";
135 //FIXME: roundness=0???
136 double c = cos(angle_rad), s = sin(angle_rad);
137 Affine rot,slant;
138 rot = Affine(c, -s, s, c, 0, 0 );
139 slant = Affine(double(width)*roundness, 0, 0, double(width), 0, 0 );
140 Piecewise<D2<SBasis> > nn = unitVector(v * ( rot * slant ) );
141 slant = Affine( 0,-roundness, 1, 0, 0, 0 );
142 rot = Affine(-s, -c, c, -s, 0, 0 );
143 nn = nn * (slant * rot );
144
145 n1 = nn*double(width);
146 n2 =-n1;
147 break;
148 }
149 case DSM_THICKTHIN_FAST:{
150// std::cout<<"fast thick thin pen\n";
152 w = n_xy[X]*sin(angle_rad) - n_xy[Y]*cos(angle_rad);
153 w = w * ((1 - roundness)*width/2.) + ((1 + roundness)*width/2.);
154 n1 = w*n;
155 n2 = -n1;
156 break;
157 }
158 case DSM_THICKTHIN_SLOW:{
159// std::cout<<"slow thick thin pen\n";
161 w = n_xy[X]*cos(angle_rad)+ n_xy[Y]*sin(angle_rad);
162 w = w * ((1 - roundness)*width/2.) + ((1 + roundness)*width/2.);
163 //->Slower and less stable, but more accurate .
164 // General formula: n1 = w*u with ||u||=1 and u.v = -dw/dt
166 Piecewise<SBasis> ncomp = sqrt(dot(v,v)-dw*dw,.1,3);
167 //FIXME: is force continuity useful? compatible with corners?
168// std::cout<<"ici\n";
169 n1 = -dw*v + ncomp*rot90(v);
171 n2 = -dw*v - ncomp*rot90(v);
173// std::cout<<"ici2\n";
174 break;
175 }
176 default:{
177 n1 = n*double(width);
178 n2 = n1*(-.5);
179 break;
180 }
181 }//case
182 }//if/else
183
184 //
185 //TODO: insert relevant stitch at each corner!!
186 //
187
188 Piecewise<D2<SBasis> > left, right;
189 if ( m.segs.front().at0() == m.segs.back().at1()){
190 // if closed:
191// std::cout<<"closed input.\n";
192 left = m + n1;//+ n;
193 right = m + n2;//- n;
194 } else {
195 //if not closed, shape the ends:
196 //TODO: allow fancy ends...
197// std::cout<<"shaping the ends\n";
198 double grow_length = growfor;// * width;
199 double fade_length = fadefor;// * width;
201 double totlength = s.segs.back().at1();
202
203 //scale factor for a sharp start
204 SBasis join = SBasis(2,Linear(0,1));
205 join[1] = Linear(1,1);
207 factor_in.cuts[1]=grow_length;
208 if (grow_length < totlength){
209 factor_in.concat(Piecewise<SBasis >(Linear(1)));
210 factor_in.cuts[2]=totlength;
211 }
212// std::cout<<"shaping the ends ici\n";
213 //scale factor for a sharp end
214 join[0] = Linear(1,0);
215 join[1] = Linear(1,1);
216 Piecewise<SBasis > factor_out;
217 if (fade_length < totlength){
218 factor_out = Piecewise<SBasis >(Linear(1));
219 factor_out.cuts[1] = totlength-fade_length;
220 factor_out.concat(Piecewise<SBasis >(join));
221 factor_out.cuts[2] = totlength;
222 }else{
223 factor_out = Piecewise<SBasis >(join);
224 factor_out.setDomain(Interval(totlength-fade_length,totlength));
225 }
226// std::cout<<"shaping the ends ici ici\n";
227
228 Piecewise<SBasis > factor = factor_in*factor_out;
229 n1 = compose(factor,s)*n1;
230 n2 = compose(factor,s)*n2;
231
232 left = m + n1;
233 right = m + n2;
234// std::cout<<"shaping the ends ici ici ici\n";
235
237// std::cout<<"shaping round start\n";
238 SBasis tau(2,Linear(0));
239 tau[1] = Linear(-1,0);
240 Piecewise<SBasis > hbump;
241 hbump.concat(Piecewise<SBasis >(tau*grow_length));
243 hbump.cuts[0]=0;
244 hbump.cuts[1]=fmin(grow_length,totlength*grow_length/(grow_length+fade_length));
245 hbump.cuts[2]=totlength;
246 hbump = compose(hbump,s);
247
248 left += - hbump * rot90(n);
249 right += - hbump * rot90(n);
250 }
251 if (end_cap.get_value() == DSCT_ROUND){
252// std::cout<<"shaping round end\n";
253 SBasis tau(2,Linear(0));
254 tau[1] = Linear(0,1);
255 Piecewise<SBasis > hbump;
257 hbump.concat(Piecewise<SBasis >(tau*fade_length));
258 hbump.cuts[0]=0;
259 hbump.cuts[1]=fmax(totlength-fade_length, totlength*grow_length/(grow_length+fade_length));
260 hbump.cuts[2]=totlength;
261 hbump = compose(hbump,s);
262
263 left += - hbump * rot90(n);
264 right += - hbump * rot90(n);
265 }
266 }
267
268 left = force_continuity(left);
269 right = force_continuity(right);
270
271// std::cout<<"gathering result: left";
272 output = left;
273// std::cout<<" + reverse(right)";
274 output.concat(reverse(right));
275// std::cout<<". done\n";
276
277//-----------
278 return output;
279#endif
280}
281
282
283/* ######################## */
284
285} //namespace LivePathEffect (setq default-directory "c:/Documents And Settings/jf/Mes Documents/InkscapeSVN")
286} /* namespace Inkscape */
287
288/*
289 Local Variables:
290 mode:c++
291 c-file-style:"stroustrup"
292 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
293 indent-tabs-mode:nil
294 fill-column:99
295 End:
296*/
297// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
Conversion between Bezier control points and SBasis curves.
3x3 matrix representing an affine transformation.
Definition affine.h:70
Adaptor that creates 2D functions from 1D ones.
Definition d2.h:55
Range of real numbers that is never empty.
Definition interval.h:59
Function that interpolates linearly between two values.
Definition linear.h:55
Range of real numbers that can be empty.
Definition interval.h:199
Function defined as discrete pieces.
Definition piecewise.h:71
output_type lastValue() const
Definition piecewise.h:109
std::vector< double > cuts
Definition piecewise.h:75
output_type firstValue() const
Definition piecewise.h:106
void concat(const Piecewise< T > &other)
Definition piecewise.h:235
std::vector< T > segs
Definition piecewise.h:76
void setDomain(Interval dom)
Definition piecewise.h:218
Polynomial in symmetric power basis.
Definition sbasis.h:70
void registerParameter(Parameter *param)
Definition effect.cpp:1704
EnumParam< DynastrokeCappingType > start_cap
LPEDynastroke(LivePathEffectObject *lpeobject)
EnumParam< DynastrokeCappingType > end_cap
EnumParam< DynastrokeMethod > method
Geom::Piecewise< Geom::D2< Geom::SBasis > > doEffect_pwd2(Geom::Piecewise< Geom::D2< Geom::SBasis > > const &pwd2_in) override
void param_set_range(double min, double max)
Simplified management of enumerations of svg items with UI labels.
Definition enums.h:42
const double w
Definition conic-4.cpp:19
double c[8][4]
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
LPE <dynastroke> implementation, see lpe-dynastroke.cpp.
Various utility functions.
Definition affine.h:22
SBasisN< n > cos(LinearN< n > bo, int k)
D2< Piecewise< SBasis > > make_cuts_independent(Piecewise< D2< SBasis > > const &a)
Definition d2-sbasis.cpp:75
OptInterval bounds_exact(Bezier const &b)
Definition bezier.cpp:310
Bezier reverse(const Bezier &a)
Definition bezier.h:342
Piecewise< SBasis > curvature(D2< SBasis > const &M, double tol=.01)
SBasisN< n > sqrt(SBasisN< n > const &a, int k)
D2< T > compose(D2< T > const &a, T const &b)
Definition d2.h:405
Bezier derivative(Bezier const &a)
Definition bezier.cpp:282
T dot(D2< T > const &a, D2< T > const &b)
Definition d2.h:355
Piecewise< SBasis > arcLengthSb(D2< SBasis > const &M, double tol=.01)
Piecewise< D2< SBasis > > force_continuity(Piecewise< D2< SBasis > > const &f, double tol=0, bool closed=false)
SBasisN< n > sin(LinearN< n > bo, int k)
D2< T > rot90(D2< T > const &a)
Definition d2.h:397
Piecewise< D2< SBasis > > unitVector(D2< SBasis > const &vect, double tol=.01, unsigned order=3)
static const Util::EnumDataConverter< DynastrokeMethod > DSMethodConverter(DynastrokeMethodData, DSM_END)
static const Util::EnumDataConverter< DynastrokeCappingType > DSCTConverter(DynastrokeCappingTypeData, DSCT_END)
static const Util::EnumData< DynastrokeCappingType > DynastrokeCappingTypeData[DSCT_END]
static const Util::EnumData< DynastrokeMethod > DynastrokeMethodData[DSM_END]
Helper class to stream background task notifications as a series of messages.
static Glib::ustring join(std::vector< Glib::ustring > const &accels, char const separator)
unsigned n1
unsigned n2
some std functions to work with (pw)s-basis
Simplified management of enumerations of svg items with UI labels.
Definition enums.h:27
double width