1 #define INKSCAPE_LPE_EXTRUDE_CPP
2 /** \file
3 * @brief LPE effect for extruding paths (making them "3D").
4 *
5 */
6 /* Authors:
7 * Johan Engelen <j.b.c.engelen@utwente.nl>
8 *
9 * Copyright (C) 2009 Authors
10 *
11 * Released under GNU GPL, read the file 'COPYING' for more information
12 */
14 #include "live_effects/lpe-extrude.h"
16 #include <2geom/path.h>
17 #include <2geom/piecewise.h>
18 #include <2geom/transforms.h>
19 #include <algorithm>
21 namespace Inkscape {
22 namespace LivePathEffect {
24 LPEExtrude::LPEExtrude(LivePathEffectObject *lpeobject) :
25 Effect(lpeobject),
26 extrude_vector(_("Direction"), _("Defines the direction and magnitude of the extrusion"), "extrude_vector", &wr, this, Geom::Point(-10,10))
27 {
28 show_orig_path = true;
29 concatenate_before_pwd2 = false;
31 registerParameter( dynamic_cast<Parameter *>(&extrude_vector) );
32 }
34 LPEExtrude::~LPEExtrude()
35 {
37 }
39 static bool are_colinear(Geom::Point a, Geom::Point b) {
40 return Geom::are_near(cross(a,b), 0., 0.5);
41 }
43 // find cusps, this should be factored out later.
44 static std::vector<double> find_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in ) {
45 using namespace Geom;
46 Piecewise<D2<SBasis> > deriv = derivative(pwd2_in);
47 std::vector<double> cusps;
48 // cusps are spots where the derivative jumps.
49 for (unsigned i = 1 ; i < deriv.size() ; ++i) {
50 if ( ! are_colinear(deriv[i-1].at1(), deriv[i].at0()) ) {
51 // there is a jump in the derivative, so add it to the cusps list
52 cusps.push_back(deriv.cuts[i]);
53 }
54 }
55 return cusps;
56 }
58 Geom::Piecewise<Geom::D2<Geom::SBasis> >
59 LPEExtrude::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
60 {
61 using namespace Geom;
63 // generate connecting lines (the 'sides' of the extrusion)
64 Path path(Point(0.,0.));
65 path.appendNew<Geom::LineSegment>( extrude_vector.getVector() );
66 Piecewise<D2<SBasis> > connector = path.toPwSb();
68 switch( 1 ) {
69 case 0: {
70 /* This one results in the following subpaths: the original, a displaced copy, and connector lines between the two
71 */
73 Piecewise<D2<SBasis> > pwd2_out = pwd2_in;
74 // generate extrusion bottom: (just a copy of original path, displaced a bit)
75 pwd2_out.concat( pwd2_in + extrude_vector.getVector() );
77 // connecting lines should be put at start and end of path if it is not closed
78 // it is not possible to check whether a piecewise<T> path is closed,
79 // so we check whether start and end are close
80 if ( ! are_near(pwd2_in.firstValue(), pwd2_in.lastValue()) ) {
81 pwd2_out.concat( connector + pwd2_in.firstValue() );
82 pwd2_out.concat( connector + pwd2_in.lastValue() );
83 }
84 // connecting lines should be put at cusps
85 Piecewise<D2<SBasis> > deriv = derivative(pwd2_in);
86 std::vector<double> cusps; // = roots(deriv);
87 for (unsigned i = 0; i < cusps.size() ; ++i) {
88 pwd2_out.concat( connector + pwd2_in.valueAt(cusps[i]) );
89 }
90 // connecting lines should be put where the tangent of the path equals the extrude_vector in direction
91 std::vector<double> rts = roots(dot(deriv, rot90(extrude_vector.getVector())));
92 for (unsigned i = 0; i < rts.size() ; ++i) {
93 pwd2_out.concat( connector + pwd2_in.valueAt(rts[i]) );
94 }
95 return pwd2_out;
96 }
98 default:
99 case 1: {
100 /* This one creates separate closed subpaths that correspond to the faces of the extruded shape.
101 * When the LPE is complete, one can convert the shape to a normal path, then break subpaths apart and start coloring them.
102 */
104 Piecewise<D2<SBasis> > pwd2_out;
105 bool closed_path = are_near(pwd2_in.firstValue(), pwd2_in.lastValue());
106 // split input path in pieces between points where deriv == vector
107 Piecewise<D2<SBasis> > deriv = derivative(pwd2_in);
108 std::vector<double> rts = roots(dot(deriv, rot90(extrude_vector.getVector())));
110 std::vector<double> cusps = find_cusps(pwd2_in);
112 std::vector<double> connector_pts;
113 if (rts.size() < 1) {
114 connector_pts = cusps;
115 } else if (cusps.size() < 1) {
116 connector_pts = rts;
117 } else {
118 connector_pts = rts;
119 connector_pts.insert(connector_pts.begin(), cusps.begin(), cusps.end());
120 sort(connector_pts.begin(), connector_pts.end());
121 }
123 double portion_t = 0.;
124 for (unsigned i = 0; i < connector_pts.size() ; ++i) {
125 Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, connector_pts[i] );
126 portion_t = connector_pts[i];
127 if (closed_path && i == 0) {
128 // if the path is closed, skip the first cut and add it to the last cut later
129 continue;
130 }
131 Piecewise<D2<SBasis> > part = cut;
132 part.continuousConcat(connector + cut.lastValue());
133 part.continuousConcat(reverse(cut) + extrude_vector.getVector());
134 part.continuousConcat(reverse(connector) + cut.firstValue());
135 pwd2_out.concat( part );
136 }
137 if (closed_path) {
138 Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, pwd2_in.domain().max() );
139 cut.continuousConcat(portion(pwd2_in, pwd2_in.domain().min(), connector_pts[0] ));
140 Piecewise<D2<SBasis> > part = cut;
141 part.continuousConcat(connector + cut.lastValue());
142 part.continuousConcat(reverse(cut) + extrude_vector.getVector());
143 part.continuousConcat(reverse(connector) + cut.firstValue());
144 pwd2_out.concat( part );
145 } else if (!are_near(portion_t, pwd2_in.domain().max())) {
146 Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, pwd2_in.domain().max() );
147 Piecewise<D2<SBasis> > part = cut;
148 part.continuousConcat(connector + cut.lastValue());
149 part.continuousConcat(reverse(cut) + extrude_vector.getVector());
150 part.continuousConcat(reverse(connector) + cut.firstValue());
151 pwd2_out.concat( part );
152 }
153 return pwd2_out;
154 }
155 }
156 }
158 void
159 LPEExtrude::resetDefaults(SPItem * item)
160 {
161 Effect::resetDefaults(item);
163 using namespace Geom;
165 Geom::OptRect bbox = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
166 if (bbox) {
167 Interval boundingbox_X = (*bbox)[Geom::X];
168 Interval boundingbox_Y = (*bbox)[Geom::Y];
169 extrude_vector.set_and_write_new_values( Geom::Point(boundingbox_X.middle(), boundingbox_Y.middle()),
170 (boundingbox_X.extent() + boundingbox_Y.extent())*Geom::Point(-0.05,0.2) );
171 }
172 }
174 } //namespace LivePathEffect
175 } /* namespace Inkscape */
177 /*
178 Local Variables:
179 mode:c++
180 c-file-style:"stroustrup"
181 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
182 indent-tabs-mode:nil
183 fill-column:99
184 End:
185 */
186 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :