1 #define INKSCAPE_LPE_POWERSTROKE_CPP
2 /** \file
3 * @brief PowerStroke LPE implementation. Creates curves with modifiable stroke width.
4 */
5 /* Authors:
6 * Johan Engelen <j.b.c.engelen@utwente.nl>
7 *
8 * Copyright (C) 2010 Authors
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 */
13 #include "live_effects/lpe-powerstroke.h"
15 #include "sp-shape.h"
16 #include "display/curve.h"
18 #include <2geom/path.h>
19 #include <2geom/piecewise.h>
20 #include <2geom/sbasis-geometric.h>
21 #include <2geom/transforms.h>
24 /// @TODO Move this to 2geom
25 namespace Geom {
26 namespace Interpolate {
28 class Interpolator {
29 public:
30 Interpolator() {};
31 virtual ~Interpolator() {};
33 // virtual Piecewise<D2<SBasis> > interpolateToPwD2Sb(std::vector<Point> points) = 0;
34 virtual Path interpolateToPath(std::vector<Point> points) = 0;
36 private:
37 Interpolator(const Interpolator&);
38 Interpolator& operator=(const Interpolator&);
39 };
41 class Linear : public Interpolator {
42 public:
43 Linear() {};
44 virtual ~Linear() {};
46 virtual Path interpolateToPath(std::vector<Point> points) {
47 Path strokepath;
48 strokepath.start( points.at(0) );
49 for (unsigned int i = 1 ; i < points.size(); ++i) {
50 strokepath.appendNew<Geom::LineSegment>(points.at(i));
51 }
52 return strokepath;
53 };
55 private:
56 Linear(const Linear&);
57 Linear& operator=(const Linear&);
58 };
60 } //namespace Interpolate
61 } //namespace Geom
63 namespace Inkscape {
64 namespace LivePathEffect {
66 LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) :
67 Effect(lpeobject),
68 offset_points(_("Offset points"), _("Offset points"), "offset_points", &wr, this),
69 sort_points(_("Sort points"), _("Sort offset points according to their time value along the curve."), "sort_points", &wr, this, true)
70 {
71 show_orig_path = true;
73 registerParameter( dynamic_cast<Parameter *>(&offset_points) );
74 registerParameter( dynamic_cast<Parameter *>(&sort_points) );
75 }
77 LPEPowerStroke::~LPEPowerStroke()
78 {
80 }
83 void
84 LPEPowerStroke::doOnApply(SPLPEItem *lpeitem)
85 {
86 std::vector<Geom::Point> points;
87 points.push_back( *(SP_SHAPE(lpeitem)->curve->first_point()) );
88 Geom::Path const *path = SP_SHAPE(lpeitem)->curve->first_path();
89 points.push_back( path->pointAt(path->size()/2) );
90 points.push_back( *(SP_SHAPE(lpeitem)->curve->last_point()) );
91 offset_points.param_set_and_write_new_value(points);
92 }
94 static bool compare_offsets (Geom::Point first, Geom::Point second)
95 {
96 return first[Geom::X] < second[Geom::X];
97 }
100 Geom::Piecewise<Geom::D2<Geom::SBasis> >
101 LPEPowerStroke::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
102 {
103 using namespace Geom;
105 // perhaps use std::list instead of std::vector?
106 std::vector<Geom::Point> ts(offset_points.data().size() + 2);
107 // first and last point coincide with input path (for now at least)
108 ts.front() = Point(pwd2_in.domain().min(),0);
109 ts.back() = Point(pwd2_in.domain().max(),0);
110 for (unsigned int i = 0; i < offset_points.data().size(); ++i) {
111 double t = nearest_point(offset_points.data().at(i), pwd2_in);
112 double offset = L2(pwd2_in.valueAt(t) - offset_points.data().at(i));
113 ts.at(i+1) = Geom::Point(t, offset);
114 }
116 if (sort_points) {
117 sort(ts.begin(), ts.end(), compare_offsets);
118 }
120 // create stroke path where points (x,y) = (t, offset)
121 Geom::Interpolate::Linear interpolator;
122 Path strokepath = interpolator.interpolateToPath(ts);
123 Path mirroredpath = strokepath.reverse() * Geom::Scale(1,-1);
124 strokepath.append(mirroredpath, Geom::Path::STITCH_DISCONTINUOUS);
125 strokepath.close();
127 D2<Piecewise<SBasis> > patternd2 = make_cuts_independent(strokepath.toPwSb());
128 Piecewise<SBasis> x = Piecewise<SBasis>(patternd2[0]);
129 Piecewise<SBasis> y = Piecewise<SBasis>(patternd2[1]);
131 Piecewise<D2<SBasis> > der = unitVector(derivative(pwd2_in));
132 Piecewise<D2<SBasis> > n = rot90(der);
134 // output = pwd2_in + n * offset;
135 // append_half_circle(output, pwd2_in.lastValue(), n.lastValue() * offset);
136 // output.continuousConcat(reverse(pwd2_in - n * offset));
137 // append_half_circle(output, pwd2_in.firstValue(), -n.firstValue() * offset);
139 Piecewise<D2<SBasis> > output = compose(pwd2_in,x) + y*compose(n,x);
140 return output;
141 }
143 /* ######################## */
145 } //namespace LivePathEffect
146 } /* namespace Inkscape */
148 /*
149 Local Variables:
150 mode:c++
151 c-file-style:"stroustrup"
152 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
153 indent-tabs-mode:nil
154 fill-column:99
155 End:
156 */
157 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :