Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-extrude.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
7/* Authors:
8 * Johan Engelen <j.b.c.engelen@utwente.nl>
9 *
10 * Copyright (C) 2009 Authors
11 *
12 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
13 */
14
16
17// TODO due to internal breakage in glibmm headers, this must be last:
18#include <glibmm/i18n.h>
19
20
21namespace Inkscape {
22namespace LivePathEffect {
23
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;
30
32}
33
34LPEExtrude::~LPEExtrude() = default;
35
37 return Geom::are_near(cross(a,b), 0., 0.5);
38}
39
40// find cusps, except at start/end for closed paths.
41// this should be factored out later.
42static std::vector<double> find_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in ) {
43 using namespace Geom;
44 Piecewise<D2<SBasis> > deriv = derivative(pwd2_in);
45 std::vector<double> cusps;
46 // cusps are spots where the derivative jumps.
47 for (unsigned i = 1 ; i < deriv.size() ; ++i) {
48 if ( ! are_colinear(deriv[i-1].at1(), deriv[i].at0()) ) {
49 // there is a jump in the derivative, so add it to the cusps list
50 cusps.push_back(deriv.cuts[i]);
51 }
52 }
53 return cusps;
54}
55
58{
59 using namespace Geom;
60
61 // generate connecting lines (the 'sides' of the extrusion)
62 Geom::Path path(Point(0.,0.));
64 Piecewise<D2<SBasis> > connector = path.toPwSb();
65
66 switch( 1 ) {
67 case 0: {
68 /* This one results in the following subpaths: the original, a displaced copy, and connector lines between the two
69 */
70
71 Piecewise<D2<SBasis> > pwd2_out = pwd2_in;
72 // generate extrusion bottom: (just a copy of original path, displaced a bit)
73 pwd2_out.concat( pwd2_in + extrude_vector.getVector() );
74
75 // connecting lines should be put at start and end of path if it is not closed
76 // it is not possible to check whether a piecewise<T> path is closed,
77 // so we check whether start and end are close
78 if ( ! are_near(pwd2_in.firstValue(), pwd2_in.lastValue()) ) {
79 pwd2_out.concat( connector + pwd2_in.firstValue() );
80 pwd2_out.concat( connector + pwd2_in.lastValue() );
81 }
82 // connecting lines should be put at cusps
83 Piecewise<D2<SBasis> > deriv = derivative(pwd2_in);
84 std::vector<double> cusps; // = roots(deriv);
85 for (double cusp : cusps) {
86 pwd2_out.concat( connector + pwd2_in.valueAt(cusp) );
87 }
88 // connecting lines should be put where the tangent of the path equals the extrude_vector in direction
89 std::vector<double> rts = roots(dot(deriv, rot90(extrude_vector.getVector())));
90 for (double rt : rts) {
91 pwd2_out.concat( connector + pwd2_in.valueAt(rt) );
92 }
93 return pwd2_out;
94 }
95
96 default:
97 case 1: {
98 /* This one creates separate closed subpaths that correspond to the faces of the extruded shape.
99 * When the LPE is complete, one can convert the shape to a normal path, then break subpaths apart and start coloring them.
100 */
101
102 Piecewise<D2<SBasis> > pwd2_out;
103 // split input path in pieces between points where deriv == vector
104 Piecewise<D2<SBasis> > deriv = derivative(pwd2_in);
105 std::vector<double> rts = roots(dot(deriv, rot90(extrude_vector.getVector())));
106
107 std::vector<double> cusps = find_cusps(pwd2_in);
108
109 // see if we should treat the path as being closed.
110 bool closed_path = false;
111 if ( are_near(pwd2_in.firstValue(), pwd2_in.lastValue()) ) {
112 // the path is closed, however if there is a cusp at the closing point, we should treat it as being an open path.
113 if ( are_colinear(deriv.firstValue(), deriv.lastValue()) ) {
114 // there is no jump in the derivative, so treat path as being closed
115 closed_path = true;
116 }
117 }
118
119 std::vector<double> connector_pts;
120 if (rts.empty()) {
121 connector_pts = cusps;
122 } else if (cusps.empty()) {
123 connector_pts = rts;
124 } else {
125 connector_pts = rts;
126 connector_pts.insert(connector_pts.begin(), cusps.begin(), cusps.end());
127 sort(connector_pts.begin(), connector_pts.end());
128 }
129
130 double portion_t = 0.;
131 for (unsigned i = 0; i < connector_pts.size() ; ++i) {
132 Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, connector_pts[i] );
133 portion_t = connector_pts[i];
134 if (closed_path && i == 0) {
135 // if the path is closed, skip the first cut and add it to the last cut later
136 continue;
137 }
138 Piecewise<D2<SBasis> > part = cut;
139 part.continuousConcat(connector + cut.lastValue());
141 part.continuousConcat(reverse(connector) + cut.firstValue());
142 pwd2_out.concat( part );
143 }
144 if (closed_path) {
145 Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, pwd2_in.domain().max() );
146 cut.continuousConcat(portion(pwd2_in, pwd2_in.domain().min(), connector_pts[0] ));
147 Piecewise<D2<SBasis> > part = cut;
148 part.continuousConcat(connector + cut.lastValue());
150 part.continuousConcat(reverse(connector) + cut.firstValue());
151 pwd2_out.concat( part );
152 } else if (!are_near(portion_t, pwd2_in.domain().max())) {
153 Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, pwd2_in.domain().max() );
154 Piecewise<D2<SBasis> > part = cut;
155 part.continuousConcat(connector + cut.lastValue());
157 part.continuousConcat(reverse(connector) + cut.firstValue());
158 pwd2_out.concat( part );
159 }
160 return pwd2_out;
161 }
162 }
163}
164
165void
167{
169
170 using namespace Geom;
171
173 if (bbox) {
174 Interval const &boundingbox_X = (*bbox)[Geom::X];
175 Interval const &boundingbox_Y = (*bbox)[Geom::Y];
176 extrude_vector.set_and_write_new_values( Geom::Point(boundingbox_X.middle(), boundingbox_Y.middle()),
177 (boundingbox_X.extent() + boundingbox_Y.extent())*Geom::Point(-0.05,0.2) );
178 }
179}
180
181} //namespace LivePathEffect
182} /* namespace Inkscape */
183
184/*
185 Local Variables:
186 mode:c++
187 c-file-style:"stroustrup"
188 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
189 indent-tabs-mode:nil
190 fill-column:99
191 End:
192*/
193// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Adaptor that creates 2D functions from 1D ones.
Definition d2.h:55
constexpr C extent() const
constexpr C middle() const
Range of real numbers that is never empty.
Definition interval.h:59
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Sequence of contiguous curves, aka spline.
Definition path.h:353
Piecewise< D2< SBasis > > toPwSb() const
Definition path.cpp:388
void appendNew(Args &&... args)
Append a new curve to the path.
Definition path.h:804
Function defined as discrete pieces.
Definition piecewise.h:71
unsigned size() const
Definition piecewise.h:131
void continuousConcat(const Piecewise< T > &other)
Definition piecewise.h:251
output_type lastValue() const
Definition piecewise.h:109
std::vector< double > cuts
Definition piecewise.h:75
output_type firstValue() const
Definition piecewise.h:106
void concat(const Piecewise< T > &other)
Definition piecewise.h:235
Two-dimensional point that doubles as a vector.
Definition point.h:66
void registerParameter(Parameter *param)
Definition effect.cpp:1704
virtual void resetDefaults(SPItem const *item)
Sets all parameters to their default values and writes them to SVG.
Definition effect.cpp:2008
LPEExtrude(LivePathEffectObject *lpeobject)
void resetDefaults(SPItem const *item) override
Sets all parameters to their default values and writes them to SVG.
Geom::Piecewise< Geom::D2< Geom::SBasis > > doEffect_pwd2(Geom::Piecewise< Geom::D2< Geom::SBasis > > const &pwd2_in) override
Geom::Point getVector() const
Definition vector.h:41
void set_and_write_new_values(Geom::Point const &new_origin, Geom::Point const &new_vector)
Definition vector.cpp:124
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::OptRect geometricBounds(Geom::Affine const &transform=Geom::identity()) const
Get item's geometric bounding box in this item's coordinate system.
Definition sp-item.cpp:919
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
SPItem * item
LPE effect for extruding paths (making them "3D").
Various utility functions.
Definition affine.h:22
Bezier reverse(const Bezier &a)
Definition bezier.h:342
std::vector< double > roots(SBasis const &s)
Bezier portion(const Bezier &a, double from, double to)
Definition bezier.cpp:250
Bezier derivative(Bezier const &a)
Definition bezier.cpp:282
Piecewise< SBasis > cross(Piecewise< D2< SBasis > > const &a, Piecewise< D2< SBasis > > const &b)
T dot(D2< T > const &a, D2< T > const &b)
Definition d2.h:355
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
D2< T > rot90(D2< T > const &a)
Definition d2.h:397
static bool are_colinear(Geom::Point a, Geom::Point b)
static std::vector< double > find_cusps(Geom::Piecewise< Geom::D2< Geom::SBasis > > const &pwd2_in)
Helper class to stream background task notifications as a series of messages.