Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-ruler.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
6/*
7 * Authors:
8 * Maximilian Albert
9 *
10 * Copyright (C) Maximilian Albert 2008 <maximilian.albert@gmail.com>
11 *
12 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
13 */
14
17// TODO due to internal breakage in glibmm headers, this must be last:
18#include <glibmm/i18n.h>
19
20namespace Inkscape {
21namespace LivePathEffect {
22
24 {MARKDIR_LEFT , N_("Left"), "left"},
25 {MARKDIR_RIGHT , N_("Right"), "right"},
26 {MARKDIR_BOTH , N_("Both"), "both"},
27};
29
31 {BORDERMARK_NONE , NC_("Border mark", "None"), "none"},
32 {BORDERMARK_START , N_("Start"), "start"},
33 {BORDERMARK_END , N_("End"), "end"},
34 {BORDERMARK_BOTH , N_("Both"), "both"},
35};
37
39 Effect(lpeobject),
40 mark_distance(_("_Mark distance:"), _("Distance between successive ruler marks"), "mark_distance", &wr, this, 20.0),
41 unit(_("Unit:"), _("Unit"), "unit", &wr, this),
42 mark_length(_("Ma_jor length:"), _("Length of major ruler marks"), "mark_length", &wr, this, 14.0),
43 minor_mark_length(_("Mino_r length:"), _("Length of minor ruler marks"), "minor_mark_length", &wr, this, 7.0),
44 minor_mark_gap(_("Minor mark _gap:"),
45 // xgettext:no-c-format
46 _("Space between path and minor ruler mark, % of mark length"),
47 "minor_mark_gap", &wr, this, 0.0),
48 major_mark_gap(_("Major mar_k gap:"),
49 // xgettext:no-c-format
50 _("Space between path and major ruler mark, % of mark length"),
51 "major_mark_gap", &wr, this, 0.0),
52 major_mark_steps(_("Major steps_:"), _("Draw a major mark every ... steps"), "major_mark_steps", &wr, this, 5),
53 mark_angle(_("Mark angle:"), _("Rotate marks (-180° to 180°)"), "mark_angle", &wr, this, 0.0),
54 shift(_("Shift marks _by:"), _("Shift marks by this many steps"), "shift", &wr, this, 0),
55 mark_dir(_("Mark direction:"), _("Direction of marks (when viewing along the path from start to end)"), "mark_dir", MarkDirTypeConverter, &wr, this, MARKDIR_LEFT),
56 offset(_("_Offset:"), _("Offset of first mark"), "offset", &wr, this, 0.0),
57 border_marks(_("Border marks:"), _("Choose whether to draw marks at the beginning and end of the path"), "border_marks", BorderMarkTypeConverter, &wr, this, BORDERMARK_BOTH)
58{
71
73 mark_angle.param_set_range(-180, 180);
77 mark_distance.param_set_range(0.01, std::numeric_limits<double>::max());
86}
87
88LPERuler::~LPERuler() = default;
89
92
94LPERuler::ruler_mark(Geom::Point const &A, Geom::Point const &n, MarkType const &marktype)
95{
96 using namespace Geom;
97
98 double real_mark_length = mark_length;
99 SPDocument *document = getSPDoc();
100 if (document) {
101 if (legacy) {
102 real_mark_length = Inkscape::Util::Quantity::convert(real_mark_length, unit.get_abbreviation(), document->getWidth().unit->abbr.c_str());
103 } else {
104 real_mark_length = Inkscape::Util::Quantity::convert(real_mark_length, unit.get_abbreviation(), "px") / document->getDocumentScale()[Geom::X];
105 }
106 }
107 double real_minor_mark_length = minor_mark_length;
108 if (document) {
109 if (legacy) {
110 real_minor_mark_length = Inkscape::Util::Quantity::convert(real_minor_mark_length, unit.get_abbreviation(), document->getWidth().unit->abbr.c_str());
111 } else {
112 real_minor_mark_length = Inkscape::Util::Quantity::convert(real_minor_mark_length, unit.get_abbreviation(), "px") / document->getDocumentScale()[Geom::X];
113 }
114 }
115 n_major = real_mark_length * n;
116 n_minor = real_minor_mark_length * n;
117 if (mark_dir == MARKDIR_BOTH) {
118 n_major = n_major * 0.5;
119 n_minor = n_minor * 0.5;
120 }
121
122 Point C, D;
123 double factor = 1.0;
124 double mark_gap = 0;
125 switch (marktype) {
126 case MARK_MAJOR:
127 mark_gap = major_mark_gap;
128 C = A;
129 D = A + n_major;
130 if (real_mark_length && real_minor_mark_length && real_mark_length < real_minor_mark_length) {
131 factor = real_mark_length/real_minor_mark_length;
132 }
133 if (mark_dir == MARKDIR_BOTH)
134 C -= n_major;
135 break;
136 case MARK_MINOR:
137 mark_gap = minor_mark_gap;
138 if (real_mark_length && real_minor_mark_length && real_mark_length > real_minor_mark_length) {
139 factor = real_minor_mark_length / real_mark_length;
140 }
141 C = A;
142 D = A + n_minor;
143 if (mark_dir == MARKDIR_BOTH)
144 C -= n_minor;
145 break;
146 default:
147 // do nothing
148 break;
149 }
150
151 Piecewise<D2<SBasis> > seg(D2<SBasis>(SBasis(C[X], D[X]), SBasis(C[Y], D[Y])));
152 if (mark_angle || mark_gap) {
153 Geom::PathVector pvec = path_from_piecewise(seg,0.0001);
154 if (mark_angle) {
155 pvec *= Geom::Translate(A).inverse();
156 pvec *= Geom::Rotate(Geom::rad_from_deg(mark_angle));
157 pvec *= Geom::Translate(A);
158 seg = paths_to_pw(pvec);
159 }
160 if (mark_gap) {
162 if (mark_dir == MARKDIR_BOTH) {
163 pv.push_back(pvec[0].portion(0, 0.5 - ((mark_gap * 0.5 * (1 + (1 - factor)))/100.0)));
164 pv.push_back(pvec[0].portion(0.5 + ((mark_gap * 0.5 * (1 + (1 - factor)))/100.0), 1));
165 } else {
166 pv.push_back(pvec[0].portion((mark_gap * (1 + (1 - factor)))/100.0,1));
167 }
168 seg = paths_to_pw(pv);
169 }
170 }
171 return seg;
172}
173
174void
176{
177 lpeversion.param_setValue("1.3.1", true);
178 legacy = false;
179}
180
183{
184 if (is_load) {
185 legacy = lpeversion.param_getSVGValue() < "1.3.1";
186 }
187 using namespace Geom;
188
189 const int mminterval = static_cast<int>(major_mark_steps);
190 const int i_shift = static_cast<int>(shift) % mminterval;
191 int sign = (mark_dir == MARKDIR_RIGHT ? 1 : -1 );
192
193 Piecewise<D2<SBasis> >output(pwd2_in);
194 Piecewise<D2<SBasis> >speed = derivative(pwd2_in);
195 Piecewise<SBasis> arclength = arcLengthSb(pwd2_in);
196 double totlength = arclength.lastValue();
197
198 //find at which times to draw a mark:
199 std::vector<double> s_cuts;
200 SPDocument *document = getSPDoc();
201
202 if (document) {
203 bool write = false;
204 if (legacy) {
205 auto const punit = prev_unit;
207 if (!punit.empty() && prev_unit != punit) {
208 //_document->getDocumentScale().inverse()
213 write = true;
214 }
215 } else {
216 auto const punit = prev_unit;
218 if (!punit.empty() && prev_unit != punit) {
223 write = true;
224 }
225 }
226 if (write) {
231 }
232 }
233 double real_mark_distance = mark_distance;
234 if (document) {
235 if (legacy) {
236 real_mark_distance = Inkscape::Util::Quantity::convert(real_mark_distance, unit.get_abbreviation(), document->getWidth().unit->abbr.c_str());
237 } else {
238 real_mark_distance = Inkscape::Util::Quantity::convert(real_mark_distance, unit.get_abbreviation(), "px") / document->getDocumentScale()[Geom::X];
239 }
240 }
241 double real_offset = offset;
242 if (document) {
243 if (legacy) {
244 real_offset = Inkscape::Util::Quantity::convert(real_offset, unit.get_abbreviation(), document->getWidth().unit->abbr.c_str());
245 } else {
246 real_offset = Inkscape::Util::Quantity::convert(real_offset, unit.get_abbreviation(), "px") / document->getDocumentScale()[Geom::X];
247 }
248 }
249 for (double s = real_offset; s<totlength; s+=real_mark_distance){
250 s_cuts.push_back(s);
251 }
252 std::vector<std::vector<double> > roots = multi_roots(arclength, s_cuts);
253 std::vector<double> t_cuts;
254 for (auto & root : roots){
255 //FIXME: 2geom multi_roots solver seem to sometimes "repeat" solutions.
256 //Here, we are supposed to have one and only one solution for each s.
257 if(root.size()>0)
258 t_cuts.push_back(root[0]);
259 }
260 //draw the marks
261 for (size_t i = 0; i < t_cuts.size(); i++) {
262 Point A = pwd2_in(t_cuts[i]);
263 Point n = rot90(unit_vector(speed(t_cuts[i])))*sign;
264 if (static_cast<int>(i % mminterval) == i_shift) {
265 output.concat (ruler_mark(A, n, MARK_MAJOR));
266 } else {
267 output.concat (ruler_mark(A, n, MARK_MINOR));
268 }
269 }
270 //eventually draw a mark at start
271 if ((border_marks == BORDERMARK_START || border_marks == BORDERMARK_BOTH) && (offset != 0.0 || i_shift != 0)){
272 Point A = pwd2_in.firstValue();
273 Point n = rot90(unit_vector(speed.firstValue()))*sign;
274 output.concat (ruler_mark(A, n, MARK_MAJOR));
275 }
276 //eventually draw a mark at end
278 Point A = pwd2_in.lastValue();
279 Point n = rot90(unit_vector(speed.lastValue()))*sign;
280 //speed.lastValue() is sometimes wrong when the path is closed: a tiny line seg might added at the end to fix rounding errors...
281 //TODO: Find a better fix!! (How do we know if the path was closed?)
282 if ( A == pwd2_in.firstValue() &&
283 speed.segs.size() > 1 &&
284 speed.segs.back()[X].size() <= 1 &&
285 speed.segs.back()[Y].size() <= 1 &&
286 speed.segs.back()[X].tailError(0) <= 1e-10 &&
287 speed.segs.back()[Y].tailError(0) <= 1e-10
288 ){
289 n = rot90(unit_vector(speed.segs[speed.segs.size()-2].at1()))*sign;
290 }
291 output.concat (ruler_mark(A, n, MARK_MAJOR));
292 }
293
294 return output;
295}
296
297} //namespace LivePathEffect
298} /* namespace Inkscape */
299
300/*
301 Local Variables:
302 mode:c++
303 c-file-style:"stroustrup"
304 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
305 indent-tabs-mode:nil
306 fill-column:99
307 End:
308*/
309// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
Adaptor that creates 2D functions from 1D ones.
Definition d2.h:55
Sequence of subpaths.
Definition pathvector.h:122
void push_back(Path const &path)
Append a path at the end.
Definition pathvector.h:172
Function defined as discrete pieces.
Definition piecewise.h:71
output_type lastValue() const
Definition piecewise.h:109
output_type firstValue() const
Definition piecewise.h:106
void concat(const Piecewise< T > &other)
Definition piecewise.h:235
std::vector< T > segs
Definition piecewise.h:76
Two-dimensional point that doubles as a vector.
Definition point.h:66
Rotation around the origin.
Definition transforms.h:187
Polynomial in symmetric power basis.
Definition sbasis.h:70
Translation by a vector.
Definition transforms.h:115
Translate inverse() const
Get the inverse translation.
Definition transforms.h:133
void registerParameter(Parameter *param)
Definition effect.cpp:1704
void param_setValue(Glib::ustring newvalue, bool write=false)
Definition hidden.cpp:71
Glib::ustring param_getSVGValue() const override
Definition hidden.cpp:53
void doOnApply(SPLPEItem const *lpeitem) override
Is performed a single time when the effect is freshly applied to a path.
Geom::Piecewise< Geom::D2< Geom::SBasis > > ruler_mark(Geom::Point const &A, Geom::Point const &n, MarkType const &marktype)
Definition lpe-ruler.cpp:94
Geom::Piecewise< Geom::D2< Geom::SBasis > > doEffect_pwd2(Geom::Piecewise< Geom::D2< Geom::SBasis > > const &pwd2_in) override
EnumParam< MarkDirType > mark_dir
Definition lpe-ruler.h:63
LPERuler(LivePathEffectObject *lpeobject)
Definition lpe-ruler.cpp:38
EnumParam< BorderMarkType > border_marks
Definition lpe-ruler.h:65
void param_set_digits(unsigned digits)
void param_set_range(double min, double max)
void param_set_increments(double step, double page)
const gchar * get_abbreviation() const
Definition unit.cpp:77
Simplified management of enumerations of svg items with UI labels.
Definition enums.h:42
Unit const * unit
Definition units.h:104
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Definition units.cpp:588
Glib::ustring abbr
Definition units.h:81
Typed SVG document implementation.
Definition document.h:101
Inkscape::Util::Quantity getWidth() const
Definition document.cpp:848
Geom::Scale getDocumentScale(bool computed=true) const
Returns document scale as defined by width/height (in pixels) and viewBox (real world to user-units).
Definition document.cpp:764
Inkscape::Util::Unit const * getDisplayUnit()
guaranteed not to return nullptr
Definition document.cpp:732
RootCluster root
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
LPE <ruler> implementation, see lpe-ruler.cpp.
double offset
Various utility functions.
Definition affine.h:22
Piecewise< D2< SBasis > > paths_to_pw(PathVector const &paths)
Definition path.cpp:1123
static float sign(double number)
Returns +1 for positive numbers, -1 for negative numbers, and 0 otherwise.
SBasisOf< T > shift(SBasisOf< T > const &a, int sh)
Definition sbasis-of.h:435
PathVector path_from_piecewise(Piecewise< D2< SBasis > > const &B, double tol, bool only_cubicbeziers=false)
Make a path from a d2 sbasis.
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
std::vector< std::vector< double > > multi_roots(SBasis const &f, std::vector< double > const &levels, double htol=1e-7, double vtol=1e-7, double a=0, double b=1)
Point unit_vector(Point const &a)
Piecewise< SBasis > arcLengthSb(D2< SBasis > const &M, double tol=.01)
D2< T > rot90(D2< T > const &a)
Definition d2.h:397
static const Util::EnumDataConverter< MarkDirType > MarkDirTypeConverter(MarkDirData, sizeof(MarkDirData)/sizeof(*MarkDirData))
static const Util::EnumData< BorderMarkType > BorderMarkData[]
Definition lpe-ruler.cpp:30
static const Util::EnumDataConverter< BorderMarkType > BorderMarkTypeConverter(BorderMarkData, sizeof(BorderMarkData)/sizeof(*BorderMarkData))
static const Util::EnumData< MarkDirType > MarkDirData[]
Definition lpe-ruler.cpp:23
Helper class to stream background task notifications as a series of messages.
Conversion between SBasis and Bezier basis polynomials.
Simplified management of enumerations of svg items with UI labels.
Definition enums.h:27