Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-dashed-stroke.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
4 */
6#include <2geom/path.h>
7#include <2geom/pathvector.h>
8#include "helper/geom.h"
9
10// TODO due to internal breakage in glibmm headers, this must be last:
11#include <glibmm/i18n.h>
12
13namespace Inkscape {
14namespace LivePathEffect {
15
17 : Effect(lpeobject)
18 , numberdashes(_("Number of dashes"), _("Number of dashes"), "numberdashes", &wr, this, 3)
19 , holefactor(_("Hole factor"), _("Hole factor"), "holefactor", &wr, this, 0.0)
20 , splitsegments(_("Use segments"), _("Use segments"), "splitsegments", &wr, this, true)
21 , halfextreme(_("Half start/end"), _("Start and end of each segment has half size"), "halfextreme", &wr, this, true)
22 , unifysegment(_("Equalize dashes"), _("Global dash length is approximately the length of the dashes in the shortest path segment"),
23 "unifysegment", &wr, this, true)
24 , message(_("Note"), _("Important messages"), "message", &wr, this,
25 _("Add <b>\"Fill Between Many LPE\"</b> to add fill."))
26{
33
34 message.write_to_SVG(); // resert old legacy uneeded data
35 numberdashes.param_set_range(2, 999999999);
38 holefactor.param_set_range(-0.99999, 0.99999);
42}
43
45
47
49//TODO: find a better place to it
50double LPEDashedStroke::timeAtLength(double const A, Geom::Path const &segment)
51{
52 if ( A == 0 || segment[0].isDegenerate()) {
53 return 0;
54 }
55 double t = 1;
56 t = timeAtLength(A, segment.toPwSb());
57 return t;
58}
59
61//TODO: find a better place to it
63{
64 if ( A == 0 || pwd2.size() == 0) {
65 return 0;
66 }
67
68 double t = pwd2.size();
69 std::vector<double> t_roots = roots(Geom::arcLengthSb(pwd2) - A);
70 if (!t_roots.empty()) {
71 t = t_roots[0];
72 }
73 return t;
74}
75
77{
80 for (const auto & path_it : pv) {
81 if (path_it.empty()) {
82 continue;
83 }
84 Geom::Path::const_iterator curve_it1 = path_it.begin();
85 Geom::Path::const_iterator curve_it2 = ++(path_it.begin());
86 Geom::Path::const_iterator curve_endit = path_it.end_default();
87 if (path_it.closed()) {
88 const Geom::Curve &closingline = path_it.back_closed();
89 // the closing line segment is always of type
90 // Geom::LineSegment.
91 if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
92 // closingline.isDegenerate() did not work, because it only checks for
93 // *exact* zero length, which goes wrong for relative coordinates and
94 // rounding errors...
95 // the closing line segment has zero-length. So stop before that one!
96 curve_endit = path_it.end_open();
97 }
98 }
99 size_t numberdashes_fixed = numberdashes;
100 if(!splitsegments) {
101 numberdashes_fixed++;
102 }
103 size_t numberholes = numberdashes_fixed - 1;
104 size_t ammount = numberdashes_fixed + numberholes;
105 if (halfextreme) {
106 ammount--;
107 }
108 double base = 1/(double)ammount;
109 double globaldash = base * numberdashes_fixed * (1 + holefactor);
110 if (halfextreme) {
111 globaldash = base * (numberdashes_fixed - 1) * (1 + holefactor);
112 }
113 double globalhole = 1-globaldash;
114 double dashpercent = globaldash/numberdashes_fixed;
115 if (halfextreme) {
116 dashpercent = globaldash/(numberdashes_fixed -1);
117 }
118 double holepercent = globalhole/numberholes;
119 double dashsize_fixed = 0;
120 double holesize_fixed = 0;
121 Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2 = path_it.toPwSb();
122 double length_pwd2 = length (pwd2);
123 double minlength = length_pwd2;
124 if(unifysegment) {
125 while (curve_it1 != curve_endit) {
126 double length_segment = (*curve_it1).length();
127 if (length_segment < minlength) {
128 minlength = length_segment;
129 dashsize_fixed = (*curve_it1).length() * dashpercent;
130 holesize_fixed = (*curve_it1).length() * holepercent;
131 }
132 ++curve_it1;
133 ++curve_it2;
134 }
135 curve_it1 = path_it.begin();
136 curve_it2 = ++(path_it.begin());
137 curve_endit = path_it.end_default();
138 }
139 size_t p_index = 0;
140 size_t start_index = result.size();
141 if(splitsegments) {
142 while (curve_it1 != curve_endit) {
143 Geom::Path segment = path_it.portion(p_index, p_index + 1);
144 if(unifysegment) {
145 double integral;
146 modf((*curve_it1).length()/(dashsize_fixed + holesize_fixed), &integral);
147 numberdashes_fixed = (size_t)integral + 1;
148 numberholes = numberdashes_fixed - 1;
149 ammount = numberdashes_fixed + numberholes;
150 if (halfextreme) {
151 ammount--;
152 }
153 base = 1/(double)ammount;
154 globaldash = base * numberdashes_fixed * (1 + holefactor);
155 if (halfextreme) {
156 globaldash = base * (numberdashes_fixed - 1) * (1 + holefactor);
157 }
158 globalhole = 1-globaldash;
159 dashpercent = globaldash/numberdashes_fixed;
160 if (halfextreme) {
161 dashpercent = globaldash/(numberdashes_fixed -1);
162 }
163 holepercent = globalhole/numberholes;
164 }
165 double dashsize = (*curve_it1).length() * dashpercent;
166 double holesize = (*curve_it1).length() * holepercent;
167 if ((*curve_it1).isLineSegment()) {
168 if (result.size() && Geom::are_near(segment.initialPoint(),result[result.size()-1].finalPoint())) {
169 result[result.size()-1].setFinal(segment.initialPoint());
170 if (halfextreme) {
171 result[result.size()-1].append(segment.portion(0.0, dashpercent/2.0));
172 } else {
173 result[result.size()-1].append(segment.portion(0.0, dashpercent));
174 }
175 } else {
176 if (halfextreme) {
177 result.push_back(segment.portion(0.0, dashpercent/2.0));
178 } else {
179 result.push_back(segment.portion(0.0, dashpercent));
180 }
181 }
182
183 double start = dashpercent + holepercent;
184 if (halfextreme) {
185 start = (dashpercent/2.0) + holepercent;
186 }
187 while (start < 1) {
188 if (start + dashpercent > 1) {
189 result.push_back(segment.portion(start, 1));
190 } else {
191 result.push_back(segment.portion(start, start + dashpercent));
192 }
193 start += dashpercent + holepercent;
194 }
195 } else if (!(*curve_it1).isLineSegment()) {
196 double start = 0.0;
197 double end = 0.0;
198 if (halfextreme) {
199 end = timeAtLength(dashsize/2.0,segment);
200 } else {
201 end = timeAtLength(dashsize,segment);
202 }
203 if (result.size() && Geom::are_near(segment.initialPoint(),result[result.size()-1].finalPoint())) {
204 result[result.size()-1].setFinal(segment.initialPoint());
205 result[result.size()-1].append(segment.portion(start, end));
206 } else {
207 result.push_back(segment.portion(start, end));
208 }
209 double startsize = dashsize + holesize;
210 if (halfextreme) {
211 startsize = (dashsize/2.0) + holesize;
212 }
213 double endsize = startsize + dashsize;
214 start = timeAtLength(startsize,segment);
215 end = timeAtLength(endsize,segment);
216 while (start < 1 && start > 0) {
217 result.push_back(segment.portion(start, end));
218 startsize = endsize + holesize;
219 endsize = startsize + dashsize;
220 start = timeAtLength(startsize,segment);
221 end = timeAtLength(endsize,segment);
222 }
223 }
224 if (curve_it2 == curve_endit) {
225 if (path_it.closed()) {
226 Geom::Path end = result[result.size()-1];
227 end.setFinal(result[start_index].initialPoint());
228 end.append(result[start_index]);
229 result[start_index] = end;
230 }
231 }
232 p_index ++;
233 ++curve_it1;
234 ++curve_it2;
235 }
236 } else {
237 double start = 0.0;
238 double end = 0.0;
239 double dashsize = length_pwd2 * dashpercent;
240 double holesize = length_pwd2 * holepercent;
241 if (halfextreme) {
242 end = timeAtLength(dashsize/2.0,pwd2);
243 } else {
244 end = timeAtLength(dashsize,pwd2);
245 }
246 result.push_back(path_it.portion(start, end));
247 double startsize = dashsize + holesize;
248 if (halfextreme) {
249 startsize = (dashsize/2.0) + holesize;
250 }
251 double endsize = startsize + dashsize;
252 start = timeAtLength(startsize,pwd2);
253 end = timeAtLength(endsize,pwd2);
254 while (start < path_it.size() && start > 0) {
255 result.push_back(path_it.portion(start, end));
256 startsize = endsize + holesize;
257 endsize = startsize + dashsize;
258 start = timeAtLength(startsize,pwd2);
259 end = timeAtLength(endsize,pwd2);
260 }
261 if (path_it.closed()) {
262 Geom::Path end = result[result.size()-1];
263 end.setFinal(result[start_index].initialPoint());
264 end.append(result[start_index]);
265 result[start_index] = end;
266 }
267 }
268 }
269 return result;
270}
271
272}; //namespace LivePathEffect
273}; /* namespace Inkscape */
274
275/*
276 Local Variables:
277 mode:c++
278 c-file-style:"stroustrup"
279 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
280 indent-tabs-mode:nil
281 fill-column:99
282 End:
283*/
284// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
Path - a sequence of contiguous curves.
Abstract continuous curve on a plane defined on [0,1].
Definition curve.h:78
virtual Point initialPoint() const =0
Retrieve the start of the curve.
virtual Point finalPoint() const =0
Retrieve the end of the curve.
Adaptor that creates 2D functions from 1D ones.
Definition d2.h:55
Sequence of subpaths.
Definition pathvector.h:122
Sequence of contiguous curves, aka spline.
Definition path.h:353
Piecewise< D2< SBasis > > toPwSb() const
Definition path.cpp:388
Path portion(Coord f, Coord t) const
Definition path.h:645
Point initialPoint() const
Get the first point in the path.
Definition path.h:705
Function defined as discrete pieces.
Definition piecewise.h:71
void registerParameter(Parameter *param)
Definition effect.cpp:1704
void doBeforeEffect(SPLPEItem const *lpeitem) override
Is performed each time before the effect is updated.
double timeAtLength(double const A, Geom::Path const &segment)
Calculate the time in curve_in with a real time of A.
Geom::PathVector doEffect_path(Geom::PathVector const &path_in) override
LPEDashedStroke(LivePathEffectObject *lpeobject)
void param_set_digits(unsigned digits)
void param_set_range(double min, double max)
void param_set_increments(double step, double page)
Css & result
Geom::PathVector pathv_to_linear_and_cubic_beziers(Geom::PathVector const &pathv)
Definition geom.cpp:586
Specific geometry functions for Inkscape, not provided my lib2geom.
Geom::Point start
Geom::Point end
Coord length(LineSegment const &seg)
std::vector< double > roots(SBasis const &s)
Bezier integral(Bezier const &a)
Definition bezier.cpp:294
Piecewise< SBasis > arcLengthSb(D2< SBasis > const &M, double tol=.01)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
Helper class to stream background task notifications as a series of messages.
PathVector - a sequence of subpaths.