Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-taperstroke.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
15#include "lpe-taperstroke.h"
16
17#include <glibmm/i18n.h>
18
20
21#include "preferences.h"
22#include "style.h"
23
24#include "helper/geom.h"
28#include "object/sp-shape.h"
29#include "svg/svg.h"
30#include "ui/knot/knot-holder.h"
32
33template<typename T>
34inline bool withinRange(T value, T low, T high) {
35 return (value > low && value < high);
36}
37
38namespace Inkscape {
39namespace LivePathEffect {
40
41namespace TpS {
42 class KnotHolderEntityAttachBegin : public LPEKnotHolderEntity {
43 public:
44 KnotHolderEntityAttachBegin(LPETaperStroke * effect, size_t index)
45 : LPEKnotHolderEntity(effect)
46 , _effect(effect)
47 , _index(index) {};
48 void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) override;
49 void knot_click(guint state) override;
50 Geom::Point knot_get() const override;
51 bool valid_index(unsigned int index) const {
52 return (_effect->attach_start._vector.size() > index);
53 };
54 private:
55 size_t _index;
56 LPETaperStroke * _effect;
57 };
58
59 class KnotHolderEntityAttachEnd : public LPEKnotHolderEntity {
60 public:
61 KnotHolderEntityAttachEnd(LPETaperStroke * effect, size_t index)
62 : LPEKnotHolderEntity(effect)
63 , _effect(effect)
64 , _index(index) {};
65 void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) override;
66 void knot_click(guint state) override;
67 Geom::Point knot_get() const override;
68 bool valid_index(unsigned int index) const {
69 return (_effect->attach_end._vector.size() > index);
70 };
71 private:
72 size_t _index;
73 LPETaperStroke * _effect;
74 };
75} // TpS
76
78 // clang-format off
79 {JOIN_BEVEL, N_("Beveled"), "bevel"},
80 {JOIN_ROUND, N_("Rounded"), "round"},
81 {JOIN_MITER, N_("Miter"), "miter"},
82 {JOIN_EXTRAPOLATE, N_("Extrapolated"), "extrapolated"},
83 // clang-format on
84};
85
93
95 {TAPER_CENTER, N_("Center"), "center"},
96 {TAPER_LEFT, N_("Left"), "left"},
97 {TAPER_RIGHT, N_("Right"), "right"},
98 {TAPER_CLAMP, N_("Clamp"), "clamp"},
99};
100
103
105 Effect(lpeobject),
106 subpath(_("Select subpath:"), _("Select the subpath you want to modify"), "subpath", &wr, this, 1.),
107 line_width(_("Stroke width:"), _("The (non-tapered) width of the path"), "stroke_width", &wr, this, 1.),
108 attach_start(_("Start offset:"), _("Taper distance from path start"), "attach_start", &wr, this, 0.2),
109 attach_end(_("End offset:"), _("The ending position of the taper"), "end_offset", &wr, this, 0.2),
110 start_smoothing(_("Start smoothing:"), _("Amount of smoothing to apply to the start taper"), "start_smoothing", &wr, this, 0.5),
111 end_smoothing(_("End smoothing:"), _("Amount of smoothing to apply to the end taper"), "end_smoothing", &wr, this, 0.5),
112 join_type(_("Join type:"), _("Join type for non-smooth nodes"), "jointype", JoinTypeConverter, &wr, this, JOIN_EXTRAPOLATE),
113 start_shape(_("Start direction:"), _("Direction of the taper at the path start"), "start_shape", TaperShapeTypeConverter, &wr, this, TAPER_CENTER),
114 end_shape(_("End direction:"), _("Direction of the taper at the path end"), "end_shape", TaperShapeTypeConverter, &wr, this, TAPER_CENTER),
115 miter_limit(_("Miter limit:"), _("Limit for miter joins"), "miter_limit", &wr, this, 100.)
116{
117 show_orig_path = true;
119 //backward compat
120 auto ss = this->getRepr()->attribute("start_shape");
121 auto se = this->getRepr()->attribute("end_shape");
122 if (!ss || !g_strcmp0(ss,"")){
123 this->getRepr()->setAttribute("start_shape", "center");
124 if (ss) {
125 g_warning("Your taper stroke is not set correctly in LPE id: %s, defaulting to center mode", this->getRepr()->attribute("id"));
126 }
127 };
128 if (!se || !g_strcmp0(se,"")){
129 this->getRepr()->setAttribute("end_shape", "center");
130 if (se) {
131 g_warning("Your taper stroke is not set correctly in LPE id: %s, defaulting to center mode", this->getRepr()->attribute("id"));
132 }
133 };
139
150}
151
153
154// from LPEPowerStroke -- sets fill if stroke color because we will
155// be converting to a fill to make the new join.
156
157void LPETaperStroke::transform_multiply(Geom::Affine const &postmul, bool /*set*/)
158{
160 bool transform_stroke = prefs ? prefs->getBool("/options/transform/stroke", true) : true;
161 if (transform_stroke && !sp_lpe_item->unoptimized()) {
162 line_width.param_transform_multiply(postmul, false);
163 }
164}
165
167{
168 auto lpeitem_mutable = const_cast<SPLPEItem *>(lpeitem);
169 auto item = cast<SPShape>(lpeitem_mutable);
170
171 if (!item) {
172 g_warning("It only makes sense to apply Taper stroke to paths (not groups).");
173 return;
174 }
175
177
178 double width = (lpeitem && lpeitem->style) ? lpeitem->style->stroke_width.computed : 1.;
179
181
182 Glib::ustring pref_path = (Glib::ustring)"/live_effects/" +
183 (Glib::ustring)LPETypeConverter.get_key(effectType()).c_str() +
184 (Glib::ustring)"/" +
185 (Glib::ustring)"stroke_width";
186
187 bool valid = prefs->getEntry(pref_path).isValidDouble();
188
189 if (!valid) {
191 }
192
194}
195
197{
198 auto lpeitem_mutable = const_cast<SPLPEItem *>(lpeitem);
199 auto shape = cast<SPShape>(lpeitem_mutable);
200 if (shape) {
202 }
203}
204
205using Geom::Piecewise;
206using Geom::D2;
207using Geom::SBasis;
208// leave Geom::Path
209
210static Geom::Path return_at_first_cusp(Geom::Path const & path_in, double /*smooth_tolerance*/ = 0.05)
211{
212 Geom::Path temp;
213
214 for (unsigned i = 0; i < path_in.size(); i++) {
215 temp.append(path_in[i]);
216 if (path_in.size() > i+1) {
217 if (Geom::get_nodetype(path_in[i], path_in[i + 1]) != Geom::NODE_SMOOTH ) {
218 break;
219 }
220 }
221 }
222
223 return temp;
224}
225
226Piecewise<D2<SBasis> > stretch_along(Piecewise<D2<SBasis> > pwd2_in, Geom::Path pattern, double width);
227
228// actual effect
229
234
242{
243 auto const endTime = std::max(path.size() - end, start);
244
245 Geom::Path p1 = path.portion(0., start);
246 Geom::Path p2 = path.portion(start, endTime);
247 Geom::Path p3 = path.portion(endTime, path.size());
248
250 out.push_back(p1);
251 out.push_back(p2);
252 out.push_back(p3);
253
254 return out;
255}
256
257
263Piecewise<D2<SBasis> > stretch_along(Piecewise<D2<SBasis> > pwd2_in, Geom::Path pattern, double prop_scale)
264{
265 using namespace Geom;
266
267 // Don't allow empty path parameter:
268 if ( pattern.empty() ) {
269 return pwd2_in;
270 }
271
272 /* Much credit should go to jfb and mgsloan of lib2geom development for the code below! */
273 Piecewise<D2<SBasis> > output;
274 std::vector<Piecewise<D2<SBasis> > > pre_output;
275
276 D2<Piecewise<SBasis> > patternd2 = make_cuts_independent(pattern.toPwSb());
277 Piecewise<SBasis> x0 = Piecewise<SBasis>(patternd2[0]);
278 Piecewise<SBasis> y0 = Piecewise<SBasis>(patternd2[1]);
279 OptInterval pattBndsX = bounds_exact(x0);
280 OptInterval pattBndsY = bounds_exact(y0);
281 if (pattBndsX && pattBndsY) {
282 x0 -= pattBndsX->min();
283 y0 -= pattBndsY->middle();
284
285 double noffset = 0;
286 double toffset = 0;
287 // Prevent more than 90% overlap...
288
289 y0+=noffset;
290
291 std::vector<Piecewise<D2<SBasis> > > paths_in;
292 paths_in = split_at_discontinuities(pwd2_in);
293
294 for (auto path_i : paths_in) {
295 Piecewise<SBasis> x = x0;
296 Piecewise<SBasis> y = y0;
297 Piecewise<D2<SBasis> > uskeleton = arc_length_parametrization(path_i,2,.1);
298 uskeleton = remove_short_cuts(uskeleton,.01);
299 Piecewise<D2<SBasis> > n = rot90(derivative(uskeleton));
300 n = force_continuity(remove_short_cuts(n,.1));
301
302 int nbCopies = 0;
303 double scaling = (uskeleton.domain().extent() - toffset)/pattBndsX->extent();
304 nbCopies = 1;
305
306 double pattWidth = pattBndsX->extent() * scaling;
307
308 if (scaling != 1.0) {
309 x*=scaling;
310 }
311 if ( false ) {
312 y*=(scaling*prop_scale);
313 } else {
314 if (prop_scale != 1.0) y *= prop_scale;
315 }
316 x += toffset;
317
318 double offs = 0;
319 for (int i=0; i<nbCopies; i++) {
320 if (false) {
321 Piecewise<D2<SBasis> > output_piece = compose(uskeleton,x+offs)+y*compose(n,x+offs);
322 std::vector<Piecewise<D2<SBasis> > > splited_output_piece = split_at_discontinuities(output_piece);
323 pre_output.insert(pre_output.end(), splited_output_piece.begin(), splited_output_piece.end() );
324 } else {
325 output.concat(compose(uskeleton,x+offs)+y*compose(n,x+offs));
326 }
327 offs+=pattWidth;
328 }
329 }
330 return output;
331 } else {
332 return pwd2_in;
333 }
334}
335
336void
338{
339 using namespace Geom;
341 size_t sicepv = pathv.size();
342 bool write = false;
343 if (previous_size != sicepv) {
344 subpath.param_set_range(1, sicepv);
346 if (!is_load) {
347 attach_start._vector.clear();
348 attach_end._vector.clear();
349 start_smoothing._vector.clear();
350 end_smoothing._vector.clear();
351 start_shape._vector.clear();
352 end_shape._vector.clear();
353 }
354 previous_size = sicepv;
355 }
356 if (!attach_start._vector.size()) {
357 for (auto path : pathvector_before_effect) {
358 attach_start._vector.push_back(0);
359 attach_end._vector.push_back(0);
360 start_smoothing._vector.push_back(0);
361 end_smoothing._vector.push_back(0);
362 start_shape._vector.emplace_back("center");
363 end_shape._vector.emplace_back("center");
364 }
371 write = true;
372 }
373 // Some SVGs have been made with bad smoothing vectors, correct them
374 for (int i = start_smoothing._vector.size(); i < (int)sicepv; i++) {
375 start_smoothing._vector.push_back(0.5);
376 write = true;
377 }
378 for (int i = end_smoothing._vector.size(); i < (int)sicepv; i++) {
379 end_smoothing._vector.push_back(0.5);
380 write = true;
381 }
382 if (prev_subpath != subpath) {
390 refresh_widgets = true;
391 write = true;
392 }
393 std::vector<double> attach_startv;
394 for (auto & doub : attach_start.data()) {
395 attach_startv.push_back(doub);
396 }
397 std::vector<double> attach_endv;
398 for (auto & doub : attach_end.data()) {
399 attach_endv.push_back(doub);
400 }
401 std::vector<double> start_smoothingv;
402 for (auto & doub : start_smoothing.data()) {
403 start_smoothingv.push_back(doub);
404 }
405 std::vector<double> end_smoothingv;
406 for (auto & doub : end_smoothing.data()) {
407 end_smoothingv.push_back(doub);
408 }
409 if (write) {
416 }
419 return;
420 }
421
422 size_t index = 0;
423 start_attach_point.clear();
424 end_attach_point.clear();
425 for (auto path : pathv) {
426 Geom::Path first_cusp = return_at_first_cusp(path);
427 Geom::Path last_cusp = return_at_first_cusp(path.reversed());
428
429 bool zeroStart = false; // [distance from start taper knot -> start of path] == 0
430 bool zeroEnd = false; // [distance from end taper knot -> end of path] == 0
431 bool metInMiddle = false; // knots are touching
432
433 // there is a pretty good chance that people will try to drag the knots
434 // on top of each other, so block it
435
436 unsigned size = path.size();
437 if (size == first_cusp.size()) {
438 // check to see if the knots were dragged over each other
439 // if so, reset the end offset, but still allow the start offset.
440 if ( attach_startv[index] >= (size - attach_endv[index]) ) {
441 attach_endv[index] = ( size - attach_startv[index] );
442 metInMiddle = true;
443 }
444 }
445
446 if (attach_startv[index] == size - attach_endv[index]) {
447 metInMiddle = true;
448 }
449 if (attach_endv[index] == size - attach_startv[index]) {
450 metInMiddle = true;
451 }
452
453 // don't let it be integer (TODO this is stupid!)
454 {
455 if (double(unsigned(attach_startv[index])) == attach_startv[index]) {
456 attach_startv[index] = (attach_startv[index] - 0.00001);
457 }
458 if (double(unsigned(attach_endv[index])) == attach_endv[index]) {
459 attach_endv[index] = (attach_endv[index] - 0.00001);
460 }
461 }
462
463 unsigned allowed_start = first_cusp.size();
464 unsigned allowed_end = last_cusp.size();
465
466 // don't let the knots be farther than they are allowed to be
467 {
468 if ((unsigned)attach_startv[index] >= allowed_start) {
469 attach_startv[index] = ((double)allowed_start - 0.00001);
470 }
471 if ((unsigned)attach_endv[index] >= allowed_end) {
472 attach_endv[index] = ((double)allowed_end - 0.00001);
473 }
474 }
475
476 // don't let it be zero (this is stupid too!)
477 if (attach_startv[index] < 0.0000001 || withinRange(double(attach_startv[index]), 0.00000001, 0.000001)) {
478 attach_startv[index] = ( 0.0000001 );
479 zeroStart = true;
480 }
481 if (attach_endv[index] < 0.0000001 || withinRange(double(attach_endv[index]), 0.00000001, 0.000001)) {
482 attach_endv[index] = ( 0.0000001 );
483 zeroEnd = true;
484 }
485
486 // Path::operator () means get point at time t
487 start_attach_point.push_back(first_cusp(attach_startv[index]));
488 end_attach_point.push_back(last_cusp(attach_endv[index]));
489 Geom::PathVector pathv_tmp;
490
491 // the following function just splits it up into three pieces.
492 pathv_tmp = doEffect_simplePath(path, index, attach_startv[index], attach_endv[index]);
493
494 // now for the actual tapering. the stretch_along method (stolen from PaP) is used to accomplish this
495
496 Geom::Path real_path;
497 Geom::PathVector pat_vec;
499 Geom::Path throwaway_path;
500
501 if (!zeroStart && start_shape.valid_index(index) && start_smoothingv.size() > index) {
502 // Construct the pattern
503 std::stringstream pat_str;
504 pat_str.imbue(std::locale::classic());
505 bool clamp = false;
507 case TAPER_CLAMP:
508 clamp = true;
509 break;
510 case TAPER_RIGHT:
511 pat_str << "M 1,0 Q " << 1 - (double)start_smoothingv[index] << ",0 0,1 L 1,1";
512 break;
513 case TAPER_LEFT:
514 pat_str << "M 1,0 L 0,0 Q " << 1 - (double)start_smoothingv[index] << ",1 1,1";
515 break;
516 default:
517 pat_str << "M 1,0 C " << 1 - (double)start_smoothingv[index] << ",0 0,0.5 0,0.5 0,0.5 " << 1 - (double)start_smoothingv[index] << ",1 1,1";
518 break;
519 }
520 if (!clamp) {
521 pat_vec = sp_svg_read_pathv(pat_str.str().c_str());
522 pwd2.concat(stretch_along(pathv_tmp[0].toPwSb(), pat_vec[0], fabs(line_width)));
523 throwaway_path = Geom::path_from_piecewise(pwd2, LPE_CONVERSION_TOLERANCE)[0];
524
525 real_path.append(throwaway_path);
526 }
527 }
528
529 // if this condition happens to evaluate false, i.e. there was no space for a path to be drawn, it is simply skipped.
530 // although this seems obvious, it can probably lead to bugs.
531 if (!metInMiddle) {
532 // append the outside outline of the path (goes with the direction of the path)
533 throwaway_path = half_outline(pathv_tmp[1], fabs(line_width)/2., miter_limit, static_cast<LineJoinType>(join_type.get_value()));
534 if (!zeroStart && real_path.size() >= 1 && throwaway_path.size() >= 1) {
535 if (!Geom::are_near(real_path.finalPoint(), throwaway_path.initialPoint())) {
536 real_path.appendNew<Geom::LineSegment>(throwaway_path.initialPoint());
537 } else {
538 real_path.setFinal(throwaway_path.initialPoint());
539 }
540 }
541 real_path.append(throwaway_path);
542 }
543
544 if (!zeroEnd && end_shape.valid_index(index) && end_smoothingv.size() > index) {
545 // append the ending taper
546 std::stringstream pat_str_1;
547 pat_str_1.imbue(std::locale::classic());
548 bool clamp = false;
550 case TAPER_CLAMP:
551 clamp = true;
552 break;
553 case TAPER_RIGHT:
554 pat_str_1 << "M 0,1 L 1,1 Q " << (double)end_smoothingv[index] << ",0 0,0";
555 break;
556 case TAPER_LEFT:
557 pat_str_1 << "M 0,1 Q " << (double)end_smoothingv[index] << ",1 1,0 L 0,0";
558 break;
559 default:
560 pat_str_1 << "M 0,1 C " << (double)end_smoothingv[index] << ",1 1,0.5 1,0.5 1,0.5 " << (double)end_smoothingv[index] << ",0 0,0";
561 break;
562 }
563 if (!clamp) {
564 pat_vec = sp_svg_read_pathv(pat_str_1.str().c_str());
565
566 pwd2 = Piecewise<D2<SBasis> >();
567 pwd2.concat(stretch_along(pathv_tmp[2].toPwSb(), pat_vec[0], fabs(line_width)));
568
569 throwaway_path = Geom::path_from_piecewise(pwd2, LPE_CONVERSION_TOLERANCE)[0];
570 if (!Geom::are_near(real_path.finalPoint(), throwaway_path.initialPoint()) && real_path.size() >= 1) {
571 real_path.appendNew<Geom::LineSegment>(throwaway_path.initialPoint());
572 } else {
573 real_path.setFinal(throwaway_path.initialPoint());
574 }
575 real_path.append(throwaway_path);
576 }
577 }
578
579 if (!metInMiddle) {
580 // append the inside outline of the path (against direction)
581 throwaway_path = half_outline(pathv_tmp[1].reversed(), fabs(line_width)/2., miter_limit, static_cast<LineJoinType>(join_type.get_value()));
582
583 if (!Geom::are_near(real_path.finalPoint(), throwaway_path.initialPoint()) && real_path.size() >= 1) {
584 real_path.appendNew<Geom::LineSegment>(throwaway_path.initialPoint());
585 } else {
586 real_path.setFinal(throwaway_path.initialPoint());
587 }
588 real_path.append(throwaway_path);
589 }
590
591 if (!Geom::are_near(real_path.finalPoint(), real_path.initialPoint())) {
592 real_path.appendNew<Geom::LineSegment>(real_path.initialPoint());
593 } else {
594 real_path.setFinal(real_path.initialPoint());
595 }
596 real_path.close();
597
598 pathv_out.push_back(real_path);
599 index++;
600 }
601 /* start_smoothing.param_set_and_write_new_value(start_smoothingv);
602 end_smoothing.param_set_and_write_new_value(end_smoothingv);
603 attach_start.param_set_and_write_new_value(attach_startv);
604 attach_end.param_set_and_write_new_value(attach_endv); */
605 start_smoothingv.clear();
606 end_smoothingv.clear();
607 attach_startv.clear();
608 attach_endv.clear();
609}
610
612{
613 for (size_t i = 0 ; i < attach_start._vector.size(); i++) {
615 e->create(nullptr, item, knotholder, Inkscape::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:TaperStrokeBegin",
616 _("<b>Start point of the taper</b>: drag to alter the taper, <b>Shift+click</b> changes the taper direction"));
617 knotholder->add(e);
618
620 f->create(nullptr, item, knotholder, Inkscape::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:TaperStrokeEnd",
621 _("<b>End point of the taper</b>: drag to alter the taper, <b>Shift+click</b> changes the taper direction"));
622 knotholder->add(f);
623 }
624}
625
626namespace TpS {
627
628void KnotHolderEntityAttachBegin::knot_set(Geom::Point const &p, Geom::Point const&/*origin*/, guint state)
629{
630 using namespace Geom;
631
632 if (!valid_index(_index) || _effect->start_attach_point.size() <= _index) {
633 return;
634 }
635
636 Geom::Point const s = snap_knot_position(p, state);
637
638 if (!is<SPShape>(_effect->sp_lpe_item)) {
639 g_warning("LPEItem is not a path!");
640 return;
641 }
642
643 if (!cast_unsafe<SPShape>(_effect->sp_lpe_item)->curve()) {
644 // oops
645 return;
646 }
647 // in case you are wondering, the above are simply sanity checks. we never want to actually
648 // use that object.
649
650 Geom::PathVector pathv = _effect->pathvector_before_effect;
652 Geom::Path p_in = return_at_first_cusp(pathv[_index]);
653 pwd2.concat(p_in.toPwSb());
654
655 double t0 = nearest_time(s, pwd2);
656 _effect->attach_start._vector[_index] = t0;
657 _effect->attach_start.write_to_SVG();
658}
659
660void KnotHolderEntityAttachEnd::knot_set(Geom::Point const &p, Geom::Point const& /*origin*/, guint state)
661{
662 using namespace Geom;
663
664 if (!valid_index(_index) || _effect->end_attach_point.size() <= _index) {
665 return;
666 }
667
668 Geom::Point const s = snap_knot_position(p, state);
669
670 if (!is<SPShape>(_effect->sp_lpe_item)) {
671 g_warning("LPEItem is not a path!");
672 return;
673 }
674
675 if (!cast_unsafe<SPShape>(_effect->sp_lpe_item)->curve()) {
676 // oops
677 return;
678 }
679 Geom::PathVector pathv = _effect->pathvector_before_effect;
680 Geom::Path p_in = return_at_first_cusp(pathv[_index].reversed());
681 Piecewise<D2<SBasis>> pwd2 = p_in.toPwSb();
682
683 double t0 = nearest_time(s, pwd2);
684 _effect->attach_end._vector[_index] = t0;
685 _effect->attach_end.write_to_SVG();
686}
687
688void KnotHolderEntityAttachBegin::knot_click(guint state)
689{
690 using namespace Geom;
691 if (!(state & GDK_SHIFT_MASK)) {
692 return;
693 }
694
695 if (!valid_index(_index) || _effect->start_attach_point.size() <= _index) {
696 return;
697 }
698
699 _effect->start_shape._vector[_index] = TaperShapeTypeConverter.get_key((TaperShapeTypeConverter.get_id_from_key(_effect->start_shape._vector[_index]) + 1) % LAST_SHAPE);
700 _effect->start_shape.write_to_SVG();
701}
702
703void KnotHolderEntityAttachEnd::knot_click(guint state)
704{
705 using namespace Geom;
706 if (!(state & GDK_SHIFT_MASK)) {
707 return;
708 }
709
710 if (!valid_index(_index) || _effect->end_attach_point.size() <= _index) {
711 return;
712 }
713
714 _effect->end_shape._vector[_index] = TaperShapeTypeConverter.get_key((TaperShapeTypeConverter.get_id_from_key(_effect->end_shape._vector[_index]) + 1) % LAST_SHAPE);
715 _effect->end_shape.write_to_SVG();
716}
717
718Geom::Point KnotHolderEntityAttachBegin::knot_get() const
719{
720 if (!valid_index(_index)) {
721 return Geom::Point();
722 }
723 if (_effect && _effect->start_attach_point.size() > _index) {
724 return _effect->start_attach_point[_index];
725 }
726 return Geom::Point();
727}
728
729Geom::Point KnotHolderEntityAttachEnd::knot_get() const
730{
731 if (!valid_index(_index)) {
732 return Geom::Point();
733 }
734 if (_effect && _effect->end_attach_point.size() > _index) {
735 return _effect->end_attach_point[_index];
736 }
737 return Geom::Point();
738}
739
740} // namespace TpS
741} // namespace LivePathEffect
742} // namespace Inkscape
743
744/*
745 Local Variables:
746 mode:c++
747 c-file-style:"stroustrup"
748 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
749 indent-tabs-mode:nil
750 fill-column:99
751 End:
752*/
753// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 :
JoinType
Definition LivarotDefs.h:61
Point origin
Definition aa.cpp:227
3x3 matrix representing an affine transformation.
Definition affine.h:70
Adaptor that creates 2D functions from 1D ones.
Definition d2.h:55
constexpr C extent() const
Range of real numbers that can be empty.
Definition interval.h:199
Sequence of subpaths.
Definition pathvector.h:122
size_type size() const
Get the number of paths in the vector.
Definition pathvector.h:147
void push_back(Path const &path)
Append a path at the end.
Definition pathvector.h:172
void clear()
Remove all paths from the vector.
Definition pathvector.h:195
bool empty() const
Check whether the vector contains any paths.
Definition pathvector.h:145
Sequence of contiguous curves, aka spline.
Definition path.h:353
Point finalPoint() const
Get the last point in the path.
Definition path.h:709
void close(bool closed=true)
Set whether the path is closed.
Definition path.cpp:322
bool empty() const
Check whether path is empty.
Definition path.h:500
Piecewise< D2< SBasis > > toPwSb() const
Definition path.cpp:388
Path portion(Coord f, Coord t) const
Definition path.h:645
void append(Curve *curve)
Add a new curve to the end of the path.
Definition path.h:750
Point initialPoint() const
Get the first point in the path.
Definition path.h:705
void setFinal(Point const &p)
Definition path.h:740
size_type size() const
Natural size of the path.
Definition path.h:490
void appendNew(Args &&... args)
Append a new curve to the path.
Definition path.h:804
Function defined as discrete pieces.
Definition piecewise.h:71
Interval domain() const
Definition piecewise.h:215
void concat(const Piecewise< T > &other)
Definition piecewise.h:235
Two-dimensional point that doubles as a vector.
Definition point.h:66
Polynomial in symmetric power basis.
Definition sbasis.h:70
void param_set_and_write_new_value(std::vector< StorageType > const &new_vector)
Definition array.h:92
std::vector< StorageType > _vector
Definition array.h:104
bool valid_index(int index) const
Definition array.h:99
std::vector< StorageType > const & data() const
Definition array.h:48
void registerParameter(Parameter *param)
Definition effect.cpp:1704
Inkscape::XML::Node * getRepr()
Definition effect.cpp:1928
Geom::PathVector pathvector_before_effect
Definition effect.h:173
EffectType effectType() const
Definition effect.cpp:1182
void doBeforeEffect(SPLPEItem const *lpeitem) override
Is performed each time before the effect is updated.
LPETaperStroke(LivePathEffectObject *lpeobject)
Geom::PathVector doEffect_simplePath(Geom::Path const &path, size_t index, double start, double end)
void transform_multiply(Geom::Affine const &postmul, bool set) override
Overridden function to apply transforms for example to powerstroke, jointtype or tapperstroke.
void doOnApply(SPLPEItem const *lpeitem) override
Is performed a single time when the effect is freshly applied to a path.
void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) override
std::vector< Geom::Point > end_attach_point
Geom::PathVector doEffect_path(Geom::PathVector const &path_in) override
std::vector< Geom::Point > start_attach_point
void doOnRemove(SPLPEItem const *lpeitem) override
void param_set_digits(unsigned digits)
void param_transform_multiply(Geom::Affine const &postmul, bool set) override
void param_set_range(double min, double max)
bool param_readSVGValue(char const *strvalue) override
void param_set_increments(double step, double page)
bool isValidDouble() const
Check if the preference value can be interpreted as a floating point value.
Preference storage class.
Definition preferences.h:61
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
static Preferences * get()
Access the singleton Preferences object.
Entry const getEntry(Glib::ustring const &pref_path)
Retrieve a preference entry without specifying its type.
Simplified management of enumerations of svg items with UI labels.
Definition enums.h:42
E get_id_from_key(const Glib::ustring &key) const
Definition enums.h:60
const Glib::ustring & get_key(const E id) const
Definition enums.h:99
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
KnotHolderEntity definition.
void create(SPDesktop *desktop, SPItem *item, KnotHolder *parent, Inkscape::CanvasItemCtrlType type=Inkscape::CANVAS_ITEM_CTRL_TYPE_DEFAULT, Glib::ustring const &name="unknown", char const *tip="", uint32_t color=0xffffff00)
void add(KnotHolderEntity *e)
Base class for visual SVG elements.
Definition sp-item.h:109
bool unoptimized()
Definition sp-item.cpp:1661
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
T< SPAttr::STROKE_WIDTH, SPILength > stroke_width
stroke-width
Definition style.h:249
Fill/stroke conversion routines for LPEs which draw a stroke.
Specific nodetype geometry functions for Inkscape, not provided my lib2geom.
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.
SPItem * item
bool withinRange(T value, T low, T high)
Taper Stroke path effect (meant as a replacement for using Power Strokes for tapering)
Geom::Point start
Geom::Point end
Various utility functions.
Definition affine.h:22
D2< Piecewise< SBasis > > make_cuts_independent(Piecewise< D2< SBasis > > const &a)
Definition d2-sbasis.cpp:75
OptInterval bounds_exact(Bezier const &b)
Definition bezier.cpp:310
Coord nearest_time(Point const &p, Curve const &c)
Definition curve.h:354
std::vector< Geom::Piecewise< Geom::D2< Geom::SBasis > > > split_at_discontinuities(Geom::Piecewise< Geom::D2< Geom::SBasis > > const &pwsbin, double tol=.0001)
@ NODE_SMOOTH
This node continuously joins two segments, with continuous unit tangent.
NodeType get_nodetype(Curve const &c_incoming, Curve const &c_outgoing)
PathVector path_from_piecewise(Piecewise< D2< SBasis > > const &B, double tol, bool only_cubicbeziers=false)
Make a path from a d2 sbasis.
D2< T > compose(D2< T > const &a, T const &b)
Definition d2.h:405
Bezier derivative(Bezier const &a)
Definition bezier.cpp:282
Piecewise< D2< SBasis > > arc_length_parametrization(D2< SBasis > const &M, unsigned order=3, double tol=.01)
Piecewise< D2< SBasis > > force_continuity(Piecewise< D2< SBasis > > const &f, double tol=0, bool closed=false)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
D2< T > rot90(D2< T > const &a)
Definition d2.h:397
void lpe_shape_convert_stroke_and_fill(SPShape *shape)
Prepares a SPShape's fill and stroke for use in a path effect by setting the existing stroke properti...
void lpe_shape_revert_stroke_and_fill(SPShape *shape, double width)
Applies the fill of the SPShape to its stroke, sets the stroke width to the provided parameter,...
Piecewise< D2< SBasis > > stretch_along(Piecewise< D2< SBasis > > pwd2_in, Geom::Path pattern, double width)
Most of the below function is verbatim from Pattern Along Path.
static const Util::EnumDataConverter< unsigned > JoinTypeConverter(JoinTypeData, sizeof(JoinTypeData)/sizeof(*JoinTypeData))
const EnumEffectDataConverter< EffectType > LPETypeConverter
defined in effect.cpp
static Geom::Path return_at_first_cusp(Geom::Path const &path_in, double=0.05)
static const Util::EnumDataConverter< unsigned > TaperShapeTypeConverter(TaperShapeType, sizeof(TaperShapeType)/sizeof(*TaperShapeType))
static const Util::EnumData< unsigned > TaperShapeType[]
Helper class to stream background task notifications as a series of messages.
@ CANVAS_ITEM_CTRL_TYPE_LPE
Geom::Path half_outline(Geom::Path const &input, double width, double miter, LineJoinType join, double tolerance)
Offset the input path by width.
Singleton class to access the preferences file in a convenient way.
Conversion between SBasis and Bezier basis polynomials.
Simplified management of enumerations of svg items with UI labels.
Definition enums.h:27
SPStyle - a style object for SPItem objects.
Geom::PathVector sp_svg_read_pathv(char const *str)
Definition svg-path.cpp:37
int index
double width