Code

Extensions. Add option to choose dxf output units
[inkscape.git] / src / live_effects / lpe-dynastroke.cpp
1 #define INKSCAPE_LPE_DYNASTROKE_CPP
2 /** \file
3  * LPE <dynastroke> implementation
4  */
5 /*
6  * Authors:
7  *   JF Barraud
8 *
9 * Copyright (C) JF Barraud 2007 <jf.barraud@gmail.com>
10  *
11  * Released under GNU GPL, read the file 'COPYING' for more information
12  */
14 #include "live_effects/lpe-dynastroke.h"
15 #include "display/curve.h"
16 //# include <libnr/n-art-bpath.h>
18 #include <2geom/path.h>
19 #include <2geom/sbasis.h>
20 #include <2geom/sbasis-geometric.h>
21 #include <2geom/bezier-to-sbasis.h>
22 #include <2geom/sbasis-to-bezier.h>
23 #include <2geom/d2.h>
24 #include <2geom/d2-sbasis.h>
25 #include <2geom/sbasis-math.h>
26 #include <2geom/piecewise.h>
28 namespace Inkscape {
29 namespace LivePathEffect {
31 //----------------------------------------------------------
32 //--- TODO: Test this and move to 2Geom --------------------
33 //----------------------------------------------------------
35 static
36 std::vector<double> 
37 find_corners (Geom::Piecewise<Geom::D2<Geom::SBasis> > const &m){
38     using namespace Geom;
39     std::vector<double> output = std::vector<double>();
40     Piecewise<D2<SBasis> > v = derivative(m);
41     for (unsigned i=0; i<m.size()-1; i++){
42         if ( m.segs[i].at1() == m.segs[i+1].at0() && 
43              v.segs[i].at1() != v.segs[i+1].at0()){
44             output.push_back(m.cuts[i+1]);
45         }
46     }
47     return output;
48 }
50 //----------------------------------------------------------
51 //----------------------------------------------------------
52 //----------------------------------------------------------
54 //TODO: growfor/fadefor can be expressed in unit of width.
55 //TODO: make round/sharp end choices independant for start and end.
56 //TODO: define more styles like in calligtool.
57 //TODO: allow fancy ends.
59 static const Util::EnumData<DynastrokeMethod> DynastrokeMethodData[DSM_END] = {
60     {DSM_ELLIPTIC_PEN,     N_("Elliptic Pen"),        "elliptic_pen"},
61     {DSM_THICKTHIN_FAST, N_("Thick-Thin strokes (fast)"),    "thickthin_fast"},
62     {DSM_THICKTHIN_SLOW, N_("Thick-Thin strokes (slow)"),    "thickthin_slow"}
63 };
64 static const Util::EnumDataConverter<DynastrokeMethod> DSMethodConverter(DynastrokeMethodData, DSM_END);
66 static const Util::EnumData<DynastrokeCappingType> DynastrokeCappingTypeData[DSCT_END] = {
67     {DSCT_SHARP, N_("Sharp"),    "sharp"},
68     {DSCT_ROUND, N_("Round"),    "round"},
69 };
70 static const Util::EnumDataConverter<DynastrokeCappingType> DSCTConverter(DynastrokeCappingTypeData, DSCT_END);
72 LPEDynastroke::LPEDynastroke(LivePathEffectObject *lpeobject) :
73     Effect(lpeobject),
74     // initialise your parameters here:
75     method(_("Method"), _("Choose pen type"), "method", DSMethodConverter, &wr, this, DSM_THICKTHIN_FAST),
76     width(_("Pen width"), _("Maximal stroke width"), "width", &wr, this, 25),
77     roundness(_("Pen roundness"), _("Min/Max width ratio"), "roundness", &wr, this, .2),
78     angle(_("angle"), _("direction of thickest strokes (opposite = thinest)"), "angle", &wr, this, 45),
79 //    modulo_pi(_("modulo pi"), _("Give forward and backward moves in one direction the same thickness "), "modulo_pi", &wr, this, false),
80     start_cap(_("Start"), _("Choose start capping type"), "start_cap", DSCTConverter, &wr, this, DSCT_SHARP),
81     end_cap(_("End"), _("Choose end capping type"), "end_cap", DSCTConverter, &wr, this, DSCT_SHARP),
82     growfor(_("Grow for"), _("Make the stroke thiner near it's start"), "growfor", &wr, this, 100),
83     fadefor(_("Fade for"), _("Make the stroke thiner near it's end"), "fadefor", &wr, this, 100),
84     round_ends(_("Round ends"), _("Strokes end with a round end"), "round_ends", &wr, this, false),
85     capping(_("Capping"), _("left capping"), "capping", &wr, this, "M 100,5 C 50,5 0,0 0,0 0,0 50,-5 100,-5")
86 {
88     registerParameter( dynamic_cast<Parameter *>(& method) );
89     registerParameter( dynamic_cast<Parameter *>(& width) );
90     registerParameter( dynamic_cast<Parameter *>(& roundness) );
91     registerParameter( dynamic_cast<Parameter *>(& angle) );
92     //registerParameter( dynamic_cast<Parameter *>(& modulo_pi) );
93     registerParameter( dynamic_cast<Parameter *>(& start_cap) );
94     registerParameter( dynamic_cast<Parameter *>(& growfor) );
95     registerParameter( dynamic_cast<Parameter *>(& end_cap) );
96     registerParameter( dynamic_cast<Parameter *>(& fadefor) );
97     registerParameter( dynamic_cast<Parameter *>(& round_ends) );
98     registerParameter( dynamic_cast<Parameter *>(& capping) );
100     width.param_set_range(0, NR_HUGE);
101     roundness.param_set_range(0.01, 1);
102     angle.param_set_range(-360, 360);
103     growfor.param_set_range(0, NR_HUGE);
104     fadefor.param_set_range(0, NR_HUGE);
106     show_orig_path = true;
109 LPEDynastroke::~LPEDynastroke()
114 Geom::Piecewise<Geom::D2<Geom::SBasis> >
115 LPEDynastroke::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
117     using namespace Geom;
119      std::cout<<"do effect: debut\n";
120     
121      Piecewise<D2<SBasis> > output;
122      Piecewise<D2<SBasis> > m = pwd2_in;
123      Piecewise<D2<SBasis> > v = derivative(m);;
124      Piecewise<D2<SBasis> > n = unitVector(v);
125      n = rot90(n);
126      Piecewise<D2<SBasis> > n1,n2;
128 //      for (unsigned i=0; i<n.size(); i++){
129 //          std::cout<<n[i][X]<<"\n";
130 //      }
131 //      return m + unitVector(v);
133 #if 0
134      Piecewise<SBasis> k = curvature(m);
135      OptInterval mag = bounds_exact(k);
136      //TODO test if mag is non empty...
137      k = (k-mag->min())*width/mag->extent() + (roundness*width);
138      Piecewise<D2<SBasis> > left = m + k*n;
139      Piecewise<D2<SBasis> > right = m - k*n;
140      right = compose(right,Linear(right.cuts.back(),right.cuts.front()));
141      D2<SBasis> line;
142      line[X] = Linear(left.lastValue()[X],right.firstValue()[X]);
143      line[Y] = Linear(left.lastValue()[Y],right.firstValue()[Y]);
144      output = left;
145      output.concat(Piecewise<D2<SBasis> >(line));
146      output.concat(right);
147      line[X] = Linear(right.lastValue()[X],left.firstValue()[X]);
148      line[Y] = Linear(right.lastValue()[Y],left.firstValue()[Y]);
149      output.concat(Piecewise<D2<SBasis> >(line));
150      return output;
151 #else
153      double angle_rad = angle*M_PI/180.;//TODO: revert orientation?...
154      Piecewise<SBasis> w;
155      
156      std::vector<double> corners = find_corners(m);
157     
158      DynastrokeMethod stroke_method = method.get_value();
159      if (roundness==1.) {
160          std::cout<<"round pen.\n";
161          n1 = n*double(width);
162          n2 =-n1;
163      }else{
164          switch(stroke_method) {
165              case DSM_ELLIPTIC_PEN:{
166                  std::cout<<"ellptic pen\n";
167                  //FIXME: roundness=0???
168                  double c = cos(angle_rad), s = sin(angle_rad); 
169                  Matrix rot,slant;
170                  rot = Matrix(c, -s, s,  c, 0, 0 );
171                  slant = Matrix(double(width)*roundness, 0, 0,  double(width), 0, 0 );
172                  Piecewise<D2<SBasis> > nn = unitVector(v * ( rot * slant ) );
173                  slant = Matrix( 0,-roundness, 1, 0, 0, 0 );
174                  rot = Matrix(-s, -c, c, -s, 0, 0 );
175                  nn = nn * (slant * rot );
177                  n1 = nn*double(width);
178                  n2 =-n1;
179                  break;
180              }    
181              case DSM_THICKTHIN_FAST:{
182                  std::cout<<"fast thick thin pen\n";
183                  D2<Piecewise<SBasis> > n_xy = make_cuts_independent(n);
184                  w = n_xy[X]*sin(angle_rad) - n_xy[Y]*cos(angle_rad);
185                  w = w * ((1 - roundness)*width/2.) + ((1 + roundness)*width/2.);
186                  n1 = w*n;
187                  n2 = -n1;
188                  break;
189              }
190              case DSM_THICKTHIN_SLOW:{
191                  std::cout<<"slow thick thin pen\n";
192                  D2<Piecewise<SBasis> > n_xy = make_cuts_independent(n);
193                  w = n_xy[X]*cos(angle_rad)+ n_xy[Y]*sin(angle_rad);
194                  w = w * ((1 - roundness)*width/2.) + ((1 + roundness)*width/2.);
195                  //->Slower and less stable, but more accurate .
196                  //  General  formula:  n1 = w*u with ||u||=1 and u.v = -dw/dt            
197                  Piecewise<SBasis> dw = derivative(w);
198                  Piecewise<SBasis> ncomp = sqrt(dot(v,v)-dw*dw,.1,3);
199                  //FIXME: is force continuity usefull? compatible with corners?
200                  std::cout<<"ici\n";
201                  n1 = -dw*v + ncomp*rot90(v);
202                  n1 = w*force_continuity(unitVector(n1),.1);
203                  n2 = -dw*v - ncomp*rot90(v);
204                  n2 = w*force_continuity(unitVector(n2),.1);
205                  std::cout<<"ici2\n";
206                  break;
207              }
208              default:{
209                  n1 = n*double(width);
210                  n2 = n1*(-.5);
211                  break;
212              }
213          }//case
214      }//if/else
215      
216      //
217      //TODO: insert relevant stitch at each corner!!
218      //
220      Piecewise<D2<SBasis> > left, right;
221      if ( m.segs.front().at0() == m.segs.back().at1()){
222          // if closed:
223          std::cout<<"closed input.\n";
224          left  = m + n1;//+ n;
225          right = m + n2;//- n;
226      } else {
227          //if not closed, shape the ends:
228          //TODO: allow fancy ends...
229          std::cout<<"shaping the ends\n";
230          double grow_length = growfor;// * width;
231          double fade_length = fadefor;// * width;
232          Piecewise<SBasis > s = arcLengthSb(m);
233          double totlength = s.segs.back().at1();
234          
235          //scale factor for a sharp start
236          SBasis join = SBasis(2,Linear(0,1));
237          join[1] = Linear(1,1);
238          Piecewise<SBasis > factor_in = Piecewise<SBasis >(join);
239          factor_in.cuts[1]=grow_length;
240          if (grow_length < totlength){
241              factor_in.concat(Piecewise<SBasis >(Linear(1)));
242              factor_in.cuts[2]=totlength;
243          }
244          std::cout<<"shaping the ends ici\n";
245          //scale factor for a sharp end
246          join[0] = Linear(1,0);
247          join[1] = Linear(1,1);
248          Piecewise<SBasis > factor_out;
249          if (fade_length < totlength){
250              factor_out = Piecewise<SBasis >(Linear(1));
251              factor_out.cuts[1] = totlength-fade_length;
252              factor_out.concat(Piecewise<SBasis >(join));
253              factor_out.cuts[2] = totlength;
254          }else{
255              factor_out = Piecewise<SBasis >(join);
256              factor_out.setDomain(Interval(totlength-fade_length,totlength));
257          }
258          std::cout<<"shaping the ends ici ici\n";
259          
260          Piecewise<SBasis > factor = factor_in*factor_out;
261          n1 = compose(factor,s)*n1;
262          n2 = compose(factor,s)*n2;
263          
264          left  = m + n1;
265          right = m + n2;
266          std::cout<<"shaping the ends ici ici ici\n";
267          
268          if (start_cap.get_value() == DSCT_ROUND){
269              std::cout<<"shaping round start\n";
270              SBasis tau(2,Linear(0));
271              tau[1] = Linear(-1,0);
272              Piecewise<SBasis > hbump;
273              hbump.concat(Piecewise<SBasis >(tau*grow_length));
274              hbump.concat(Piecewise<SBasis >(Linear(0)));
275              hbump.cuts[0]=0;
276              hbump.cuts[1]=fmin(grow_length,totlength*grow_length/(grow_length+fade_length));
277              hbump.cuts[2]=totlength;
278              hbump = compose(hbump,s);
279              
280              left  += - hbump * rot90(n);
281              right += - hbump * rot90(n);
282          }
283          if (end_cap.get_value() == DSCT_ROUND){
284              std::cout<<"shaping round end\n";
285              SBasis tau(2,Linear(0));
286              tau[1] = Linear(0,1);
287              Piecewise<SBasis > hbump;
288              hbump.concat(Piecewise<SBasis >(Linear(0)));
289              hbump.concat(Piecewise<SBasis >(tau*fade_length));
290              hbump.cuts[0]=0;
291              hbump.cuts[1]=fmax(totlength-fade_length, totlength*grow_length/(grow_length+fade_length));
292              hbump.cuts[2]=totlength;
293              hbump = compose(hbump,s);
294              
295              left  += - hbump * rot90(n);
296              right += - hbump * rot90(n);
297          }
298      }
299      
300      left = force_continuity(left);
301      right = force_continuity(right);
302           
303      std::cout<<"gathering result: left";
304      output = left;
305      std::cout<<" + reverse(right)";
306      output.concat(reverse(right));
307      std::cout<<". done\n";
309 //-----------
310         return output;
311 #endif
315 /* ######################## */
317 } //namespace LivePathEffect (setq default-directory "c:/Documents And Settings/jf/Mes Documents/InkscapeSVN")
318 } /* namespace Inkscape */
320 /*
321   Local Variables:
322   mode:c++
323   c-file-style:"stroustrup"
324   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
325   indent-tabs-mode:nil
326   fill-column:99
327   End:
328 */
329 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :