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 default:
77 case 1: {
78 Piecewise<D2<SBasis> > pwd2_out;
79 bool closed_path = are_near(pwd2_in.firstValue(), pwd2_in.lastValue());
80 // split input path in pieces between points where deriv == vector
81 Piecewise<D2<SBasis> > deriv = derivative(pwd2_in);
82 std::vector<double> rts = roots(dot(deriv, rot90(extrude_vector.getVector())));
83 double portion_t = 0.;
84 for (unsigned i = 0; i < rts.size() ; ++i) {
85 Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, rts[i] );
86 portion_t = rts[i];
87 if (closed_path && i == 0) {
88 // if the path is closed, skip the first cut and add it to the last cut later
89 continue;
90 }
91 Piecewise<D2<SBasis> > part = cut;
92 part.continuousConcat(connector + cut.lastValue());
93 part.continuousConcat(reverse(cut) + extrude_vector.getVector());
94 part.continuousConcat(reverse(connector) + cut.firstValue());
95 pwd2_out.concat( part );
96 }
97 if (closed_path) {
98 Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, pwd2_in.domain().max() );
99 cut.continuousConcat(portion(pwd2_in, pwd2_in.domain().min(), rts[0] ));
100 Piecewise<D2<SBasis> > part = cut;
101 part.continuousConcat(connector + cut.lastValue());
102 part.continuousConcat(reverse(cut) + extrude_vector.getVector());
103 part.continuousConcat(reverse(connector) + cut.firstValue());
104 pwd2_out.concat( part );
105 } else if (!are_near(portion_t, pwd2_in.domain().max())) {
106 Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, pwd2_in.domain().max() );
107 Piecewise<D2<SBasis> > part = cut;
108 part.continuousConcat(connector + cut.lastValue());
109 part.continuousConcat(reverse(cut) + extrude_vector.getVector());
110 part.continuousConcat(reverse(connector) + cut.firstValue());
111 pwd2_out.concat( part );
112 }
113 return pwd2_out;
114 }
115 }
116 }
118 void
119 LPEExtrude::resetDefaults(SPItem * item)
120 {
121 Effect::resetDefaults(item);
123 using namespace Geom;
125 Geom::OptRect bbox = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
126 if (bbox) {
127 Interval boundingbox_X = (*bbox)[Geom::X];
128 Interval boundingbox_Y = (*bbox)[Geom::Y];
129 extrude_vector.set_and_write_new_values( Geom::Point(boundingbox_X.middle(), boundingbox_Y.middle()),
130 (boundingbox_X.extent() + boundingbox_Y.extent())*Geom::Point(-0.05,0.2) );
131 }
132 }
134 } //namespace LivePathEffect
135 } /* namespace Inkscape */
137 /*
138 Local Variables:
139 mode:c++
140 c-file-style:"stroustrup"
141 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
142 indent-tabs-mode:nil
143 fill-column:99
144 End:
145 */
146 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :