~valavanisalex/ubuntu/oneiric/inkscape/inkscape_0.48.1-2ubuntu4

« back to all changes in this revision

Viewing changes to src/live_effects/lpe-sketch.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Kees Cook, Ted Gould, Kees Cook
  • Date: 2009-06-24 14:00:43 UTC
  • mfrom: (1.1.8 upstream)
  • Revision ID: james.westby@ubuntu.com-20090624140043-07stp20mry48hqup
Tags: 0.47~pre0-0ubuntu1
* New upstream release

[ Ted Gould ]
* debian/control: Adding libgsl0 and removing version specifics on boost

[ Kees Cook ]
* debian/watch: updated to run uupdate and mangle pre-release versions.
* Dropped patches that have been taken upstream:
  - 01_mips
  - 02-poppler-0.8.3
  - 03-chinese-inkscape
  - 05_fix_latex_patch
  - 06_gcc-4.4
  - 07_cdr2svg
  - 08_skip-bad-utf-on-pdf-import
  - 09_gtk-clist
  - 10_belarussian
  - 11_libpng
  - 12_desktop
  - 13_slider
  - 100_svg_import_improvements
  - 102_sp_pattern_painter_free
  - 103_bitmap_type_print

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** @file
 
2
 * @brief LPE sketch effect implementation
 
3
 */
 
4
/* Authors:
 
5
 *   Jean-Francois Barraud <jf.barraud@gmail.com>
 
6
 *   Johan Engelen <j.b.c.engelen@utwente.nl>
 
7
 *
 
8
 * Copyright (C) 2007 Authors
 
9
 *
 
10
 * Released under GNU GPL, read the file 'COPYING' for more information
 
11
 */
 
12
 
 
13
#include "live_effects/lpe-sketch.h"
 
14
 
 
15
// You might need to include other 2geom files. You can add them here:
 
16
#include <2geom/path.h>
 
17
#include <2geom/sbasis.h>
 
18
#include <2geom/sbasis-geometric.h>
 
19
#include <2geom/sbasis-math.h>
 
20
#include <2geom/bezier-to-sbasis.h>
 
21
#include <2geom/sbasis-to-bezier.h>
 
22
#include <2geom/d2.h>
 
23
#include <2geom/sbasis-math.h>
 
24
#include <2geom/piecewise.h>
 
25
#include <2geom/crossing.h>
 
26
#include <2geom/path-intersection.h>
 
27
 
 
28
namespace Inkscape {
 
29
namespace LivePathEffect {
 
30
 
 
31
LPESketch::LPESketch(LivePathEffectObject *lpeobject) :
 
32
    Effect(lpeobject),
 
33
    // initialise your parameters here:
 
34
    //testpointA(_("Test Point A"), _("Test A"), "ptA", &wr, this, Geom::Point(100,100)),
 
35
    nbiter_approxstrokes(_("Strokes"), _("Draw that many approximating strokes"), "nbiter_approxstrokes", &wr, this, 5),
 
36
    strokelength(_("Max stroke length"),
 
37
                 _("Maximum length of approximating strokes"), "strokelength", &wr, this, 100.),
 
38
    strokelength_rdm(_("Stroke length variation"),
 
39
                     _("Random variation of stroke length (relative to maximum length)"), "strokelength_rdm", &wr, this, .3),
 
40
    strokeoverlap(_("Max. overlap"),
 
41
                  _("How much successive strokes should overlap (relative to maximum length)"), "strokeoverlap", &wr, this, .3),
 
42
    strokeoverlap_rdm(_("Overlap variation"),
 
43
                      _("Random variation of overlap (relative to maximum overlap)"), "strokeoverlap_rdm", &wr, this, .3),
 
44
    ends_tolerance(_("Max. end tolerance"),
 
45
                   _("Maximum distance between ends of original and approximating paths (relative to maximum length)"), "ends_tolerance", &wr, this, .1),
 
46
    parallel_offset(_("Average offset"),
 
47
                    _("Average distance each stroke is away from the original path"), "parallel_offset", &wr, this, 5.),
 
48
    tremble_size(_("Max. tremble"),
 
49
                 _("Maximum tremble magnitude"), "tremble_size", &wr, this, 5.),
 
50
    tremble_frequency(_("Tremble frequency"),
 
51
                      _("Average number of tremble periods in a stroke"), "tremble_frequency", &wr, this, 1.)
 
52
#ifdef LPE_SKETCH_USE_CONSTRUCTION_LINES
 
53
    ,nbtangents(_("Construction lines"),
 
54
               _("How many construction lines (tangents) to draw"), "nbtangents", &wr, this, 5),
 
55
    tgtscale(_("Scale"),
 
56
             _("Scale factor relating curvature and length of construction lines (try 5*offset)"), "tgtscale", &wr, this, 10.0),
 
57
    tgtlength(_("Max. length"), _("Maximum length of construction lines"), "tgtlength", &wr, this, 100.0),
 
58
    tgtlength_rdm(_("Length variation"), _("Random variation of the length of construction lines"), "tgtlength_rdm", &wr, this, .3),
 
59
    tgt_places_rdmness(_("Placement randomness"), _("0: evenly distributed construction lines, 1: purely random placement"), "tgt_places_rdmness", &wr, this, 1.)
 
60
#ifdef LPE_SKETCH_USE_CURVATURE
 
61
    ,min_curvature(_("k_min"), _("min curvature"), "k_min", &wr, this, 4.0)
 
62
    ,max_curvature(_("k_max"), _("max curvature"), "k_max", &wr, this, 1000.0)
 
63
#endif
 
64
#endif
 
65
{
 
66
    // register all your parameters here, so Inkscape knows which parameters this effect has:
 
67
    //Add some comment in the UI:  *warning* the precise output of this effect might change in future releases!
 
68
    //convert to path if you want to keep exact output unchanged in future releases...
 
69
    //registerParameter( dynamic_cast<Parameter *>(&testpointA) );
 
70
    registerParameter( dynamic_cast<Parameter *>(&nbiter_approxstrokes) );
 
71
    registerParameter( dynamic_cast<Parameter *>(&strokelength) );
 
72
    registerParameter( dynamic_cast<Parameter *>(&strokelength_rdm) );
 
73
    registerParameter( dynamic_cast<Parameter *>(&strokeoverlap) );
 
74
    registerParameter( dynamic_cast<Parameter *>(&strokeoverlap_rdm) );
 
75
    registerParameter( dynamic_cast<Parameter *>(&ends_tolerance) );
 
76
    registerParameter( dynamic_cast<Parameter *>(&parallel_offset) );
 
77
    registerParameter( dynamic_cast<Parameter *>(&tremble_size) );
 
78
    registerParameter( dynamic_cast<Parameter *>(&tremble_frequency) );
 
79
#ifdef LPE_SKETCH_USE_CONSTRUCTION_LINES
 
80
    registerParameter( dynamic_cast<Parameter *>(&nbtangents) );
 
81
    registerParameter( dynamic_cast<Parameter *>(&tgt_places_rdmness) );
 
82
    registerParameter( dynamic_cast<Parameter *>(&tgtscale) );
 
83
    registerParameter( dynamic_cast<Parameter *>(&tgtlength) );
 
84
    registerParameter( dynamic_cast<Parameter *>(&tgtlength_rdm) );
 
85
#ifdef LPE_SKETCH_USE_CURVATURE
 
86
    registerParameter( dynamic_cast<Parameter *>(&min_curvature) );
 
87
    registerParameter( dynamic_cast<Parameter *>(&max_curvature) );
 
88
#endif
 
89
#endif
 
90
 
 
91
    nbiter_approxstrokes.param_make_integer();
 
92
    nbiter_approxstrokes.param_set_range(0, NR_HUGE);
 
93
    strokelength.param_set_range(1, NR_HUGE);
 
94
    strokelength.param_set_increments(1., 5.);
 
95
    strokelength_rdm.param_set_range(0, 1.);
 
96
    strokeoverlap.param_set_range(0, 1.);
 
97
    strokeoverlap.param_set_increments(0.1, 0.30);
 
98
    ends_tolerance.param_set_range(0., 1.);
 
99
    parallel_offset.param_set_range(0, NR_HUGE);
 
100
    tremble_frequency.param_set_range(0.01, 100.);
 
101
    tremble_frequency.param_set_increments(.5, 1.5);
 
102
    strokeoverlap_rdm.param_set_range(0, 1.);
 
103
 
 
104
#ifdef LPE_SKETCH_USE_CONSTRUCTION_LINES
 
105
    nbtangents.param_make_integer();
 
106
    nbtangents.param_set_range(0, NR_HUGE);
 
107
    tgtscale.param_set_range(0, NR_HUGE);
 
108
    tgtscale.param_set_increments(.1, .5);
 
109
    tgtlength.param_set_range(0, NR_HUGE);
 
110
    tgtlength.param_set_increments(1., 5.);
 
111
    tgtlength_rdm.param_set_range(0, 1.);
 
112
    tgt_places_rdmness.param_set_range(0, 1.);
 
113
    //this is not very smart, but required to avoid having lot of tangents stacked on short components.
 
114
    //Nota: we could specify a density instead of an absolute number, but this would be scale dependant.
 
115
    concatenate_before_pwd2 = true;
 
116
#endif
 
117
}
 
118
 
 
119
LPESketch::~LPESketch()
 
120
{
 
121
 
 
122
}
 
123
 
 
124
/*
 
125
Geom::Piecewise<Geom::D2<Geom::SBasis> >
 
126
addLinearEnds (Geom::Piecewise<Geom::D2<Geom::SBasis> > & m){
 
127
    using namespace Geom;
 
128
    Piecewise<D2<SBasis> > output;
 
129
    Piecewise<D2<SBasis> > start;
 
130
    Piecewise<D2<SBasis> > end;
 
131
    double x,y,vx,vy;
 
132
 
 
133
    x  = m.segs.front()[0].at0();
 
134
    y  = m.segs.front()[1].at0();
 
135
    vx = m.segs.front()[0][1][0]+Tri(m.segs.front()[0][0]);
 
136
    vy = m.segs.front()[1][1][0]+Tri(m.segs.front()[1][0]);
 
137
    start = Piecewise<D2<SBasis> >(D2<SBasis>(Linear (x-vx,x),Linear (y-vy,y)));
 
138
    start.offsetDomain(m.cuts.front()-1.);
 
139
 
 
140
    x  = m.segs.back()[0].at1();
 
141
    y  = m.segs.back()[1].at1();
 
142
    vx = -m.segs.back()[0][1][1]+Tri(m.segs.back()[0][0]);;
 
143
    vy = -m.segs.back()[1][1][1]+Tri(m.segs.back()[1][0]);;
 
144
    end = Piecewise<D2<SBasis> >(D2<SBasis>(Linear (x,x+vx),Linear (y,y+vy)));
 
145
    //end.offsetDomain(m.cuts.back());
 
146
 
 
147
    output = start;
 
148
    output.concat(m);
 
149
    output.concat(end);
 
150
    return output;
 
151
}
 
152
*/
 
153
 
 
154
 
 
155
 
 
156
//This returns a random perturbation. Notice the domain is [s0,s0+first multiple of period>s1]...
 
157
Geom::Piecewise<Geom::D2<Geom::SBasis> >
 
158
LPESketch::computePerturbation (double s0, double s1){
 
159
    using namespace Geom;
 
160
    Piecewise<D2<SBasis> >res;
 
161
 
 
162
    //global offset for this stroke.
 
163
    double offsetX = 2*parallel_offset-parallel_offset.get_value();
 
164
    double offsetY = 2*parallel_offset-parallel_offset.get_value();
 
165
    Point A,dA,B,dB,offset = Point(offsetX,offsetY);
 
166
    //start point A
 
167
    for (unsigned dim=0; dim<2; dim++){
 
168
        A[dim]  = offset[dim] + 2*tremble_size-tremble_size.get_value();
 
169
        dA[dim] = 2*tremble_size-tremble_size.get_value();
 
170
    }
 
171
    //compute howmany deg 3 sbasis to concat according to frequency.
 
172
 
 
173
    unsigned count = unsigned((s1-s0)/strokelength*tremble_frequency)+1; 
 
174
    //unsigned count = unsigned((s1-s0)/tremble_frequency)+1; 
 
175
 
 
176
    for (unsigned i=0; i<count; i++){
 
177
        D2<SBasis> perturb = D2<SBasis>(SBasis(2, Linear()), SBasis(2, Linear()));
 
178
        for (unsigned dim=0; dim<2; dim++){
 
179
            B[dim] = offset[dim] + 2*tremble_size-tremble_size.get_value();
 
180
            perturb[dim][0] = Linear(A[dim],B[dim]);
 
181
            dA[dim] = dA[dim]-B[dim]+A[dim];
 
182
            //avoid dividing by 0. Very short strokes will have ends parallel to the curve...
 
183
            if ( s1-s0 > 1e-2)
 
184
                dB[dim] = -(2*tremble_size-tremble_size.get_value())/(s0-s1)-B[dim]+A[dim];
 
185
            else
 
186
                dB[dim] = -(2*tremble_size-tremble_size.get_value())-B[dim]+A[dim];
 
187
            perturb[dim][1] = Linear(dA[dim],dB[dim]);
 
188
        }
 
189
        dA = B-A-dB;
 
190
        A = B;
 
191
        //dA = B-A-dB;
 
192
        res.concat(Piecewise<D2<SBasis> >(perturb));
 
193
    }
 
194
    res.setDomain(Interval(s0,s0+count*strokelength/tremble_frequency));
 
195
    //res.setDomain(Interval(s0,s0+count*tremble_frequency));
 
196
    return res;
 
197
}
 
198
 
 
199
 
 
200
// Main effect body...
 
201
Geom::Piecewise<Geom::D2<Geom::SBasis> >
 
202
LPESketch::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
 
203
{
 
204
    using namespace Geom;
 
205
    //If the input path is empty, do nothing.
 
206
    //Note: this happens when duplicating a 3d box... dunno why.
 
207
    if (pwd2_in.size()==0) return pwd2_in;
 
208
 
 
209
    Piecewise<D2<SBasis> > output;
 
210
 
 
211
    // some variables for futur use (for construction lines; compute arclength only once...)
 
212
    // notations will be : t = path time, s = distance from start along the path.
 
213
    Piecewise<SBasis> pathlength;
 
214
    double total_length = 0;
 
215
 
 
216
    //TODO: split Construction Lines/Approximated Strokes into two separate effects?
 
217
 
 
218
    //----- Approximated Strokes.
 
219
    std::vector<Piecewise<D2<SBasis> > > pieces_in = split_at_discontinuities (pwd2_in);
 
220
 
 
221
    //work separately on each component.
 
222
    for (unsigned pieceidx = 0; pieceidx < pieces_in.size(); pieceidx++){
 
223
 
 
224
        Piecewise<D2<SBasis> > piece = pieces_in[pieceidx];
 
225
        Piecewise<SBasis> piecelength = arcLengthSb(piece,.1);
 
226
        double piece_total_length = piecelength.segs.back().at1()-piecelength.segs.front().at0();
 
227
        pathlength.concat(piecelength + total_length);
 
228
        total_length += piece_total_length;
 
229
 
 
230
 
 
231
        //TODO: better check this on the Geom::Path.
 
232
        bool closed = piece.segs.front().at0() == piece.segs.back().at1();
 
233
        if (closed){
 
234
            piece.concat(piece);
 
235
            piecelength.concat(piecelength+piece_total_length);
 
236
        }
 
237
 
 
238
        for (unsigned i = 0; i<nbiter_approxstrokes; i++){
 
239
            //Basic steps:
 
240
            //- Choose a rdm seg [s0,s1], find coresponding [t0,t1],
 
241
            //- Pick a rdm perturbation delta(s), collect 'piece(t)+delta(s(t))' over [t0,t1] into output.
 
242
 
 
243
            // pick a point where to start the stroke (s0 = dist from start).
 
244
            double s1=0.,s0 = ends_tolerance*strokelength+0.0001;//the root finder might miss 0.
 
245
            double t1, t0;
 
246
            double s0_initial = s0;
 
247
            bool done = false;// was the end of the component reached?
 
248
 
 
249
            while (!done){
 
250
                // if the start point is already too far... do nothing. (this should not happen!)
 
251
                if (!closed && s1>piece_total_length - ends_tolerance.get_value()*strokelength) break;
 
252
                if ( closed && s0>piece_total_length + s0_initial) break;
 
253
 
 
254
                std::vector<double> times;
 
255
                times = roots(piecelength-s0);
 
256
                t0 = times.at(0);//there should be one and only one solution!!
 
257
 
 
258
                // pick a new end point (s1 = s0 + strokelength).
 
259
                s1 = s0 + strokelength*(1-strokelength_rdm);
 
260
                // don't let it go beyond the end of the orgiginal path.
 
261
                // TODO/FIXME: this might result in short strokes near the end...
 
262
                if (!closed && s1>piece_total_length-ends_tolerance.get_value()*strokelength){
 
263
                    done = true;
 
264
                    //!!the root solver might miss s1==piece_total_length...
 
265
                    if (s1>piece_total_length){s1 = piece_total_length - ends_tolerance*strokelength-0.0001;}
 
266
                }
 
267
                if (closed && s1>piece_total_length + s0_initial){
 
268
                    done = true;
 
269
                    if (closed && s1>2*piece_total_length){
 
270
                        s1 = 2*piece_total_length - strokeoverlap*(1-strokeoverlap_rdm)*strokelength-0.0001;
 
271
                    }
 
272
                }
 
273
                times = roots(piecelength-s1);
 
274
                if (times.size()==0) break;//we should not be there.
 
275
                t1 = times[0];
 
276
 
 
277
                //pick a rdm perturbation, and collect the perturbed piece into output.
 
278
                Piecewise<D2<SBasis> > pwperturb = computePerturbation(s0-0.01,s1+0.01);
 
279
                pwperturb = compose(pwperturb,portion(piecelength,t0,t1));
 
280
 
 
281
                output.concat(portion(piece,t0,t1)+pwperturb);
 
282
 
 
283
                //step points: s0 = s1 - overlap.
 
284
                //TODO: make sure this has to end?
 
285
                s0 = s1 - strokeoverlap*(1-strokeoverlap_rdm)*(s1-s0);
 
286
            }
 
287
        }
 
288
    }
 
289
 
 
290
#ifdef LPE_SKETCH_USE_CONSTRUCTION_LINES
 
291
 
 
292
    //----- Construction lines.
 
293
    //TODO: choose places according to curvature?.
 
294
 
 
295
    //at this point we should have:
 
296
    //pathlength = arcLengthSb(pwd2_in,.1);
 
297
    //total_length = pathlength.segs.back().at1()-pathlength.segs.front().at0();
 
298
    Piecewise<D2<SBasis> > m = pwd2_in;
 
299
    Piecewise<D2<SBasis> > v = derivative(pwd2_in);
 
300
    Piecewise<D2<SBasis> > a = derivative(v);
 
301
 
 
302
#ifdef LPE_SKETCH_USE_CURVATURE
 
303
    //---- curvature experiment...(enable
 
304
    Piecewise<SBasis> k = curvature(pwd2_in);
 
305
    OptInterval k_bnds = bounds_exact(abs(k));
 
306
    double k_min = k_bnds->min() + k_bnds->extent() * min_curvature;
 
307
    double k_max = k_bnds->min() + k_bnds->extent() * max_curvature;
 
308
 
 
309
    Piecewise<SBasis> bump;
 
310
    //SBasis bump_seg = SBasis( 2, Linear(0) );
 
311
    //bump_seg[1] = Linear( 4. );
 
312
    SBasis bump_seg = SBasis( 1, Linear(1) );
 
313
    bump.push_cut( k_bnds->min() - 1 );
 
314
    bump.push( Linear(0), k_min );
 
315
    bump.push(bump_seg,k_max);
 
316
    bump.push( Linear(0), k_bnds->max()+1 );
 
317
        
 
318
    Piecewise<SBasis> repartition = compose( bump, k );
 
319
    repartition = integral(repartition);
 
320
    //-------------------------------
 
321
#endif
 
322
 
 
323
    for (unsigned i=0; i<nbtangents; i++){
 
324
 
 
325
        // pick a point where to draw a tangent (s = dist from start along path).
 
326
#ifdef LPE_SKETCH_USE_CURVATURE
 
327
        double proba = repartition.firstValue()+ (rand()%100)/100.*(repartition.lastValue()-repartition.firstValue());
 
328
        std::vector<double> times;
 
329
        times = roots(repartition - proba);
 
330
        double t = times.at(0);//there should be one and only one solution!
 
331
#else
 
332
        //double s = total_length * ( i + tgtlength_rdm ) / (nbtangents+1.);
 
333
        double reg_place = total_length * ( i + .5) / ( nbtangents );
 
334
        double rdm_place = total_length * tgt_places_rdmness;
 
335
        double s = ( 1.- tgt_places_rdmness.get_value() ) * reg_place  +  rdm_place ;
 
336
        std::vector<double> times;
 
337
        times = roots(pathlength-s);
 
338
        double t = times.at(0);//there should be one and only one solution!
 
339
#endif
 
340
        Point m_t = m(t), v_t = v(t), a_t = a(t);
 
341
        //Compute tgt length according to curvature (not exceeding tgtlength) so that
 
342
        //  dist to origninal curve ~ 4 * (parallel_offset+tremble_size).
 
343
        //TODO: put this 4 as a parameter in the UI...
 
344
        //TODO: what if with v=0?
 
345
        double l = tgtlength*(1-tgtlength_rdm)/v_t.length();
 
346
        double r = pow(v_t.length(),3)/cross(a_t,v_t);
 
347
        r = sqrt((2*fabs(r)-tgtscale)*tgtscale)/v_t.length();
 
348
        l=(r<l)?r:l;
 
349
        //collect the tgt segment into output.
 
350
        D2<SBasis> tgt = D2<SBasis>();
 
351
        for (unsigned dim=0; dim<2; dim++){
 
352
            tgt[dim] = SBasis(Linear(m_t[dim]-v_t[dim]*l, m_t[dim]+v_t[dim]*l));
 
353
        }
 
354
        output.concat(Piecewise<D2<SBasis> >(tgt));
 
355
    }
 
356
#endif
 
357
 
 
358
    return output;
 
359
}
 
360
 
 
361
void
 
362
LPESketch::doBeforeEffect (SPLPEItem */*lpeitem*/)
 
363
{
 
364
    //init random parameters.
 
365
    parallel_offset.resetRandomizer();
 
366
    strokelength_rdm.resetRandomizer();
 
367
    strokeoverlap_rdm.resetRandomizer();
 
368
    ends_tolerance.resetRandomizer();
 
369
    tremble_size.resetRandomizer();
 
370
#ifdef LPE_SKETCH_USE_CONSTRUCTION_LINES
 
371
    tgtlength_rdm.resetRandomizer();
 
372
    tgt_places_rdmness.resetRandomizer();
 
373
#endif
 
374
}
 
375
 
 
376
/* ######################## */
 
377
 
 
378
} //namespace LivePathEffect (setq default-directory "c:/Documents And Settings/jf/Mes Documents/InkscapeSVN")
 
379
} /* namespace Inkscape */
 
380
 
 
381
/*
 
382
  Local Variables:
 
383
  mode:c++
 
384
  c-file-style:"stroustrup"
 
385
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
386
  indent-tabs-mode:nil
 
387
  fill-column:99
 
388
  End:
 
389
*/
 
390
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :