~centralelyon2010/inkscape/imagelinks2

« back to all changes in this revision

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

  • Committer: Ted Gould
  • Date: 2008-11-21 05:24:08 UTC
  • Revision ID: ted@canonical.com-20081121052408-tilucis2pjrrpzxx
MergeĀ fromĀ fe-moved

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define INKSCAPE_LPE_HATCHES_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
 *   JF Barraud.
 
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
 */
 
15
 
 
16
#include "live_effects/lpe-hatches.h"
 
17
 
 
18
#include "sp-item.h"
 
19
#include "sp-path.h"
 
20
#include "svg/svg.h"
 
21
#include "xml/repr.h"
 
22
 
 
23
#include <2geom/path.h>
 
24
#include <2geom/piecewise.h>
 
25
#include <2geom/sbasis.h>
 
26
#include <2geom/sbasis-math.h>
 
27
#include <2geom/sbasis-geometric.h>
 
28
#include <2geom/bezier-to-sbasis.h>
 
29
#include <2geom/sbasis-to-bezier.h>
 
30
#include <2geom/d2.h>
 
31
#include <2geom/matrix.h>
 
32
 
 
33
#include "ui/widget/scalar.h"
 
34
#include "libnr/nr-values.h"
 
35
 
 
36
namespace Inkscape {
 
37
namespace LivePathEffect {
 
38
 
 
39
using namespace Geom;
 
40
 
 
41
//------------------------------------------------
 
42
// Some goodies to navigate through curve's levels.
 
43
//------------------------------------------------
 
44
struct LevelCrossing{
 
45
    Point pt;
 
46
    double t;
 
47
    bool sign;
 
48
    bool used;
 
49
    std::pair<unsigned,unsigned> next_on_curve;
 
50
    std::pair<unsigned,unsigned> prev_on_curve;
 
51
};
 
52
struct LevelCrossingOrder {
 
53
    bool operator()(LevelCrossing a, LevelCrossing b) {
 
54
        return a.pt[Y] < b.pt[Y];
 
55
    }
 
56
};
 
57
struct LevelCrossingInfo{
 
58
    double t;
 
59
    unsigned level;
 
60
    unsigned idx;
 
61
};
 
62
struct LevelCrossingInfoOrder {
 
63
    bool operator()(LevelCrossingInfo a, LevelCrossingInfo b) {
 
64
        return a.t < b.t;
 
65
    }
 
66
};
 
67
 
 
68
typedef std::vector<LevelCrossing> LevelCrossings;
 
69
 
 
70
std::vector<double>
 
71
discontinuities(Piecewise<D2<SBasis> > const &f){
 
72
    std::vector<double> result;
 
73
    if (f.size()==0) return result;
 
74
    result.push_back(f.cuts[0]);
 
75
    Point prev_pt = f.segs[0].at1();
 
76
    //double old_t  = f.cuts[0];
 
77
    for(unsigned i=1; i<f.size(); i++){
 
78
        if ( f.segs[i].at0()!=prev_pt){
 
79
            result.push_back(f.cuts[i]);
 
80
            //old_t = f.cuts[i];
 
81
            //assert(f.segs[i-1].at1()==f.valueAt(old_t));
 
82
        }
 
83
        prev_pt = f.segs[i].at1();
 
84
    }
 
85
    result.push_back(f.cuts.back());
 
86
    //assert(f.segs.back().at1()==f.valueAt(old_t));
 
87
    return result;
 
88
}
 
89
 
 
90
class LevelsCrossings: public std::vector<LevelCrossings>{
 
91
public:
 
92
    LevelsCrossings():std::vector<LevelCrossings>(){};
 
93
    LevelsCrossings(std::vector<std::vector<double> > const &times,
 
94
                    Piecewise<D2<SBasis> > const &f,
 
95
                    Piecewise<SBasis> const &dx){
 
96
        
 
97
        for (unsigned i=0; i<times.size(); i++){
 
98
            LevelCrossings lcs;
 
99
            for (unsigned j=0; j<times[i].size(); j++){
 
100
                LevelCrossing lc;
 
101
                lc.pt = f.valueAt(times[i][j]);
 
102
                lc.t = times[i][j];
 
103
                lc.sign = ( dx.valueAt(times[i][j])>0 );
 
104
                lc.used = false;
 
105
                lcs.push_back(lc);
 
106
            }
 
107
            std::sort(lcs.begin(), lcs.end(), LevelCrossingOrder());
 
108
            push_back(lcs);
 
109
        }
 
110
        //Now create time ordering.
 
111
        std::vector<LevelCrossingInfo>temp;
 
112
        for (unsigned i=0; i<size(); i++){
 
113
            for (unsigned j=0; j<(*this)[i].size(); j++){
 
114
                LevelCrossingInfo elem;
 
115
                elem.t = (*this)[i][j].t;
 
116
                elem.level = i;
 
117
                elem.idx = j;
 
118
                temp.push_back(elem);
 
119
            }
 
120
        }
 
121
        std::sort(temp.begin(),temp.end(),LevelCrossingInfoOrder());
 
122
        std::vector<double> jumps = discontinuities(f);
 
123
        unsigned jump_idx = 0;
 
124
        unsigned first_in_comp = 0;
 
125
        for (unsigned i=0; i<temp.size(); i++){
 
126
            unsigned lvl = temp[i].level, idx = temp[i].idx;
 
127
            if ( i == temp.size()-1 || temp[i+1].t > jumps[jump_idx+1]){
 
128
                std::pair<unsigned,unsigned>next_data(temp[first_in_comp].level,temp[first_in_comp].idx);
 
129
                (*this)[lvl][idx].next_on_curve = next_data;
 
130
                first_in_comp = i+1;
 
131
                jump_idx += 1;
 
132
            }else{
 
133
                std::pair<unsigned,unsigned> next_data(temp[i+1].level,temp[i+1].idx);
 
134
                (*this)[lvl][idx].next_on_curve = next_data;
 
135
            }
 
136
        }
 
137
 
 
138
        for (unsigned i=0; i<size(); i++){
 
139
            for (unsigned j=0; j<(*this)[i].size(); j++){
 
140
                std::pair<unsigned,unsigned> next = (*this)[i][j].next_on_curve;
 
141
                (*this)[next.first][next.second].prev_on_curve = std::pair<unsigned,unsigned>(i,j);
 
142
            }
 
143
        }
 
144
#if 0
 
145
        std::cout<<"\n";
 
146
        for (unsigned i=0; i<temp.size()-1; i++){
 
147
            std::cout<<temp[i].level<<","<<temp[i].idx<<" -> ";
 
148
        }
 
149
        std::cout<<"\n";
 
150
        for (unsigned i=0; i<size(); i++){
 
151
            for (unsigned j=0; j<(*this)[i].size(); j++){
 
152
                std::cout<<"level:"<<i<<", idx:"<<j<<" -  ";
 
153
                std::cout<<"next:"<<(*this)[i][j].next_on_curve.first<<",";
 
154
                std::cout<<(*this)[i][j].next_on_curve.second<<" - ";
 
155
                std::cout<<"prev:"<<(*this)[i][j].prev_on_curve.first<<",";
 
156
                std::cout<<(*this)[i][j].prev_on_curve.second<<"\n";
 
157
            }
 
158
        }
 
159
#endif
 
160
    }
 
161
 
 
162
    void findFirstUnused(unsigned &level, unsigned &idx){
 
163
        level = size();
 
164
        idx = 0;
 
165
        for (unsigned i=0; i<size(); i++){
 
166
            for (unsigned j=0; j<(*this)[i].size(); j++){
 
167
                if (!(*this)[i][j].used){
 
168
                    level = i;
 
169
                    idx = j;
 
170
                    return;
 
171
                }
 
172
            }
 
173
        }
 
174
    }
 
175
    //set indexes to point to the next point in the "snake walk"
 
176
    //follow_level's meaning: 
 
177
    //  0=yes upward
 
178
    //  1=no, last move was upward,
 
179
    //  2=yes downward
 
180
    //  3=no, last move was downward.
 
181
    void step(unsigned &level, unsigned &idx, int &direction){
 
182
        if ( direction % 2 == 0 ){
 
183
            if (direction == 0) {
 
184
                if ( idx >= (*this)[level].size()-1 || (*this)[level][idx+1].used ) {
 
185
                    level = size();
 
186
                    return;
 
187
                }
 
188
                idx += 1;
 
189
            }else{
 
190
                if ( idx <= 0  || (*this)[level][idx-1].used ) {
 
191
                    level = size();
 
192
                    return;
 
193
                }
 
194
                idx -= 1;
 
195
            }
 
196
            direction += 1;
 
197
            return;
 
198
        }
 
199
        double t = (*this)[level][idx].t;
 
200
        double sign = ((*this)[level][idx].sign ? 1 : -1);
 
201
        double next_t = t;
 
202
        //level += 1;
 
203
        direction = (direction + 1)%4;
 
204
        if (level == size()){
 
205
            return;
 
206
        }
 
207
 
 
208
        std::pair<unsigned,unsigned> next;
 
209
        if ( sign > 0 ){
 
210
            next = (*this)[level][idx].next_on_curve;
 
211
        }else{
 
212
            next = (*this)[level][idx].prev_on_curve;
 
213
        }
 
214
 
 
215
        if ( level+1 != next.first || (*this)[next.first][next.second].used ) {
 
216
            level = size();
 
217
            return;
 
218
        }
 
219
        level = next.first;
 
220
        idx = next.second;
 
221
 
 
222
/*********************
 
223
        //look for next time on the same level
 
224
        for (unsigned j=0; j<(*this)[level].size(); j++){
 
225
            double tj = (*this)[level][j].t;
 
226
            if ( sign*(tj-t) > 0 ){
 
227
                if( next_t == t ||  sign*(tj-next_t)<0 ){
 
228
                    next_t = tj;
 
229
                    idx = j;
 
230
                }
 
231
            }
 
232
        }
 
233
        if ( next_t == t ){//not found? look at max/min time in this component, as time is "periodic".
 
234
            for (unsigned j=0; j<(*this)[level].size(); j++){
 
235
                double tj = (*this)[level][j].t;
 
236
                if ( -sign*(tj-next_t) > 0 ){
 
237
                    next_t = tj;
 
238
                    idx = j;
 
239
                }
 
240
            }
 
241
        }
 
242
        if ( next_t == t ){//still not found? houch! this should not happen.
 
243
            level = size();
 
244
            return;
 
245
        }
 
246
        if ( (*this)[level][idx].used ) {
 
247
            level = size();
 
248
            return;
 
249
        }
 
250
*************************/
 
251
        return;
 
252
    }
 
253
};
 
254
 
 
255
//-------------------------------------------------------
 
256
// Bend a path...
 
257
//-------------------------------------------------------
 
258
 
 
259
Piecewise<D2<SBasis> > bend(Piecewise<D2<SBasis> > const &f, Piecewise<SBasis> bending){
 
260
    D2<Piecewise<SBasis> > ff = make_cuts_independent(f);
 
261
    ff[X] += compose(bending, ff[Y]);
 
262
    return sectionize(ff);
 
263
}
 
264
 
 
265
//--------------------------------------------------------
 
266
// The Hatches lpe.
 
267
//--------------------------------------------------------
 
268
LPEHatches::LPEHatches(LivePathEffectObject *lpeobject) :
 
269
    Effect(lpeobject),
 
270
    dist_rdm(_("Dist randomness"), _("Variation of dist between hatches, in %."), "dist_rdm", &wr, this, 75),
 
271
    growth(_("Growth"), _("Growth of distance between hatches."), "growth", &wr, this, 0.),
 
272
    scale_tf(_("Start smothness (front side)"), _("MISSING DESCRIPTION"), "scale_tf", &wr, this, 1.),
 
273
    scale_tb(_("Start smothness (back side)"), _("MISSING DESCRIPTION"), "scale_tb", &wr, this, 1.),
 
274
    scale_bf(_("End smothness (front side)"), _("MISSING DESCRIPTION"), "scale_bf", &wr, this, 1.),
 
275
    scale_bb(_("End smothness (back side)"), _("MISSING DESCRIPTION"), "scale_bb", &wr, this, 1.),
 
276
    top_edge_variation(_("Start edge variance"), _("The amount of random jitter to move the hatches start"), "top_edge_variation", &wr, this, 0),
 
277
    bot_edge_variation(_("End edge variance"), _("The amount of random jitter to move the hatches end"), "bot_edge_variation", &wr, this, 0),
 
278
    top_tgt_variation(_("Start tangential variance"), _("The amount of random jitter to move the hatches start along the boundary"), "top_tgt_variation", &wr, this, 0),
 
279
    bot_tgt_variation(_("End tangential variance"), _("The amount of random jitter to move the hatches end along the boundary"), "bot_tgt_variation", &wr, this, 0),
 
280
    top_smth_variation(_("Start smoothness variance"), _("Randomness of the smoothness of the U turn at hatches start"), "top_smth_variation", &wr, this, 0),
 
281
    bot_smth_variation(_("End spacing variance"), _("Randomness of the smoothness of the U turn at hatches end"), "bot_smth_variation", &wr, this, 0),
 
282
    fat_output(_("Generate thick/thin path"), _("Simulate a stroke of varrying width"), "fat_output", &wr, this, true),
 
283
    do_bend(_("Bend hatches"), _("Add a global bend to the hatches (slower)"), "do_bend", &wr, this, true),
 
284
    stroke_width_top(_("Stroke width (start side)"), _("Width at hatches 'start'"), "stroke_width_top", &wr, this, 1.),
 
285
    stroke_width_bot(_("Stroke width (end side)"), _("Width at hatches 'end'"), "stroke_width_bot", &wr, this, 1.),
 
286
    front_thickness(_("Front thickness (%)"), _("MISSING DESCRIPTION"), "front_thickness", &wr, this, 1.),
 
287
    back_thickness(_("Back thickness (%)"), _("MISSING DESCRIPTION"), "back_thickness", &wr, this, .25),
 
288
    bender(_("Global bending"), _("Relative position to ref point defines global bending direction and amount"), "bender", &wr, this, NULL, Geom::Point(-5,0)),
 
289
    direction(_("Hatches width and dir"), _("Defines hatches frequency and direction"), "direction", &wr, this, Geom::Point(50,0))
 
290
{
 
291
    registerParameter( dynamic_cast<Parameter *>(&direction) );
 
292
    registerParameter( dynamic_cast<Parameter *>(&do_bend) );
 
293
    registerParameter( dynamic_cast<Parameter *>(&bender) );
 
294
    registerParameter( dynamic_cast<Parameter *>(&dist_rdm) );
 
295
    registerParameter( dynamic_cast<Parameter *>(&growth) );
 
296
    registerParameter( dynamic_cast<Parameter *>(&top_edge_variation) );
 
297
    registerParameter( dynamic_cast<Parameter *>(&bot_edge_variation) );
 
298
    registerParameter( dynamic_cast<Parameter *>(&top_tgt_variation) );
 
299
    registerParameter( dynamic_cast<Parameter *>(&bot_tgt_variation) );
 
300
    registerParameter( dynamic_cast<Parameter *>(&scale_tf) );
 
301
    registerParameter( dynamic_cast<Parameter *>(&scale_tb) );
 
302
    registerParameter( dynamic_cast<Parameter *>(&scale_bf) );
 
303
    registerParameter( dynamic_cast<Parameter *>(&scale_bb) );
 
304
    registerParameter( dynamic_cast<Parameter *>(&top_smth_variation) );
 
305
    registerParameter( dynamic_cast<Parameter *>(&bot_smth_variation) );
 
306
    registerParameter( dynamic_cast<Parameter *>(&fat_output) );
 
307
    registerParameter( dynamic_cast<Parameter *>(&stroke_width_top) );
 
308
    registerParameter( dynamic_cast<Parameter *>(&stroke_width_bot) );
 
309
    registerParameter( dynamic_cast<Parameter *>(&front_thickness) );
 
310
    registerParameter( dynamic_cast<Parameter *>(&back_thickness) );
 
311
 
 
312
    //hatch_dist.param_set_range(0.1, NR_HUGE);
 
313
    growth.param_set_range(-0.95, NR_HUGE);
 
314
    dist_rdm.param_set_range(0, 99.);
 
315
    stroke_width_top.param_set_range(0,  NR_HUGE);
 
316
    stroke_width_bot.param_set_range(0,  NR_HUGE);
 
317
    front_thickness.param_set_range(0, NR_HUGE);
 
318
    back_thickness.param_set_range(0, NR_HUGE);
 
319
 
 
320
    concatenate_before_pwd2 = true;
 
321
    show_orig_path = true;
 
322
}
 
323
 
 
324
LPEHatches::~LPEHatches()
 
325
{
 
326
 
 
327
}
 
328
 
 
329
Geom::Piecewise<Geom::D2<Geom::SBasis> > 
 
330
LPEHatches::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in){
 
331
 
 
332
    Piecewise<D2<SBasis> > result;
 
333
    
 
334
    Piecewise<D2<SBasis> > transformed_pwd2_in = pwd2_in;
 
335
    Piecewise<SBasis> tilter;//used to bend the hatches
 
336
    Matrix bend_mat;//used to bend the hatches
 
337
 
 
338
    if (do_bend.get_value()){
 
339
        Point bend_dir = -rot90(unit_vector(direction.getOrigin() - bender));
 
340
        double bend_amount = L2(direction.getOrigin() - bender);
 
341
        bend_mat = Matrix(-bend_dir[Y], bend_dir[X], bend_dir[X], bend_dir[Y],0,0);
 
342
        transformed_pwd2_in = pwd2_in * bend_mat;
 
343
        tilter = Piecewise<SBasis>(shift(Linear(bend_amount),1));
 
344
        OptRect bbox = bounds_exact( transformed_pwd2_in );
 
345
        if (not(bbox)) return pwd2_in;
 
346
        tilter.setDomain((*bbox)[Y]);
 
347
        transformed_pwd2_in = bend(transformed_pwd2_in, tilter);
 
348
        transformed_pwd2_in = transformed_pwd2_in * bend_mat.inverse();
 
349
    }
 
350
    hatch_dist = Geom::L2(direction.getVector())/5;
 
351
    Point hatches_dir = rot90(unit_vector(direction.getVector()));
 
352
    Matrix mat(-hatches_dir[Y], hatches_dir[X], hatches_dir[X], hatches_dir[Y],0,0);
 
353
    transformed_pwd2_in = transformed_pwd2_in * mat;
 
354
        
 
355
    std::vector<std::vector<Point> > snakePoints;
 
356
    snakePoints = linearSnake(transformed_pwd2_in);
 
357
    if ( snakePoints.size() > 0 ){
 
358
        Piecewise<D2<SBasis> >smthSnake = smoothSnake(snakePoints); 
 
359
        smthSnake = smthSnake*mat.inverse();
 
360
        if (do_bend.get_value()){
 
361
            smthSnake = smthSnake*bend_mat;
 
362
            smthSnake = bend(smthSnake, -tilter);
 
363
            smthSnake = smthSnake*bend_mat.inverse();
 
364
        }
 
365
        return (smthSnake);
 
366
    }
 
367
    return pwd2_in;
 
368
}
 
369
 
 
370
//------------------------------------------------
 
371
// Generate the levels with random, growth...
 
372
//------------------------------------------------
 
373
std::vector<double>
 
374
LPEHatches::generateLevels(Interval const &domain){
 
375
    std::vector<double> result;
 
376
    double x = domain.min() + double(hatch_dist)/2.;
 
377
    double step = double(hatch_dist);
 
378
    double scale = 1+(hatch_dist*growth/domain.extent());
 
379
    while (x < domain.max()){
 
380
        result.push_back(x);
 
381
        double rdm = 1;
 
382
        if (dist_rdm.get_value() != 0) 
 
383
            rdm = 1.+ double((2*dist_rdm - dist_rdm.get_value()))/100.;
 
384
        x+= step*rdm;
 
385
        step*=scale;//(1.+double(growth));
 
386
    }
 
387
    return result;
 
388
}
 
389
 
 
390
 
 
391
//-------------------------------------------------------
 
392
// Walk through the intersections to create linear hatches
 
393
//-------------------------------------------------------
 
394
std::vector<std::vector<Point> > 
 
395
LPEHatches::linearSnake(Piecewise<D2<SBasis> > const &f){
 
396
 
 
397
    std::vector<std::vector<Point> > result;
 
398
 
 
399
    Piecewise<SBasis> x = make_cuts_independent(f)[X];
 
400
    //Rque: derivative is computed twice in the 2 lines below!!
 
401
    Piecewise<SBasis> dx = derivative(x);
 
402
    OptInterval range = bounds_exact(x);
 
403
 
 
404
    if (not range) return result;
 
405
    std::vector<double> levels = generateLevels(*range);
 
406
    std::vector<std::vector<double> > times;
 
407
    times = multi_roots(x,levels);
 
408
 
 
409
//TODO: fix multi_roots!!!*****************************************
 
410
//remove doubles :-(
 
411
    std::vector<std::vector<double> > cleaned_times(levels.size(),std::vector<double>());
 
412
    for (unsigned i=0; i<times.size(); i++){
 
413
        if ( times[i].size()>0 ){
 
414
            double last_t = times[i][0]-1;//ugly hack!!
 
415
            for (unsigned j=0; j<times[i].size(); j++){
 
416
                if (times[i][j]-last_t >0.000001){
 
417
                    last_t = times[i][j];
 
418
                    cleaned_times[i].push_back(last_t);
 
419
                }
 
420
            }
 
421
        }
 
422
    }
 
423
    times = cleaned_times;
 
424
//     for (unsigned i=0; i<times.size(); i++){
 
425
//         std::cout << "roots on level "<<i<<": ";
 
426
//         for (unsigned j=0; j<times[i].size(); j++){
 
427
//             std::cout << times[i][j] <<" ";
 
428
//         }
 
429
//         std::cout <<"\n";
 
430
//     }
 
431
//*******************************************************************
 
432
    LevelsCrossings lscs(times,f,dx);
 
433
    unsigned i,j;
 
434
    lscs.findFirstUnused(i,j);
 
435
    std::vector<Point> result_component;
 
436
    while ( i < lscs.size() ){ 
 
437
        int dir = 0;
 
438
        while ( i < lscs.size() ){
 
439
            result_component.push_back(lscs[i][j].pt);
 
440
            lscs[i][j].used = true;
 
441
            lscs.step(i,j, dir);
 
442
        }
 
443
        result.push_back(result_component);
 
444
        result_component = std::vector<Point>();
 
445
        lscs.findFirstUnused(i,j);
 
446
    }
 
447
    return result;
 
448
}
 
449
 
 
450
//-------------------------------------------------------
 
451
// Smooth the linear hatches according to params...
 
452
//-------------------------------------------------------
 
453
Piecewise<D2<SBasis> > 
 
454
LPEHatches::smoothSnake(std::vector<std::vector<Point> > const &linearSnake){
 
455
 
 
456
    Piecewise<D2<SBasis> > result;
 
457
    for (unsigned comp=0; comp<linearSnake.size(); comp++){
 
458
        if (linearSnake[comp].size()>=2){
 
459
            bool is_top = true;//Inversion here; due to downward y? 
 
460
            Point last_pt = linearSnake[comp][0];
 
461
            Point last_top = linearSnake[comp][0];
 
462
            Point last_bot = linearSnake[comp][0];
 
463
            Point last_hdle = linearSnake[comp][0];
 
464
            Point last_top_hdle = linearSnake[comp][0];
 
465
            Point last_bot_hdle = linearSnake[comp][0];
 
466
            Geom::Path res_comp(last_pt);
 
467
            Geom::Path res_comp_top(last_pt);
 
468
            Geom::Path res_comp_bot(last_pt);
 
469
            unsigned i=1;
 
470
            while( i+1<linearSnake[comp].size() ){
 
471
                Point pt0 = linearSnake[comp][i];
 
472
                Point pt1 = linearSnake[comp][i+1];
 
473
                Point new_pt = (pt0+pt1)/2;
 
474
                double scale_in = (is_top ? scale_tf : scale_bf );
 
475
                double scale_out = (is_top ? scale_tb : scale_bb );
 
476
                if (is_top){
 
477
                    if (top_edge_variation.get_value() != 0) 
 
478
                        new_pt[Y] += double(top_edge_variation)-top_edge_variation.get_value()/2.;
 
479
                    if (top_tgt_variation.get_value() != 0) 
 
480
                        new_pt[X] += double(top_tgt_variation)-top_tgt_variation.get_value()/2.;
 
481
                    if (top_smth_variation.get_value() != 0) {
 
482
                        scale_in*=(100.-double(top_smth_variation))/100.;
 
483
                        scale_out*=(100.-double(top_smth_variation))/100.;
 
484
                    }
 
485
                }else{
 
486
                    if (bot_edge_variation.get_value() != 0) 
 
487
                        new_pt[Y] += double(bot_edge_variation)-bot_edge_variation.get_value()/2.;
 
488
                    if (bot_tgt_variation.get_value() != 0) 
 
489
                        new_pt[X] += double(bot_tgt_variation)-bot_tgt_variation.get_value()/2.;
 
490
                    if (bot_smth_variation.get_value() != 0) {
 
491
                        scale_in*=(100.-double(bot_smth_variation))/100.;
 
492
                        scale_out*=(100.-double(bot_smth_variation))/100.;
 
493
                    }
 
494
                }
 
495
                Point new_hdle_in  = new_pt + (pt0-pt1) * (scale_in /2.);
 
496
                Point new_hdle_out = new_pt - (pt0-pt1) * (scale_out/2.);
 
497
                
 
498
                if ( fat_output.get_value() ){
 
499
                    double scaled_width = double((is_top ? stroke_width_top : stroke_width_bot))/(pt1[X]-pt0[X]);
 
500
                    Point hdle_offset = (pt1-pt0)*scaled_width;
 
501
                    Point inside = new_pt;
 
502
                    Point inside_hdle_in;
 
503
                    Point inside_hdle_out;
 
504
                    inside[Y]+= double((is_top ? -stroke_width_top : stroke_width_bot));
 
505
                    inside_hdle_in  = inside + (new_hdle_in -new_pt) + hdle_offset * double((is_top ? front_thickness : back_thickness));
 
506
                    inside_hdle_out = inside + (new_hdle_out-new_pt) - hdle_offset * double((is_top ? back_thickness : front_thickness));
 
507
                    //TODO: find a good way to handle limit cases (small smthness, large stroke).
 
508
                    //if (inside_hdle_in[X]  > inside[X]) inside_hdle_in = inside;
 
509
                    //if (inside_hdle_out[X] < inside[X]) inside_hdle_out = inside;
 
510
                    
 
511
                    if (is_top){
 
512
                        res_comp_top.appendNew<CubicBezier>(last_top_hdle,new_hdle_in,new_pt);
 
513
                        res_comp_bot.appendNew<CubicBezier>(last_bot_hdle,inside_hdle_in,inside);
 
514
                        last_top_hdle = new_hdle_out;
 
515
                        last_bot_hdle = inside_hdle_out;
 
516
                    }else{
 
517
                        res_comp_top.appendNew<CubicBezier>(last_top_hdle,inside_hdle_in,inside);
 
518
                        res_comp_bot.appendNew<CubicBezier>(last_bot_hdle,new_hdle_in,new_pt);
 
519
                        last_top_hdle = inside_hdle_out;
 
520
                        last_bot_hdle = new_hdle_out;
 
521
                    }
 
522
                }else{
 
523
                    res_comp.appendNew<CubicBezier>(last_hdle,new_hdle_in,new_pt);
 
524
                }
 
525
            
 
526
                last_hdle = new_hdle_out;
 
527
                i+=2;
 
528
                is_top = !is_top;
 
529
            }
 
530
            if ( i<linearSnake[comp].size() )
 
531
                if ( fat_output.get_value() ){
 
532
                    res_comp_top.appendNew<CubicBezier>(last_top_hdle,linearSnake[comp][i],linearSnake[comp][i]);
 
533
                    res_comp_bot.appendNew<CubicBezier>(last_bot_hdle,linearSnake[comp][i],linearSnake[comp][i]);
 
534
                }else{
 
535
                    res_comp.appendNew<CubicBezier>(last_hdle,linearSnake[comp][i],linearSnake[comp][i]);
 
536
                }
 
537
            if ( fat_output.get_value() ){
 
538
                res_comp = res_comp_bot;
 
539
                res_comp.append(res_comp_top.reverse(),Geom::Path::STITCH_DISCONTINUOUS);
 
540
            }    
 
541
            result.concat(res_comp.toPwSb());
 
542
        }
 
543
    }
 
544
    return result;
 
545
}
 
546
 
 
547
void
 
548
LPEHatches::doBeforeEffect (SPLPEItem */*lpeitem*/)
 
549
{
 
550
    using namespace Geom;
 
551
    top_edge_variation.resetRandomizer();
 
552
    bot_edge_variation.resetRandomizer();
 
553
    top_tgt_variation.resetRandomizer();
 
554
    bot_tgt_variation.resetRandomizer();
 
555
    top_smth_variation.resetRandomizer();
 
556
    bot_smth_variation.resetRandomizer();
 
557
    dist_rdm.resetRandomizer();
 
558
 
 
559
    //original_bbox(lpeitem);
 
560
}
 
561
 
 
562
 
 
563
void
 
564
LPEHatches::resetDefaults(SPItem * item)
 
565
{
 
566
    Geom::OptRect bbox = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
 
567
    Geom::Point origin(0.,0.);
 
568
    Geom::Point vector(50.,0.);
 
569
    if (bbox) {
 
570
        origin = bbox->midpoint();
 
571
        vector = Geom::Point((*bbox)[X].extent()/4, 0.);
 
572
        top_edge_variation.param_set_value( (*bbox)[Y].extent()/10, 0 );
 
573
        bot_edge_variation.param_set_value( (*bbox)[Y].extent()/10, 0 );
 
574
    }
 
575
    direction.set_and_write_new_values(origin, vector);
 
576
    bender.param_set_and_write_new_value( origin + Geom::Point(5,0) );
 
577
    hatch_dist = Geom::L2(vector)/5;
 
578
}
 
579
 
 
580
 
 
581
} //namespace LivePathEffect
 
582
} /* namespace Inkscape */
 
583
 
 
584
/*
 
585
  Local Variables:
 
586
  mode:c++
 
587
  c-file-style:"stroustrup"
 
588
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
589
  indent-tabs-mode:nil
 
590
  fill-column:99
 
591
  End:
 
592
*/
 
593
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :