Code

Super duper mega (fun!) commit: replaced encoding=utf-8 with fileencoding=utf-8 in...
[inkscape.git] / src / live_effects / lpe-extrude.cpp
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 #include "sp-item.h"
23 namespace Inkscape {
24 namespace LivePathEffect {
26 LPEExtrude::LPEExtrude(LivePathEffectObject *lpeobject) :
27     Effect(lpeobject),
28     extrude_vector(_("Direction"), _("Defines the direction and magnitude of the extrusion"), "extrude_vector", &wr, this, Geom::Point(-10,10))
29 {
30     show_orig_path = true;
31     concatenate_before_pwd2 = false;
33     registerParameter( dynamic_cast<Parameter *>(&extrude_vector) );
34 }
36 LPEExtrude::~LPEExtrude()
37 {
39 }
41 static bool are_colinear(Geom::Point a, Geom::Point b) {
42     return Geom::are_near(cross(a,b), 0., 0.5);
43 }
45 // find cusps, except at start/end for closed paths.
46 // this should be factored out later.
47 static std::vector<double> find_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in ) {
48     using namespace Geom;
49     Piecewise<D2<SBasis> > deriv = derivative(pwd2_in);
50     std::vector<double> cusps;
51     // cusps are spots where the derivative jumps.
52     for (unsigned i = 1 ; i < deriv.size() ; ++i) {
53         if ( ! are_colinear(deriv[i-1].at1(), deriv[i].at0()) ) {
54             // there is a jump in the derivative, so add it to the cusps list
55             cusps.push_back(deriv.cuts[i]);
56         }
57     }
58     return cusps;
59 }
61 Geom::Piecewise<Geom::D2<Geom::SBasis> >
62 LPEExtrude::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
63 {
64     using namespace Geom;
66     // generate connecting lines (the 'sides' of the extrusion)
67     Path path(Point(0.,0.));
68     path.appendNew<Geom::LineSegment>( extrude_vector.getVector() );
69     Piecewise<D2<SBasis> > connector = path.toPwSb();
71     switch( 1 ) {
72     case 0: {
73         /* This one results in the following subpaths: the original, a displaced copy, and connector lines between the two
74          */
76         Piecewise<D2<SBasis> > pwd2_out = pwd2_in;
77         // generate extrusion bottom: (just a copy of original path, displaced a bit)
78         pwd2_out.concat( pwd2_in + extrude_vector.getVector() );
80         // connecting lines should be put at start and end of path if it is not closed
81         // it is not possible to check whether a piecewise<T> path is closed, 
82         // so we check whether start and end are close
83         if ( ! are_near(pwd2_in.firstValue(), pwd2_in.lastValue()) ) {
84             pwd2_out.concat( connector + pwd2_in.firstValue() );
85             pwd2_out.concat( connector + pwd2_in.lastValue() );
86         }
87         // connecting lines should be put at cusps
88         Piecewise<D2<SBasis> > deriv = derivative(pwd2_in);
89         std::vector<double> cusps; // = roots(deriv);
90         for (unsigned i = 0; i < cusps.size() ; ++i) {
91             pwd2_out.concat( connector + pwd2_in.valueAt(cusps[i]) );
92         }
93         // connecting lines should be put where the tangent of the path equals the extrude_vector in direction
94         std::vector<double> rts = roots(dot(deriv, rot90(extrude_vector.getVector())));
95         for (unsigned i = 0; i < rts.size() ; ++i) {
96             pwd2_out.concat( connector + pwd2_in.valueAt(rts[i]) );
97         }
98         return pwd2_out;
99     }
101     default:
102     case 1: {
103         /* This one creates separate closed subpaths that correspond to the faces of the extruded shape.
104          * When the LPE is complete, one can convert the shape to a normal path, then break subpaths apart and start coloring them.
105          */
107         Piecewise<D2<SBasis> > pwd2_out;
108         // split input path in pieces between points where deriv == vector
109         Piecewise<D2<SBasis> > deriv = derivative(pwd2_in);
110         std::vector<double> rts = roots(dot(deriv, rot90(extrude_vector.getVector())));
112         std::vector<double> cusps = find_cusps(pwd2_in);
114         // see if we should treat the path as being closed.
115         bool closed_path = false;
116         if ( are_near(pwd2_in.firstValue(), pwd2_in.lastValue()) ) {
117             // the path is closed, however if there is a cusp at the closing point, we should treat it as being an open path.
118             if ( are_colinear(deriv.firstValue(), deriv.lastValue()) ) {
119                 // there is no jump in the derivative, so treat path as being closed
120                 closed_path = true;
121             }
122         }
124         std::vector<double> connector_pts;
125         if (rts.size() < 1) {
126             connector_pts = cusps;
127         } else if (cusps.size() < 1) {
128             connector_pts = rts;
129         } else {
130             connector_pts = rts;
131             connector_pts.insert(connector_pts.begin(), cusps.begin(), cusps.end());
132             sort(connector_pts.begin(), connector_pts.end());
133         }
135         double portion_t = 0.;
136         for (unsigned i = 0; i < connector_pts.size() ; ++i) {
137             Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, connector_pts[i] );
138             portion_t = connector_pts[i];
139             if (closed_path && i == 0) {
140                 // if the path is closed, skip the first cut and add it to the last cut later
141                 continue;
142             }
143             Piecewise<D2<SBasis> > part = cut;
144             part.continuousConcat(connector + cut.lastValue());
145             part.continuousConcat(reverse(cut) + extrude_vector.getVector());
146             part.continuousConcat(reverse(connector) + cut.firstValue());
147             pwd2_out.concat( part );
148         }
149         if (closed_path) {
150             Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, pwd2_in.domain().max() );
151             cut.continuousConcat(portion(pwd2_in, pwd2_in.domain().min(), connector_pts[0] ));
152             Piecewise<D2<SBasis> > part = cut;
153             part.continuousConcat(connector + cut.lastValue());
154             part.continuousConcat(reverse(cut) + extrude_vector.getVector());
155             part.continuousConcat(reverse(connector) + cut.firstValue());
156             pwd2_out.concat( part );
157         } else if (!are_near(portion_t, pwd2_in.domain().max())) {
158             Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, pwd2_in.domain().max() );
159             Piecewise<D2<SBasis> > part = cut;
160             part.continuousConcat(connector + cut.lastValue());
161             part.continuousConcat(reverse(cut) + extrude_vector.getVector());
162             part.continuousConcat(reverse(connector) + cut.firstValue());
163             pwd2_out.concat( part );
164         }
165         return pwd2_out;
166     }
167     }
170 void
171 LPEExtrude::resetDefaults(SPItem * item)
173     Effect::resetDefaults(item);
175     using namespace Geom;
177     Geom::OptRect bbox = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
178     if (bbox) {
179         Interval boundingbox_X = (*bbox)[Geom::X];
180         Interval boundingbox_Y = (*bbox)[Geom::Y];
181         extrude_vector.set_and_write_new_values( Geom::Point(boundingbox_X.middle(), boundingbox_Y.middle()), 
182                                                  (boundingbox_X.extent() + boundingbox_Y.extent())*Geom::Point(-0.05,0.2) );
183     }
186 } //namespace LivePathEffect
187 } /* namespace Inkscape */
189 /*
190   Local Variables:
191   mode:c++
192   c-file-style:"stroustrup"
193   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
194   indent-tabs-mode:nil
195   fill-column:99
196   End:
197 */
198 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :