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>
20 namespace Inkscape {
21 namespace LivePathEffect {
23 LPEExtrude::LPEExtrude(LivePathEffectObject *lpeobject) :
24 Effect(lpeobject),
25 extrude_vector(_("Direction"), _("Defines the direction and magnitude of the extrusion"), "extrude_vector", &wr, this, Geom::Point(-10,10))
26 {
27 show_orig_path = true;
28 concatenate_before_pwd2 = false;
30 registerParameter( dynamic_cast<Parameter *>(&extrude_vector) );
31 }
33 LPEExtrude::~LPEExtrude()
34 {
36 }
39 Geom::Piecewise<Geom::D2<Geom::SBasis> >
40 LPEExtrude::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
41 {
42 using namespace Geom;
44 // generate connecting lines (the 'sides' of the extrusion)
45 Path path(Point(0.,0.));
46 path.appendNew<Geom::LineSegment>( extrude_vector.getVector() );
47 Piecewise<D2<SBasis> > connector = path.toPwSb();
49 switch( 1 ) {
50 case 0: {
51 Piecewise<D2<SBasis> > pwd2_out = pwd2_in;
52 // generate extrusion bottom: (just a copy of original path, displaced a bit)
53 pwd2_out.concat( pwd2_in + extrude_vector.getVector() );
55 // connecting lines should be put at start and end of path if it is not closed
56 // it is not possible to check whether a piecewise<T> path is closed,
57 // so we check whether start and end are close
58 if ( ! are_near(pwd2_in.firstValue(), pwd2_in.lastValue()) ) {
59 pwd2_out.concat( connector + pwd2_in.firstValue() );
60 pwd2_out.concat( connector + pwd2_in.lastValue() );
61 }
62 // connecting lines should be put at cusps
63 Piecewise<D2<SBasis> > deriv = derivative(pwd2_in);
64 std::vector<double> cusps; // = roots(deriv);
65 for (unsigned i = 0; i < cusps.size() ; ++i) {
66 pwd2_out.concat( connector + pwd2_in.valueAt(cusps[i]) );
67 }
68 // connecting lines should be put where the tangent of the path equals the extrude_vector in direction
69 std::vector<double> rts = roots(dot(deriv, rot90(extrude_vector.getVector())));
70 for (unsigned i = 0; i < rts.size() ; ++i) {
71 pwd2_out.concat( connector + pwd2_in.valueAt(rts[i]) );
72 }
73 return pwd2_out;
74 }
76 case 1: {
77 Piecewise<D2<SBasis> > pwd2_out;
78 bool closed_path = are_near(pwd2_in.firstValue(), pwd2_in.lastValue());
79 // split input path in pieces between points where deriv == vector
80 Piecewise<D2<SBasis> > deriv = derivative(pwd2_in);
81 std::vector<double> rts = roots(dot(deriv, rot90(extrude_vector.getVector())));
82 double portion_t = 0.;
83 for (unsigned i = 0; i < rts.size() ; ++i) {
84 Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, rts[i] );
85 portion_t = rts[i];
86 if (closed_path && i == 0) {
87 // if the path is closed, skip the first cut and add it to the last cut later
88 continue;
89 }
90 Piecewise<D2<SBasis> > part = cut;
91 part.continuousConcat(connector + cut.lastValue());
92 part.continuousConcat(reverse(cut) + extrude_vector.getVector());
93 part.continuousConcat(reverse(connector) + cut.firstValue());
94 pwd2_out.concat( part );
95 }
96 if (closed_path) {
97 Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, pwd2_in.domain().max() );
98 cut.continuousConcat(portion(pwd2_in, pwd2_in.domain().min(), rts[0] ));
99 Piecewise<D2<SBasis> > part = cut;
100 part.continuousConcat(connector + cut.lastValue());
101 part.continuousConcat(reverse(cut) + extrude_vector.getVector());
102 part.continuousConcat(reverse(connector) + cut.firstValue());
103 pwd2_out.concat( part );
104 } else if (!are_near(portion_t, pwd2_in.domain().max())) {
105 Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, pwd2_in.domain().max() );
106 Piecewise<D2<SBasis> > part = cut;
107 part.continuousConcat(connector + cut.lastValue());
108 part.continuousConcat(reverse(cut) + extrude_vector.getVector());
109 part.continuousConcat(reverse(connector) + cut.firstValue());
110 pwd2_out.concat( part );
111 }
112 return pwd2_out;
113 }
114 }
115 }
117 void
118 LPEExtrude::resetDefaults(SPItem * item)
119 {
120 Effect::resetDefaults(item);
122 using namespace Geom;
124 Geom::OptRect bbox = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
125 if (bbox) {
126 Interval boundingbox_X = (*bbox)[Geom::X];
127 Interval boundingbox_Y = (*bbox)[Geom::Y];
128 extrude_vector.set_and_write_new_values( Geom::Point(boundingbox_X.middle(), boundingbox_Y.middle()),
129 (boundingbox_X.extent() + boundingbox_Y.extent())*Geom::Point(-0.05,0.2) );
130 }
131 }
133 } //namespace LivePathEffect
134 } /* namespace Inkscape */
136 /*
137 Local Variables:
138 mode:c++
139 c-file-style:"stroustrup"
140 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
141 indent-tabs-mode:nil
142 fill-column:99
143 End:
144 */
145 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :