Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-measure-segments.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Author(s):
4 * Jabiertxo Arraiza Cenoz <jabier.arraiza@marker.es>
5 * Some code and ideas migrated from dimensioning.py by
6 * Johannes B. Rutzmoser, johannes.rutzmoser (at) googlemail (dot) com
7 * https://github.com/Rutzmoser/inkscape_dimensioning
8 *
9 * Copyright (C) 2014 Author(s)
10 *
11 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12 */
13
15
16#include <cmath>
17#include <iomanip>
18
19#include <glibmm/i18n.h>
20#include <gtkmm/box.h>
21#include <gtkmm/notebook.h>
22
23#include <2geom/affine.h>
24#include <2geom/angle.h>
25#include <2geom/point.h>
26#include <2geom/ray.h>
27
28#include <pangomm/fontdescription.h>
29
30#include "document.h"
31#include "document-undo.h"
32#include "preferences.h"
33
34#include "display/curve.h"
35#include "helper/geom.h"
40#include "object/sp-defs.h"
41#include "object/sp-flowtext.h"
43#include "object/sp-item.h"
44#include "object/sp-path.h"
45#include "object/sp-root.h"
46#include "object/sp-shape.h"
47#include "object/sp-text.h"
48#include "svg/stringstream.h"
49#include "svg/svg.h"
50#include "text-editing.h"
51#include "ui/pack.h"
52#include "util/safe-printf.h"
53#include "util/units.h"
55#include "xml/node.h"
56#include "xml/sp-css-attr.h"
57
58
59using namespace Geom;
60namespace Inkscape {
61namespace LivePathEffect {
62
63
65 { OM_HORIZONTAL , N_("Horizontal"), "horizontal" },
66 { OM_VERTICAL , N_("Vertical") , "vertical" },
67 { OM_PARALLEL , N_("Parallel") , "parallel" }
68};
70
72 Effect(lpeobject),
73 unit(_("Unit"), _("Unit of measurement"), "unit", &wr, this, "mm"),
74 orientation(_("Orientation"), _("Orientation of the line and labels"), "orientation", OMConverter, &wr, this, OM_PARALLEL, false),
75 coloropacity(_("Color and opacity"), _("Set color and opacity of the dimensions"), "coloropacity", &wr, this, Colors::Color(0x000000ff)),
76 fontbutton(_("Font"), _("Select font for labels"), "fontbutton", &wr, this),
77 precision(_("Precision"), _("Number of digits after the decimal point"), "precision", &wr, this, 2),
78 fix_overlaps(_("Merge overlaps °"), _("Minimum angle at which overlapping dimension lines are merged into one, use 180° to disable merging"), "fix_overlaps", &wr, this, 0),
79 position(_("Position"), _("Distance of dimension line from the path"), "position", &wr, this, 5),
80 text_top_bottom(_("Label position"), _("Distance of the labels from the dimension line"), "text_top_bottom", &wr, this, 0),
81 helpline_distance(_("Help line distance"), _("Distance of the perpendicular lines from the path"), "helpline_distance", &wr, this, 0.0),
82 helpline_overlap(_("Help line elongation"), _("Distance of the perpendicular lines' ends from the dimension line"), "helpline_overlap", &wr, this, 2.0),
83 line_width(_("Line width"), _("Dimension line width in mm. DIN standard: 0.25 or 0.35 mm"), "line_width", &wr, this, 0.25),
84 scale(_("Scale"), _("Scaling factor"), "scale", &wr, this, 1.0),
85
86 // TRANSLATORS: Don't translate "{measure}" and "{unit}" variables.
87 format(_("Label format"), _("Label text format, available variables: {measure}, {unit}"), "format", &wr, this,"{measure}{unit}"),
88 blacklist(_("Blacklist segments"), _("Comma-separated list of indices of segments that should not be measured. You can use another LPE with different parameters to measure these."), "blacklist", &wr, this,""),
89 whitelist(_("Invert blacklist"), _("Use the blacklist as whitelist"), "whitelist", &wr, this, false),
90 showindex(_("Show segment index"), _("Display the index of the segments in the text label for easier blacklisting"), "showindex", &wr, this, false),
91 arrows_outside(_("Arrows outside"), _("Draw arrows pointing in the opposite direction outside the dimension line"), "arrows_outside", &wr, this, false),
92 flip_side(_("Flip side"), _("Draw dimension lines and labels on the other side of the path"), "flip_side", &wr, this, false),
93 scale_sensitive(_("Scale sensitive"), _("When the path is grouped and the group is then scaled, adjust the dimensions."), "scale_sensitive", &wr, this, true),
94 local_locale(_("Localize number format"), _("Use localized number formatting, e.g. '1,0' instead of '1.0' with German locale"), "local_locale", &wr, this, true),
95 rotate_anotation(_("Rotate labels"), _("Labels are parallel to the dimension line"), "rotate_anotation", &wr, this, true),
96 hide_back(_("Hide line under label"), _("Hide the dimension line where the label overlaps it"), "hide_back", &wr, this, true),
97 hide_arrows(_("Hide arrows"), _("Don't show any arrows"), "hide_arrows", &wr, this, false),
98 // active for 1.1
99 smallx100(_("Multiply values &lt; 1"), _("Multiply values smaller than 1 by 100 and leave out the unit"), "smallx100", &wr, this, false),
100 linked_items(_("Linked objects:"), _("Objects whose nodes are projected onto the path and generate new measurements"), "linked_items", &wr, this, true),
101 distance_projection(_("Distance"), _("Distance of the dimension lines from the outermost node (mm)"), "distance_projection", &wr, this, 20.0),
102 angle_projection(_("Angle of projection"), _("Angle of projection in 90° steps"), "angle_projection", &wr, this, 0.0),
103 active_projection(_("Activate projection"), _("Activate projection mode"), "active_projection", &wr, this, false),
104 avoid_overlapping(_("Avoid label overlap"), _("Rotate labels if the segment is shorter than the label"), "avoid_overlapping", &wr, this, true),
105 onbbox(_("Measure bounding box"), _("Add measurements for the geometrical bounding box"), "onbbox", &wr, this, false),
106 bboxonly(_("Only bounding box"), _("Measure only the geometrical bounding box"), "bboxonly", &wr, this, false),
107 centers(_("Add object center"), _("Add the projected object center"), "centers", &wr, this, false),
108 maxmin(_("Only max and min"), _("Compute only max/min projection values"), "maxmin", &wr, this, false),
109 helpdata(_("Help"), _("Measure segments help"), "helpdata", &wr, this, "", "")
110 , color(0x000000ff)
111{
112 //set to true the parameters you want to be changed his default values
137 // active for 1.1
149
150 Glib::ustring format_value = prefs->getString("/live_effects/measure-line/format");
151 if(format_value.empty()){
152 format_value = "{measure}{unit}";
153 }
154 format.param_update_default(format_value.c_str());
155
166 position.param_set_range(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max());
169 scale.param_set_range(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max());
172 text_top_bottom.param_set_range(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max());
175 line_width.param_set_range(0, std::numeric_limits<double>::max());
178 helpline_distance.param_set_range(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max());
181 helpline_overlap.param_set_range(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max());
184 distance_projection.param_set_range(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max());
190 locale_base = strdup(setlocale(LC_NUMERIC, nullptr));
191 previous_size = 0;
192 pagenumber = 0;
193 anotation_width = 0;
194 fontsize = 0;
195 arrow_gap = 0;
196}
197
198Gtk::Widget *
200{
201 // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
202 auto const vbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
203
204 auto const vbox0 = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 2);
205 vbox0->set_margin(5);
206
207 auto const vbox1 = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 2);
208 vbox1->set_margin(5);
209
210 auto const vbox2 = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 2);
211 vbox2->set_margin(5);
212
213 //Help page
214 auto const vbox3 = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 2);
215 vbox3->set_margin(5);
216
217 std::vector<Parameter *>::iterator it = param_vector.begin();
218 while (it != param_vector.end()) {
219 if ((*it)->widget_is_visible) {
220 Parameter * param = *it;
221 Gtk::Widget * widg = param->param_newWidget();
222
223 if (widg) {
224 if ( param->param_key == "linked_items") {
225 UI::pack_start(*vbox1, *widg, true, true, 2);
226 } else if (param->param_key == "active_projection" ||
227 param->param_key == "distance_projection" ||
228 param->param_key == "angle_projection" ||
229 param->param_key == "maxmin" ||
230 param->param_key == "centers" ||
231 param->param_key == "bboxonly" ||
232 param->param_key == "onbbox" )
233 {
234 UI::pack_start(*vbox1, *widg, false, true, 2);
235 } else if (param->param_key == "precision" ||
236 param->param_key == "coloropacity" ||
237 param->param_key == "font" ||
238 param->param_key == "format" ||
239 param->param_key == "blacklist" ||
240 param->param_key == "whitelist" ||
241 param->param_key == "showindex" ||
242 param->param_key == "local_locale" ||
243 param->param_key == "hide_arrows" )
244 {
245 UI::pack_start(*vbox2, *widg, false, true, 2);
246 } else if (param->param_key == "helpdata") {
247 UI::pack_start(*vbox3, *widg, false, true, 2);
248 } else {
249 UI::pack_start(*vbox0, *widg, false, true, 2);
250 }
251
252 if (auto const tip = param->param_getTooltip()) {
253 widg->set_tooltip_markup(*tip);
254 } else {
255 widg->set_tooltip_text("");
256 widg->set_has_tooltip(false);
257 }
258 }
259 }
260
261 ++it;
262 }
263
264 auto const notebook = Gtk::make_managed<Gtk::Notebook>();
265 notebook->append_page (*vbox0, Glib::ustring(_("General")));
266 notebook->append_page (*vbox1, Glib::ustring(_("Projection")));
267 notebook->append_page (*vbox2, Glib::ustring(_("Options")));
268 notebook->append_page (*vbox3, Glib::ustring(_("Help")));
269 UI::pack_start(*vbox, *notebook, true, true, 2);
270 notebook->set_current_page(pagenumber);
271 notebook->signal_switch_page().connect(sigc::mem_fun(*this, &LPEMeasureSegments::on_my_switch_page));
272 return vbox;
273}
274
275void
276LPEMeasureSegments::on_my_switch_page(Gtk::Widget* page, guint page_number)
277{
278 if(!page->get_parent()->in_destruction()) {
279 pagenumber = page_number;
280 }
281}
282
283void
285{
286 SPDocument *document = getSPDoc();
287 if (!document || !sp_lpe_item|| !sp_lpe_item->getId()) {
288 return;
289 }
290 Glib::ustring lpobjid = this->lpeobj->getId();
291 Glib::ustring itemid = sp_lpe_item->getId();
292
293 auto const css = sp_repr_css_attr_new();
294 sp_repr_css_set_property_string(css, "fill", "context-stroke");
295 sp_repr_css_set_property_double(css, "fill-opacity", coloropacity.get_value()->getOpacity());
296 sp_repr_css_set_property_string(css, "stroke", "none");
297
298 Inkscape::XML::Document *xml_doc = document->getReprDoc();
299 SPObject *elemref = nullptr;
300 Inkscape::XML::Node *arrow = nullptr;
301 if ((elemref = document->getObjectById(mode.c_str()))) {
302 Inkscape::XML::Node *arrow= elemref->getRepr();
303 if (arrow) {
304 arrow->setAttribute("sodipodi:insensitive", "true");
305 arrow->removeAttribute("transform");
306 Inkscape::XML::Node *arrow_data = arrow->firstChild();
307 if (arrow_data) {
308 arrow_data->removeAttribute("transform");
309 sp_repr_css_change(arrow_data, css, "style");
310 }
311 }
312 } else {
313 arrow = xml_doc->createElement("svg:marker");
314 arrow->setAttribute("id", mode);
315 Glib::ustring classarrow = itemid;
316 classarrow += " ";
317 classarrow += lpobjid;
318 classarrow += " measure-arrow-marker";
319 arrow->setAttribute("class", classarrow);
320 arrow->setAttributeOrRemoveIfEmpty("inkscape:stockid", mode);
321 arrow->setAttribute("orient", "auto");
322 arrow->setAttribute("refX", "0.0");
323 arrow->setAttribute("refY", "0.0");
324
325 arrow->setAttribute("sodipodi:insensitive", "true");
326 /* Create <path> */
327 Inkscape::XML::Node *arrow_path = xml_doc->createElement("svg:path");
328 if (std::strcmp(mode.c_str(), "ArrowDIN-start") == 0) {
329 arrow_path->setAttribute("d", "M -8,0 8,-2.11 8,2.11 z");
330 } else if (std::strcmp(mode.c_str(), "ArrowDIN-end") == 0) {
331 arrow_path->setAttribute("d", "M 8,0 -8,2.11 -8,-2.11 z");
332 } else if (std::strcmp(mode.c_str(), "ArrowDINout-start") == 0) {
333 arrow_path->setAttribute("d", "M 0,0 -16,2.11 -16,0.5 -26,0.5 -26,-0.5 -16,-0.5 -16,-2.11 z");
334 } else {
335 arrow_path->setAttribute("d", "M 0,0 16,-2.11 16,-0.5 26,-0.5 26,0.5 16,0.5 16,2.11 z");
336 }
337 Glib::ustring classarrowpath = itemid;
338 classarrowpath += " ";
339 classarrowpath += lpobjid;
340 classarrowpath += " measure-arrow";
341 arrow_path->setAttributeOrRemoveIfEmpty("class", classarrowpath);
342 Glib::ustring arrowpath = mode + Glib::ustring("_path");
343 arrow_path->setAttribute("id", arrowpath);
344 sp_repr_css_change(arrow_path, css, "style");
345 arrow->addChild(arrow_path, nullptr);
346 Inkscape::GC::release(arrow_path);
347 elemref = document->getDefs()->appendChildRepr(arrow);
349 }
351 items.push_back(mode);
352}
353
354void
356{
357 SPDocument *document = getSPDoc();
358 if (!document || !sp_lpe_item || !sp_lpe_item->getId()) {
359 return;
360 }
361 Inkscape::XML::Document *xml_doc = document->getReprDoc();
362 Glib::ustring lpobjid = this->lpeobj->getId();
363 Glib::ustring itemid = sp_lpe_item->getId();
364 Glib::ustring id = Glib::ustring("text-on-");
366 id += "-";
367 id += lpobjid;
368 SPObject *elemref = nullptr;
369 Inkscape::XML::Node *rtext = nullptr;
370 Inkscape::XML::Node *rtspan = nullptr;
371 Inkscape::XML::Node *rstring = nullptr;
372 elemref = document->getObjectById(id.c_str());
373 if (elemref) {
374 rtext = elemref->getRepr();
375 rtext->setAttributeSvgDouble("x", pos[Geom::X]);
376 rtext->setAttributeSvgDouble("y", pos[Geom::Y]);
377 rtext->setAttribute("sodipodi:insensitive", "true");
378 rtext->removeAttribute("transform");
379 rtspan = rtext->firstChild();
380 rstring = rtspan->firstChild();
381 rtspan->removeAttribute("x");
382 rtspan->removeAttribute("y");
383 Glib::ustring classlabel = itemid;
384 classlabel += " ";
385 classlabel += lpobjid;
386 classlabel += " measure-label";
387 rtext->setAttribute("class", classlabel);
388 } else {
389 rtext = xml_doc->createElement("svg:text");
390 rtext->setAttribute("xml:space", "preserve");
391 rtext->setAttribute("id", id);
392 Glib::ustring classlabel = itemid;
393 classlabel += " ";
394 classlabel += lpobjid;
395 classlabel += " measure-label";
396 rtext->setAttribute("class", classlabel);
397 rtext->setAttribute("sodipodi:insensitive", "true");
398 rtext->removeAttribute("transform");
399 rtext->setAttributeSvgDouble("x", pos[Geom::X]);
400 rtext->setAttributeSvgDouble("y", pos[Geom::Y]);
401 rtspan = xml_doc->createElement("svg:tspan");
402 rtspan->setAttribute("sodipodi:role", "line");
403 rtspan->removeAttribute("x");
404 rtspan->removeAttribute("y");
405 elemref = document->getRoot()->appendChildRepr(rtext);
407 rtext->addChild(rtspan, nullptr);
408 Inkscape::GC::release(rtspan);
409 rstring = xml_doc->createTextNode("");
410 rtspan->addChild(rstring, nullptr);
411 Inkscape::GC::release(rstring);
412 }
415 auto fontbutton_str = fontbutton.param_getSVGValue();
416 fontlister->fill_css(css, fontbutton_str);
417 std::stringstream font_size;
418 setlocale (LC_NUMERIC, "C");
419 font_size << fontsize << "px";
420 setlocale (LC_NUMERIC, locale_base);
423 sp_repr_css_set_property (css, "font-size",font_size.str().c_str());
424 sp_repr_css_unset_property (css, "-inkscape-font-specification");
425 if (remove) {
426 sp_repr_css_set_property (css, "display","none");
427 }
428 sp_repr_css_change(rtext, css, "style");
429 sp_repr_css_change(rtspan, css, "style");
430 rtspan->removeAttribute("transform");
432 if (legacy) {
434 } else {
436 }
437 if (local_locale) {
438 setlocale (LC_NUMERIC, "");
439 } else {
440 setlocale (LC_NUMERIC, "C");
441 }
442 gchar length_str[64];
443 bool x100 = false;
444 // active for 1.1
445 if (smallx100 && length < 1 ) {
446 length *=100;
447 x100 = true;
448 g_snprintf(length_str, 64, "%.*f", (int)precision - 2, length);
449 } else {
450 g_snprintf(length_str, 64, "%.*f", (int)precision, length);
451 }
452 setlocale (LC_NUMERIC, locale_base);
453 auto label_value = format.param_getSVGValue();
454 size_t s = label_value.find(Glib::ustring("{measure}"),0);
455 if(s < label_value.length()) {
456 label_value.replace(s, 9, length_str);
457 }
458
459 s = label_value.find(Glib::ustring("{unit}"),0);
460 if(s < label_value.length()) {
461 if (x100) {
462 label_value.replace(s, 6, "");
463 } else {
464 label_value.replace(s, 6, unit.get_abbreviation());
465 }
466 }
467
468 if (showindex) {
469 label_value = Glib::ustring("[") + Inkscape::ustring::format_classic(counter) + Glib::ustring("] ") + label_value;
470 }
471 if (!valid) {
472 label_value = Glib::ustring(_("Non Uniform Scale"));
473 }
474 rstring->setContent(label_value.c_str());
475 if (elemref) {
476 auto text = cast<SPText>(elemref);
477 if (text) {
478 text->rebuildLayout();
479 if (Geom::OptRect bounds = text->geometricBounds()) {
481 rtext->setAttributeSvgDouble("x", pos[Geom::X] - (anotation_width / 2.0));
482 rtspan->removeAttribute("style");
483 }
484 }
485 }
486
487 std::string transform;
488 if (rotate_anotation) {
490 angle = std::fmod(angle, 2*M_PI);
491 if (angle < 0) angle += 2*M_PI;
492 if (angle >= rad_from_deg(90) && angle < rad_from_deg(270)) {
493 angle = std::fmod(angle + rad_from_deg(180), 2*M_PI);
494 if (angle < 0) angle += 2*M_PI;
495 }
496 affine *= Geom::Rotate(angle);
497 affine *= Geom::Translate(pos);
498 transform = sp_svg_transform_write(affine);
499 }
500 rtext->setAttributeOrRemoveIfEmpty("transform", transform);
501}
502
503void
504LPEMeasureSegments::createLine(Geom::Point start,Geom::Point end, Glib::ustring name, size_t counter, bool main, bool remove, bool arrows)
505{
506 SPDocument *document = getSPDoc();
507 if (!document || !sp_lpe_item || !sp_lpe_item->getId()) {
508 return;
509 }
510 Glib::ustring lpobjid = this->lpeobj->getId();
511 Glib::ustring itemid = sp_lpe_item->getId();
512 Glib::ustring id = name;
514 id += "-";
515 id += lpobjid;
516 Inkscape::XML::Document *xml_doc = document->getReprDoc();
517 SPObject *elemref = document->getObjectById(id.c_str());
518 Inkscape::XML::Node *line = nullptr;
519 if (!main) {
520 Geom::Ray ray(start, end);
521 Geom::Coord angle = ray.angle();
522 start = start + Point::polar(angle, helpline_distance );
523 end = end + Point::polar(angle, helpline_overlap );
524 }
525 Geom::PathVector line_pathv;
526
527 double k = (Geom::distance(start,end)/2.0) - (anotation_width/1.7);
528 if (main &&
529 std::abs(text_top_bottom) < fontsize/1.5 &&
530 hide_back &&
531 k > 0)
532 {
533 //k = std::max(k , arrow_gap -1);
534 Geom::Ray ray(end, start);
535 Geom::Coord angle = ray.angle();
536 Geom::Path line_path(start);
537 line_path.appendNew<Geom::LineSegment>(start - Point::polar(angle, k));
538 line_pathv.push_back(line_path);
539 line_path.clear();
540 line_path.start(end + Point::polar(angle, k));
541 line_path.appendNew<Geom::LineSegment>(end);
542 line_pathv.push_back(line_path);
543 } else {
544 Geom::Path line_path(start);
545 line_path.appendNew<Geom::LineSegment>(end);
546 line_pathv.push_back(line_path);
547 }
548 if (elemref) {
549 line = elemref->getRepr();
550 line->setAttribute("d", sp_svg_write_path(line_pathv));
551 line->removeAttribute("transform");
552 } else {
553 line = xml_doc->createElement("svg:path");
554 line->setAttributeOrRemoveIfEmpty("id", id);
555 if (main) {
556 Glib::ustring classlinedim = itemid;
557 classlinedim += " ";
558 classlinedim += lpobjid;
559 classlinedim += " measure-DIM-line measure-line";
560 line->setAttribute("class", classlinedim);
561 } else {
562 Glib::ustring classlinehelper = itemid;
563 classlinehelper += " ";
564 classlinehelper += lpobjid;
565 classlinehelper += " measure-helper-line measure-line";
566 line->setAttribute("class", classlinehelper);
567 }
568 line->setAttribute("d", sp_svg_write_path(line_pathv));
569 }
570
571 line->setAttribute("sodipodi:insensitive", "true");
572 line_pathv.clear();
573
574 Glib::ustring style;
575 if (remove) {
576 style ="display:none;";
577 }
578 if (main) {
579 line->setAttribute("inkscape:label", "dinline");
580 if (!hide_arrows) {
581 if (arrows_outside) {
582 style += "marker-start:url(#ArrowDINout-start);marker-end:url(#ArrowDINout-end);";
583 } else {
584 style += "marker-start:url(#ArrowDIN-start);marker-end:url(#ArrowDIN-end);";
585 }
586 }
587 } else {
588 line->setAttribute("inkscape:label", "dinhelpline");
589 }
590 std::stringstream stroke_w;
591 setlocale (LC_NUMERIC, "C");
592 double stroke_width;
593 if (legacy) {
595 } else {
597 }
598 stroke_w << stroke_width;
599 setlocale (LC_NUMERIC, locale_base);
600 style += "stroke-width:";
601 style += stroke_w.str();
602
607 sp_repr_css_change(line, css, "style");
609
610 if (!elemref) {
611 elemref = document->getRoot()->appendChildRepr(line);
613 }
614}
615
616void
618{
619 if (!is<SPShape>(lpeitem)) {
620 g_warning("LPE measure line can only be applied to shapes (not groups).");
621 SPLPEItem * item = const_cast<SPLPEItem*>(lpeitem);
622 item->removeCurrentPathEffect(false);
623 return;
624 }
625 SPDocument *document = getSPDoc();
626 DocumentUndo::ScopedInsensitive _no_undo(document);
627 Inkscape::XML::Node *styleNode = nullptr;
628 Inkscape::XML::Node *textNode = nullptr;
629 Inkscape::XML::Node *root = document->getReprRoot();
630 for (unsigned i = 0; i < root->childCount(); ++i) {
631 if (Glib::ustring(root->nthChild(i)->name()) == "svg:style") {
632 styleNode = root->nthChild(i);
633 for (unsigned j = 0; j < styleNode->childCount(); ++j) {
634 if (styleNode->nthChild(j)->type() == Inkscape::XML::NodeType::TEXT_NODE) {
635 textNode = styleNode->nthChild(j);
636 }
637 }
638 if (textNode == nullptr) {
639 // Style element found but does not contain text node!
640 std::cerr << "LPEMeasureSegments::doOnApply(): No text node!" << std::endl;
641 textNode = document->getReprDoc()->createTextNode("");
642 styleNode->appendChild(textNode);
643 Inkscape::GC::release(textNode);
644 }
645 }
646 }
647
648 if (styleNode == nullptr) {
649 // Style element not found, create one
650 styleNode = document->getReprDoc()->createElement("svg:style");
651 textNode = document->getReprDoc()->createTextNode("");
652 root->addChild(styleNode, nullptr);
653 Inkscape::GC::release(styleNode);
654
655 styleNode->appendChild(textNode);
656 Inkscape::GC::release(textNode);
657 }
658 // To fix old measuring files pre 1.0
659 Glib::ustring styleContent = Glib::ustring(textNode->content());
660 if (styleContent.find(".measure-arrow\n{\n") == std::string::npos) {
661 styleContent = styleContent + Glib::ustring("\n.measure-arrow") + Glib::ustring("\n{\n}");
662 styleContent = styleContent + Glib::ustring("\n.measure-label") + Glib::ustring("\n{\n\n}");
663 styleContent = styleContent + Glib::ustring("\n.measure-line") + Glib::ustring("\n{\n}");
664 textNode->setContent(styleContent.c_str());
665 }
667 lpeversion.param_setValue("1.3.1", true);
668 legacy = false;
669}
670
671bool
672LPEMeasureSegments::isWhitelist (size_t i, std::string listsegments, bool whitelist)
673{
674 size_t s = listsegments.find(std::to_string(i) + std::string(","), 0);
675 if (s != std::string::npos) {
676 if (whitelist) {
677 return true;
678 } else {
679 return false;
680 }
681 } else {
682 if (whitelist) {
683 return false;
684 } else {
685 return true;
686 }
687 }
688 return false;
689}
690
691double getAngle(Geom::Point p1, Geom::Point p2, Geom::Point p3, bool flip_side, double fix_overlaps)
692{
693 Geom::Ray ray_1(p2,p1);
694 Geom::Ray ray_2(p3,p1);
695 bool ccw_toggle = cross(p1 - p2, p3 - p2) < 0;
696 double angle = angle_between(ray_1, ray_2, ccw_toggle);
697 if (Geom::deg_from_rad(angle) < fix_overlaps ||
698 Geom::deg_from_rad(angle) > 180 ||
699 ((ccw_toggle && flip_side) || (!ccw_toggle && !flip_side)))
700 {
701 angle = 0;
702 }
703 return angle;
704}
705
706std::vector< Point >
707transformNodes(std::vector< Point > nodes, Geom::Affine transform)
708{
709 std::vector< Point > result;
710 for (auto & node : nodes) {
711 Geom::Point point = node;
712 result.push_back(point * transform);
713 }
714 return result;
715}
716
717std::vector< Point >
718getNodes(SPItem * item, Geom::Affine transform, bool onbbox, bool centers, bool bboxonly, double angle_projection)
719{
720 std::vector< Point > current_nodes;
721 SPShape * shape = cast<SPShape> (item);
722 SPText * text = cast<SPText> (item);
723 SPGroup * group = cast<SPGroup> (item);
724 SPFlowtext * flowtext = cast<SPFlowtext> (item);
725 //TODO handle clones/use
726
727 if (group) {
728 std::vector<SPItem*> const item_list = group->item_list();
729 for (auto sub_item : item_list) {
730 std::vector< Point > nodes = transformNodes(getNodes(sub_item, sub_item->transform, onbbox, centers, bboxonly, angle_projection), transform);
731 current_nodes.insert(current_nodes.end(), nodes.begin(), nodes.end());
732 }
733 } else if (shape && !bboxonly) {
734 SPCurve const *c = shape->curve();
735 if (c) {
736 current_nodes = transformNodes(c->get_pathvector().nodes(), transform);
737 }
738 } else if ((text || flowtext) && !bboxonly) {
740 do {
741 Inkscape::Text::Layout::iterator iter_next = iter;
742 iter_next.nextGlyph(); // iter_next is one glyph ahead from iter
743 if (iter == iter_next) {
744 break;
745 }
746 // get path from iter to iter_next:
747 auto curve = te_get_layout(item)->convertToCurves(iter, iter_next);
748 iter = iter_next; // shift to next glyph
749 if (curve.is_empty()) { // whitespace glyph?
750 continue;
751 }
752 std::vector< Point > letter_nodes = transformNodes(curve.get_pathvector().nodes(), transform);
753 current_nodes.insert(current_nodes.end(),letter_nodes.begin(),letter_nodes.end());
754 if (iter == te_get_layout(item)->end()) {
755 break;
756 }
757 } while (true);
758 } else {
759 onbbox = true;
760 }
761 if (onbbox || centers) {
763 if (bbox && onbbox) {
764 current_nodes.push_back((*bbox).corner(0) * transform);
765 current_nodes.push_back((*bbox).corner(2) * transform);
766 if (!Geom::are_near(angle_projection, 0.0) &&
767 !Geom::are_near(angle_projection, 90.0) &&
768 !Geom::are_near(angle_projection, 180.0) &&
769 !Geom::are_near(angle_projection, 360.0))
770 {
771 current_nodes.push_back((*bbox).corner(1) * transform);
772 current_nodes.push_back((*bbox).corner(3) * transform);
773 }
774
775 }
776 if (bbox && centers) {
777 current_nodes.push_back((*bbox).midpoint() * transform);
778 }
779 }
780 return current_nodes;
781}
782
783static void extractFirstPoint(Geom::Point & dest, const Glib::ustring & lpobjid, const char *const prefix, const gint idx, SPDocument *const document)
784{
785 Glib::ustring id = Glib::ustring(prefix);
787 id += "-";
788 id += lpobjid;
789 auto path = cast<SPPath>(document->getObjectById(id));
790 if (path) {
791 SPCurve const *curve = path->curve();
792 if (curve) {
793 dest = *curve->first_point();
794 }
795 }
796}
797
798bool
800 if (!is_load || is_applied) {
801 return false;
802 }
803 if (active_projection) {
806 }
807 return true;
808}
809
810void
812{
813 if (isOnClipboard()) {
814 return;
815 }
816 if (is_load) {
817 legacy = lpeversion.param_getSVGValue() < "1.3.1";
818 auto msg = Glib::ustring(_("<b><big>General</big></b>\n"
819 "Display and position dimension lines and labels\n\n"
820 "<b><big>Projection</big></b>\n"
821 "Show a line with measurements based on the selected items\n\n"
822 "<b><big>Options</big></b>\n"
823 "Options for color, precision, label formatting and display\n\n"
824 "<b><big>Tips</big></b>\n"
825 "<b><i>Custom styling:</i></b> To further customize the styles, "
826 "use the XML editor to find out the class or ID, then use the "
827 "Style dialog to apply a new style.\n"
828 "<b><i>Blacklists:</i></b> allow to hide some segments or projection steps.\n"
829 "<b><i>Multiple Measure LPEs:</i></b> In the same object, in conjunction with blacklists,"
830 "this allows for labels and measurements with different orientations or additional projections.\n"
831 "<b><i>Set Defaults:</i></b> For every LPE, default values can be set at the bottom."));
832
833 if (legacy) {
834 // helpdata is a message parameter, we must not need write this data into the SVG so we empty balue in this legacy files
835 helpdata.write_to_SVG(); // resert old legacy uneeded data
836 msg = Glib::ustring(_("<b><big>IMPORTANT</big></b>\n"
837 "This LPE was added in an older version.\n"
838 "The LPE will be kept, but we recommend you remove and re-add it for better results.\n")) + msg;
839 }
841 }
842 if (!linked_items.data().size()) {
844 if (linked_items.data().size()) {
846 }
847 }
848 SPLPEItem * splpeitem = const_cast<SPLPEItem *>(lpeitem);
849 Glib::ustring lpobjid = this->lpeobj->getId();
850 SPDocument *document = getSPDoc();
851 if (!document) {
852 return;
853 }
854 bool active = !linked_items.data().size();
855 for (auto lpereference : linked_items.data()) {
856 if (lpereference && lpereference.get()->isAttached() && lpereference.get()->getObject() != nullptr) {
857 active = true;
858 }
859 }
860 if (!active && !is_load && prev_active_projection) {
863 return;
864 }
866 //Avoid crashes on previews
867 bool fontsizechanged = false;
868 Geom::Affine parentaffinetransform = i2anc_affine(lpeitem->parent, document->getRoot());
869 Geom::Affine affinetransform = i2anc_affine(lpeitem, document->getRoot());
870 Geom::Affine itemtransform = affinetransform * parentaffinetransform.inverse();
872 Geom::PathVector pathvector;
873 std::vector< Point > nodes;
874 if (active_projection) {
876 if (bbox) {
877 Geom::Point mid = bbox->midpoint();
878 double angle = Geom::rad_from_deg(angle_projection);
879 Geom::Affine transform = itemtransform;
880 transform *= Geom::Translate(mid).inverse();
881 transform *= Geom::Rotate(angle).inverse();
882 transform *= Geom::Translate(mid);
883 std::vector< Point > current_nodes = getNodes(splpeitem, transform, onbbox, centers, bboxonly, angle_projection);
884 nodes.insert(nodes.end(),current_nodes.begin(), current_nodes.end());
885 auto satellites = linked_items.data();
886 if (satellites.size() != prevsatellitecount ) {
887 prevsatellitecount = satellites.size();
888 sp_lpe_item_update_patheffect(sp_lpe_item, false, false, true);
889 }
890 prevsatellitecount = satellites.size();
891 for (auto & iter : satellites) {
892 SPObject *obj;
893 if (iter && iter->isAttached() && iter->getActive() && (obj = iter->getObject()) && is<SPItem>(obj)) {
894 auto item = cast<SPItem>(obj);
895 if (item) {
896 Geom::Affine affinetransform_sub = i2anc_affine(item, document->getRoot());
897 Geom::Affine transform = affinetransform_sub ;
898 transform *= Geom::Translate(-mid);
899 transform *= Geom::Rotate(angle).inverse();
900 transform *= Geom::Translate(mid);
901 std::vector< Point > current_nodes = getNodes(item, transform, onbbox, centers, bboxonly, angle_projection);
902 nodes.insert(nodes.end(),current_nodes.begin(), current_nodes.end());
903 }
904 }
905 }
906
907 double maxdistance = -std::numeric_limits<double>::max();
908 std::vector<double> result;
909 for (auto & node : nodes) {
910 Geom::Point point = node;
911 if (point[Geom::X] > maxdistance) {
912 maxdistance = point[Geom::X];
913 }
914 result.push_back(point[Geom::Y]);
915 }
916 double dproj;
917 if (legacy) {
919 } else {
921 }
922 Geom::Coord xpos = maxdistance + dproj;
923 std::sort (result.begin(), result.end());
924 Geom::Path path;
926 bool started = false;
927 Geom::Point point = Geom::Point();
928 for (auto & iter : result) {
929 point = Geom::Point(xpos, iter);
930 if (Geom::are_near(prevpoint, point)){
931 continue;
932 }
933 if (!started) {
934 path.setInitial(point);
935 started = true;
936 } else {
937 if (!maxmin) {
938 path.appendNew<Geom::LineSegment>(point);
939 }
940 }
941 prevpoint = point;
942 }
943 if (maxmin) {
944 path.appendNew<Geom::LineSegment>(point);
945 }
946 pathvector.push_back(path);
947 pathvector *= Geom::Translate(-mid);
948 pathvector *= Geom::Rotate(angle);
949 pathvector *= Geom::Translate(mid);
950 }
951
952 }
953
954 //end projection prepare
955 auto shape = cast<SPShape>(splpeitem);
956 if (shape) {
957 auto opacity = coloropacity.get_value();
958 bool colorchanged = false;
959 if (opacity != color) {
960 colorchanged = true;
961 }
962 color = *opacity;
963 bool unitchanged = false;
964 Glib::ustring currentunit = unit.get_abbreviation();
965 if (currentunit != prevunit) {
966 unitchanged = true;
967 }
968 prevunit = std::move(currentunit);
969 auto fontdesc_ustring = fontbutton.param_getSVGValue();
970 Pango::FontDescription fontdesc(fontdesc_ustring);
971 double newfontsize = fontdesc.get_size() / (double)Pango::SCALE;
972 if (legacy) {
973 fontsize = Inkscape::Util::Quantity::convert(newfontsize, "pt", document->getDisplayUnit()->abbr.c_str());
974 } else {
976 }
977 if (newfontsize != prevfontsize) {
978 fontsizechanged = true;
979 }
980 prevfontsize = std::move(newfontsize);
981 Geom::Point prev_stored = Geom::Point(0,0);
982 Geom::Point start_stored = Geom::Point(0,0);
983 Geom::Point end_stored = Geom::Point(0,0);
984 Geom::Point next_stored = Geom::Point(0,0);
985 if (!active_projection) {
986 SPCurve const *c = shape->curve();
987 pathvector = pathv_to_linear_and_cubic_beziers(c->get_pathvector());
988 pathvector *= affinetransform;
989 }
990 auto format_str = format.param_getSVGValue();
991 if (format_str.empty()) {
992 format.param_setValue(Glib::ustring("{measure}{unit}"));
993 }
994 size_t ncurves = pathvector.curveCount();
995 items.clear();
996 double start_angle_cross = 0;
997 double end_angle_cross = 0;
998 gint counter = -1;
999 bool previous_fix_overlaps = true;
1000 for (size_t i = 0; i < pathvector.size(); i++) {
1001 size_t count = pathvector[i].size_default();
1002 if (!pathvector[i].empty() && pathvector[i].closed()) {
1003 const Geom::Curve &closingline = pathvector[i].back_closed();
1004 if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
1005 count = pathvector[i].size_open();
1006 }
1007 }
1008 for (size_t j = 0; j < count; j++) {
1009 counter++;
1010 gint fix_overlaps_degree = fix_overlaps;
1011 Geom::Point prev = Geom::Point(0,0);
1012 if (j == 0 && pathvector[i].closed()) {
1013 prev = pathvector.pointAt(pathvector[i].size() - 1);
1014 } else if (j != 0) {
1015 prev = pathvector[i].pointAt(j - 1);
1016 }
1017 Geom::Point start = pathvector[i].pointAt(j);
1018 Geom::Point end = pathvector[i].pointAt(j + 1);
1019 Geom::Point next = Geom::Point(0,0);
1020 if (pathvector[i].closed() && pathvector[i].size() == j+1){
1021 end = pathvector[i].pointAt(0);
1022 next = pathvector[i].pointAt(1);
1023 } else if (pathvector[i].size() > j + 1) {
1024 next = pathvector[i].pointAt(j+2);
1025 }
1026 auto blacklist_str = blacklist.param_getSVGValue();
1027 std::string listsegments(blacklist_str.raw() + ",");
1028 listsegments.erase(std::remove(listsegments.begin(), listsegments.end(), ' '), listsegments.end());
1029 if (isWhitelist(counter, listsegments, (bool)whitelist) && !Geom::are_near(start, end)) {
1030 extractFirstPoint(prev_stored, lpobjid, "infoline-on-start-", counter-1, document);
1031 extractFirstPoint(start_stored, lpobjid, "infoline-on-start-", counter, document);
1032 extractFirstPoint(end_stored, lpobjid, "infoline-on-end-", counter, document);
1033 extractFirstPoint(next_stored, lpobjid, "infoline-on-start-", counter+1, document);
1034 Glib::ustring infoline_on_start = "infoline-on-start-";
1035 infoline_on_start += Inkscape::ustring::format_classic(counter);
1036 infoline_on_start += "-";
1037 infoline_on_start += lpobjid;
1038
1039 Glib::ustring infoline_on_end = "infoline-on-end-";
1040 infoline_on_end += Inkscape::ustring::format_classic(counter);
1041 infoline_on_end += "-";
1042 infoline_on_end += lpobjid;
1043
1044 Glib::ustring infoline = "infoline-";
1046 infoline += "-";
1047 infoline += lpobjid;
1048
1049 Glib::ustring texton = "text-on-";
1051 texton += "-";
1052 texton += lpobjid;
1053 items.push_back(infoline_on_start);
1054 items.push_back(infoline_on_end);
1055 items.push_back(infoline);
1056 items.push_back(texton);
1057 if (!hide_arrows) {
1058 if (arrows_outside) {
1059 items.emplace_back("ArrowDINout-start");
1060 items.emplace_back("ArrowDINout-end");
1061 } else {
1062 items.emplace_back("ArrowDIN-start");
1063 items.emplace_back("ArrowDIN-end");
1064 }
1065 }
1066 if (((Geom::are_near(prev, prev_stored, 0.01) && Geom::are_near(next, next_stored, 0.01)) ||
1067 fix_overlaps_degree == 180) &&
1068 Geom::are_near(start, start_stored, 0.01) && Geom::are_near(end, end_stored, 0.01) &&
1069 !this->refresh_widgets && !colorchanged && !unitchanged && !fontsizechanged &&
1071 {
1072 continue;
1073 }
1074 Geom::Point hstart = start;
1075 Geom::Point hend = end;
1076 bool remove = false;
1077 if (orientation == OM_VERTICAL) {
1078 Coord xpos = std::max(hstart[Geom::X],hend[Geom::X]);
1079 if (flip_side) {
1080 xpos = std::min(hstart[Geom::X],hend[Geom::X]);
1081 }
1082 hstart[Geom::X] = xpos;
1083 hend[Geom::X] = xpos;
1084 if (hstart[Geom::Y] > hend[Geom::Y]) {
1085 std::swap(hstart,hend);
1086 std::swap(start,end);
1087 }
1088 if (Geom::are_near(hstart[Geom::Y], hend[Geom::Y])) {
1089 remove = true;
1090 }
1091 } else if (orientation == OM_HORIZONTAL) {
1092 Coord ypos = std::max(hstart[Geom::Y],hend[Geom::Y]);
1093 if (flip_side) {
1094 ypos = std::min(hstart[Geom::Y],hend[Geom::Y]);
1095 }
1096 hstart[Geom::Y] = ypos;
1097 hend[Geom::Y] = ypos;
1098 if (hstart[Geom::X] < hend[Geom::X]) {
1099 std::swap(hstart,hend);
1100 std::swap(start,end);
1101 }
1102 if (Geom::are_near(hstart[Geom::X], hend[Geom::X])) {
1103 remove = true;
1104 }
1105 } else if (fix_overlaps_degree != 180) {
1106 start_angle_cross = getAngle( start, prev, end, flip_side, fix_overlaps_degree);
1107 if (prev == Geom::Point(0,0)) {
1108 start_angle_cross = 0;
1109 }
1110 end_angle_cross = getAngle(end, start, next, flip_side, fix_overlaps_degree);
1111 if (next == Geom::Point(0,0)) {
1112 end_angle_cross = 0;
1113 }
1114 }
1115 if (remove) {
1116 Geom::Point pos = Geom::Point();
1117 createLine(pos, pos, Glib::ustring("infoline-"), counter, true, true, true);
1118 createLine(pos, pos, Glib::ustring("infoline-on-start-"), counter, true, true, true);
1119 createLine(pos, pos, Glib::ustring("infoline-on-end-"), counter, true, true, true);
1120 createTextLabel(pos, counter, 0, 0, true, true);
1121 continue;
1122 }
1123 Geom::Ray ray(hstart,hend);
1124 Geom::Coord angle = ray.angle();
1125 if (flip_side) {
1126 angle = std::fmod(angle + rad_from_deg(180), 2*M_PI);
1127 if (angle < 0) angle += 2*M_PI;
1128 }
1129 Geom::Coord angle_cross = std::fmod(angle + rad_from_deg(90), 2*M_PI);
1130 if (angle_cross < 0) angle_cross += 2*M_PI;
1131 angle = std::fmod(angle, 2*M_PI);
1132 if (angle < 0) angle += 2*M_PI;
1133 double turn = Geom::rad_from_deg(-90);
1134 if (flip_side) {
1135 end_angle_cross *= -1;
1136 start_angle_cross *= -1;
1137 //turn *= -1;
1138 }
1139 double position_turned_start = position / sin(start_angle_cross/2.0);
1140 double length = Geom::distance(start,end);
1141 if (fix_overlaps_degree != 180 &&
1142 start_angle_cross != 0 &&
1143 position_turned_start < length &&
1144 previous_fix_overlaps)
1145 {
1146 hstart = hstart - Point::polar(angle_cross - (start_angle_cross/2.0) - turn, position_turned_start);
1147 } else {
1148 hstart = hstart - Point::polar(angle_cross, position);
1149 }
1150 createLine(start, hstart, Glib::ustring("infoline-on-start-"), counter, false, false);
1151 double position_turned_end = position / sin(end_angle_cross/2.0);
1152 double endlength = Geom::distance(end,next);
1153 if (fix_overlaps_degree != 180 &&
1154 end_angle_cross != 0 &&
1155 position_turned_end < length &&
1156 position_turned_end < endlength)
1157 {
1158 hend = hend - Point::polar(angle_cross + (end_angle_cross/2.0) + turn, position_turned_end);
1159 previous_fix_overlaps = true;
1160 } else {
1161 hend = hend - Point::polar(angle_cross, position);
1162 previous_fix_overlaps = false;
1163 }
1165 Geom::Point pos = Geom::middle_point(hstart, hend);
1166 if (!hide_arrows) {
1167 if (arrows_outside) {
1168 createArrowMarker(Glib::ustring("ArrowDINout-start"));
1169 createArrowMarker(Glib::ustring("ArrowDINout-end"));
1170 } else {
1171 createArrowMarker(Glib::ustring("ArrowDIN-start"));
1172 createArrowMarker(Glib::ustring("ArrowDIN-end"));
1173 }
1174 }
1175 if (angle >= rad_from_deg(90) && angle < rad_from_deg(270)) {
1176 pos = pos - Point::polar(angle_cross, text_top_bottom + (fontsize/2.5));
1177 } else {
1178 pos = pos + Point::polar(angle_cross, text_top_bottom + (fontsize/2.5));
1179 }
1180 double parents_scale = (parentaffinetransform.expansionX() + parentaffinetransform.expansionY()) / 2.0;
1181 if (!scale_sensitive) {
1182 length /= parents_scale;
1183 }
1184 if ((anotation_width/2) > Geom::distance(hstart,hend)/2.0) {
1185 if (avoid_overlapping) {
1186 pos = pos - Point::polar(angle_cross, position + (anotation_width/2.0));
1187 angle += Geom::rad_from_deg(90);
1188 } else {
1189 pos = pos - Point::polar(angle_cross, position);
1190 }
1191 }
1192 if (!scale_sensitive && !parentaffinetransform.preservesAngles()) {
1193 createTextLabel(pos, counter, length, angle, remove, false);
1194 } else {
1195 createTextLabel(pos, counter, length, angle, remove, true);
1196 }
1197 if (legacy) {
1199 } else {
1201 }
1202 if(flip_side) {
1203 arrow_gap *= -1;
1204 }
1205 if(hide_arrows) {
1206 arrow_gap *= 0;
1207 }
1208 createLine(end, hend, Glib::ustring("infoline-on-end-"), counter, false, false);
1209 if (!arrows_outside) {
1210 hstart = hstart + Point::polar(angle, arrow_gap);
1211 hend = hend - Point::polar(angle, arrow_gap );
1212 }
1213 if ((Geom::distance(hstart, hend) / 2.0) > (anotation_width / 1.9) + arrow_gap) {
1214 createLine(hstart, hend, Glib::ustring("infoline-"), counter, true, false, true);
1215 } else {
1216 createLine(hstart, hend, Glib::ustring("infoline-"), counter, true, true, true);
1217 }
1218 } else {
1219 Geom::Point pos = Geom::Point();
1220 createLine(pos, pos, Glib::ustring("infoline-"), counter, true, true, true);
1221 createLine(pos, pos, Glib::ustring("infoline-on-start-"), counter, true, true, true);
1222 createLine(pos, pos, Glib::ustring("infoline-on-end-"), counter, true, true, true);
1223 createTextLabel(pos, counter, 0, 0, true, true);
1224 }
1225 }
1226 }
1227 if (previous_size) {
1228 for (size_t counter = ncurves; counter < previous_size; counter++) {
1229 Geom::Point pos = Geom::Point();
1230 createLine(pos, pos, Glib::ustring("infoline-"), counter, true, true, true);
1231 createLine(pos, pos, Glib::ustring("infoline-on-start-"), counter, true, true, true);
1232 createLine(pos, pos, Glib::ustring("infoline-on-end-"), counter, true, true, true);
1233 createTextLabel(pos, counter, 0, 0, true, true);
1234 }
1235 }
1236 previous_size = ncurves;
1237 }
1238}
1239
1240void
1245
1246void
1248{
1249 //set "keep paths" hook on sp-lpe-item.cpp
1250 if (keep_paths) {
1252 items.clear();
1253 return;
1254 }
1256 items.clear();
1257}
1258
1259// we override processObjects because satellite items are not selectable and dont surf any issues
1260void
1262{
1263 if (lpe_action == LPE_UPDATE && _lpe_action != LPE_ERASE) {
1264 _lpe_action = lpe_action;
1265 return;
1266 }
1267 SPDocument *document = getSPDoc();
1268 if (!document) {
1269 return;
1270 }
1271 sp_lpe_item = cast<SPLPEItem>(*getLPEObj()->hrefList.begin());
1272 if (!document || !sp_lpe_item) {
1273 return;
1274 }
1276 for (auto id : items) {
1277 SPObject *elemref = nullptr;
1278 if ((elemref = document->getObjectById(id.c_str()))) {
1279 Inkscape::XML::Node * elemnode = elemref->getRepr();
1280 auto item = cast<SPItem>(elemref);
1281 SPCSSAttr *css;
1282 Glib::ustring css_str;
1283 switch (lpe_action){
1284 case LPE_TO_OBJECTS:
1285 if (item->isHidden()) {
1286 item->deleteObject(true);
1287 } else {
1288 elemnode->removeAttribute("sodipodi:insensitive");
1289 if (!is<SPDefs>(item->parent)) {
1291 item->transform *= trans.inverse();
1293 item->moveTo(sp_lpe_item, false);
1294 }
1295 }
1296 break;
1297
1298 case LPE_ERASE:
1299 item->deleteObject(true);
1300 break;
1301
1302 case LPE_VISIBILITY:
1305 if (!this->isVisible()/* && std::strcmp(elemref->getId(),sp_lpe_item->getId()) != 0*/) {
1306 css->setAttribute("display", "none");
1307 } else {
1308 css->removeAttribute("display");
1309 }
1311 elemnode->setAttributeOrRemoveIfEmpty("style", css_str);
1313 break;
1314
1315 default:
1316 break;
1317 }
1318 }
1319 }
1320 if (lpe_action == LPE_ERASE || lpe_action == LPE_TO_OBJECTS) {
1321 items.clear();
1322 }
1324}
1325
1326}; //namespace LivePathEffect
1327}; /* namespace Inkscape */
1328
1329/*
1330 Local Variables:
1331 mode:c++
1332 c-file-style:"stroustrup"
1333 c-file-offset:((innamespace . 0)(inline-open . 0)(case-label . +))
1334 indent-tabs-mode:nil
1335 fill-column:99
1336 End:
1337*/
1338// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
Cartesian point / 2D vector and related operations.
double scale
Definition aa.cpp:228
3x3 affine transformation matrix.
Various trigoniometric helper functions.
int main()
uint64_t page
Definition canvas.cpp:171
Geom::IntRect bounds
Definition canvas.cpp:182
3x3 matrix representing an affine transformation.
Definition affine.h:70
bool preservesAngles(Coord eps=EPSILON) const
Check whether the transformation preserves angles between lines.
Definition affine.cpp:339
Coord expansionX() const
Calculates the amount of x-scaling imparted by the Affine.
Definition affine.cpp:64
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
Coord expansionY() const
Calculates the amount of y-scaling imparted by the Affine.
Definition affine.cpp:71
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.
C width() const
Get the horizontal extent of the rectangle.
Axis-aligned rectangle that can be empty.
Definition rect.h:203
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
Point pointAt(Coord t) const
void clear()
Remove all paths from the vector.
Definition pathvector.h:195
size_type curveCount() const
Get the total number of curves in the vector.
Sequence of contiguous curves, aka spline.
Definition path.h:353
void clear()
Remove all curves from the path.
Definition path.cpp:337
void setInitial(Point const &p)
Definition path.h:734
void appendNew(Args &&... args)
Append a new curve to the path.
Definition path.h:804
void start(Point const &p)
Definition path.cpp:426
Two-dimensional point that doubles as a vector.
Definition point.h:66
Straight ray from a specific point to infinity.
Definition ray.h:53
Coord angle() const
Definition ray.h:73
Rotation around the origin.
Definition transforms.h:187
Rotate inverse() const
Definition transforms.h:209
Translation by a vector.
Definition transforms.h:115
Translate inverse() const
Get the inverse translation.
Definition transforms.h:133
std::string toString(bool opacity=true) const
Format the color as a css string and return it.
Definition color.cpp:106
double getOpacity() const
Get the opacity in this color, if it's stored.
Definition color.cpp:407
RAII-style mechanism for creating a temporary undo-insensitive context.
This class enumerates fonts using libnrtype into reusable data stores and allows for random access to...
Definition font-lister.h:84
void fill_css(SPCSSAttr *css, Glib::ustring fontspec={})
Fill css using given fontspec (doesn't need to be member function).
static Inkscape::FontLister * get_instance()
std::vector< StorageType > const & data() const
Definition array.h:48
std::optional< Colors::Color > get_value() const
Definition colorpicker.h:38
std::vector< Parameter * > param_vector
Definition effect.h:179
void registerParameter(Parameter *param)
Definition effect.cpp:1710
LivePathEffectObject * lpeobj
Definition effect.h:220
bool isOnClipboard()
The lpe is on clipboard.
Definition effect.cpp:1248
LivePathEffectObject * getLPEObj()
Definition effect.h:151
Glib::ustring param_getSVGValue() const override
void param_setValue(Glib::ustring newvalue, bool write=false)
Definition hidden.cpp:71
Glib::ustring param_getSVGValue() const override
Definition hidden.cpp:53
void on_my_switch_page(Gtk::Widget *page, guint page_number)
bool isWhitelist(size_t i, std::string listsegments, bool whitelist)
void processObjects(LPEAction lpe_action) override
void doBeforeEffect(SPLPEItem const *lpeitem) override
Is performed each time before the effect is updated.
void createTextLabel(Geom::Point &pos, size_t counter, double length, Geom::Coord angle, bool remove, bool valid)
void doOnVisibilityToggled(SPLPEItem const *) override
void doOnApply(SPLPEItem const *lpeitem) override
Is performed a single time when the effect is freshly applied to a path.
void createLine(Geom::Point start, Geom::Point end, Glib::ustring name, size_t counter, bool main, bool remove, bool arrows=false)
bool doOnOpen(SPLPEItem const *lpeitem) override
Is performed on load document or revert If the item is fixed legacy return true.
Gtk::Widget * newWidget() override
This creates a managed widget.
void param_update_default(const gchar *default_value) override
Definition message.cpp:47
Glib::ustring const * param_getTooltip() const
Definition parameter.h:81
virtual Gtk::Widget * param_newWidget()=0
void param_set_digits(unsigned digits)
void param_set_range(double min, double max)
void param_set_increments(double step, double page)
void param_setValue(Glib::ustring newvalue)
Definition text.cpp:144
void param_update_default(const gchar *default_value) override
Definition text.cpp:51
Glib::ustring param_getSVGValue() const override
Definition text.cpp:107
const gchar * get_abbreviation() const
Definition unit.cpp:77
Preference storage class.
Definition preferences.h:66
Glib::ustring getString(Glib::ustring const &pref_path, Glib::ustring const &def="")
Retrieve an UTF-8 string.
static Preferences * get()
Access the singleton Preferences object.
Holds a position within the glyph output of Layout.
Definition Layout-TNG.h:973
SPCurve convertToCurves(iterator const &from_glyph, iterator const &to_glyph) const
Convert the specified range of characters into their bezier outlines.
iterator begin() const
Returns an iterator pointing at the first glyph of the flowed output.
Simplified management of enumerations of svg items with UI labels.
Definition enums.h:42
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Definition units.cpp:525
Glib::ustring abbr
Definition units.h:76
Interface for refcounted XML nodes.
Definition node.h:80
virtual void addChild(Node *child, Node *after)=0
Insert another node as a child of this node.
virtual void appendChild(Node *child)=0
Append a node as the last child of this node.
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:167
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual Node * nthChild(unsigned index)=0
Get the child of this node with a given index.
virtual Node * firstChild()=0
Get the first child of this node.
virtual void setContent(char const *value)=0
Set the content of a text or comment node.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
void removeAttribute(Inkscape::Util::const_char_ptr key)
Remove an attribute of this node.
Definition node.h:280
virtual char const * content() const =0
Get the content of a text or comment node.
virtual unsigned childCount() const =0
Get the number of children of this node.
virtual NodeType type() const =0
Get the type of the node.
bool setAttributeSvgDouble(Util::const_char_ptr key, double val)
For attributes where an exponent is allowed.
Definition node.cpp:111
Wrapper around a Geom::PathVector object.
Definition curve.h:26
Typed SVG document implementation.
Definition document.h:103
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:202
SPObject * getObjectById(std::string const &id) const
Inkscape::XML::Node * getReprRoot()
Definition document.h:208
SPDefs * getDefs()
Return the main defs object for the document.
Definition document.cpp:245
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:213
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:773
Inkscape::Util::Unit const * getDisplayUnit()
guaranteed not to return nullptr
Definition document.cpp:741
std::vector< SPItem * > item_list()
Base class for visual SVG elements.
Definition sp-item.h:109
bool isHidden() const
Definition sp-item.cpp:235
void moveTo(SPItem *target, bool intoafter)
Move this SPItem into or after another SPItem in the doc.
Definition sp-item.cpp:478
Geom::Affine transform
Definition sp-item.h:138
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:920
void doWriteTransform(Geom::Affine const &transform, Geom::Affine const *adv=nullptr, bool compensate=true)
Set a new transform on an object.
Definition sp-item.cpp:1658
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
SPDocument * document
Definition sp-object.h:188
char const * getId() const
Returns the objects current ID string.
SPObject * parent
Definition sp-object.h:189
void deleteObject(bool propagate, bool propagate_descendants)
Deletes an object, unparenting it from its parent.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
SPObject * appendChildRepr(Inkscape::XML::Node *repr)
Append repr as child of this object.
Base class for shapes, including <path> element.
Definition sp-shape.h:38
SPCurve const * curve() const
Return a borrowed pointer to the curve (if any exists) or NULL if there is no curve.
Definition sp-shape.cpp:970
RootCluster root
std::shared_ptr< Css const > css
Css & result
Glib::ustring msg
double c[8][4]
TODO: insert short description here.
Font selection widgets.
constexpr Coord infinity()
Get a value representing infinity.
Definition coord.h:88
double Coord
Floating point type used to store coordinates.
Definition coord.h:76
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Geom::PathVector pathv_to_linear_and_cubic_beziers(Geom::PathVector const &pathv)
Definition geom.cpp:588
Specific geometry functions for Inkscape, not provided my lib2geom.
SPItem * item
Inkscape::XML::Node * node
Geom::Point start
Geom::Point end
Various utility functions.
Definition affine.h:22
Coord length(LineSegment const &seg)
SBasisN< n > inverse(SBasisN< n > a, int k)
Angle distance(Angle const &a, Angle const &b)
Definition angle.h:163
double angle_between(Line const &l1, Line const &l2)
Definition line.h:456
Piecewise< SBasis > cross(Piecewise< D2< SBasis > > const &a, Piecewise< D2< SBasis > > const &b)
SBasisN< n > sin(LinearN< n > bo, int k)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
Point middle_point(LineSegment const &_segment)
static R & release(R &r)
Decrements the reference count of a anchored object.
std::vector< Point > getNodes(SPItem *item, Geom::Affine transform, bool onbbox, bool centers, bool bboxonly, double angle_projection)
static const Util::EnumDataConverter< OrientationMethod > OMConverter(OrientationMethodData, OM_END)
double getAngle(Geom::Point p1, Geom::Point p2, Geom::Point p3, bool flip_side, double fix_overlaps)
static void extractFirstPoint(Geom::Point &dest, const Glib::ustring &lpobjid, const char *const prefix, const gint idx, SPDocument *const document)
std::vector< Point > transformNodes(std::vector< Point > nodes, Geom::Affine transform)
static const Util::EnumData< OrientationMethod > OrientationMethodData[]
void pack_start(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the start of box.
Definition pack.cpp:141
@ TEXT_NODE
Text node, e.g. "Some text" in <group>Some text</group> is represented by a text node.
Glib::ustring format_classic(T const &... args)
Helper class to stream background task notifications as a series of messages.
static gint counter
Definition box3d.cpp:39
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
int mode
Singleton class to access the preferences file in a convenient way.
Infinite straight ray.
SPCSSAttr * sp_repr_css_attr_new()
Creates an empty SPCSSAttr (a class for manipulating CSS style properties).
Definition repr-css.cpp:67
void sp_repr_css_write_string(SPCSSAttr *css, Glib::ustring &str)
Write a style attribute string from a list of properties stored in an SPCSAttr object.
Definition repr-css.cpp:243
void sp_repr_css_change(Node *repr, SPCSSAttr *css, gchar const *attr)
Creates a new SPCSAttr with the values filled from a repr, merges in properties from the given SPCSAt...
Definition repr-css.cpp:358
void sp_repr_css_set_property_double(SPCSSAttr *css, gchar const *name, double value)
Set a style property to a new float value (e.g.
Definition repr-css.cpp:224
void sp_repr_css_attr_unref(SPCSSAttr *css)
Unreferences an SPCSSAttr (will be garbage collected if no references remain).
Definition repr-css.cpp:76
void sp_repr_css_set_property_string(SPCSSAttr *css, char const *name, std::string const &value)
Set a style property to a standard string.
Definition repr-css.cpp:235
void sp_repr_css_unset_property(SPCSSAttr *css, gchar const *name)
Set a style property to "inkscape:unset".
Definition repr-css.cpp:202
void sp_repr_css_set_property(SPCSSAttr *css, gchar const *name, gchar const *value)
Set a style property to a new value (e.g.
Definition repr-css.cpp:191
void sp_repr_css_attr_add_from_string(SPCSSAttr *css, gchar const *p)
Use libcroco to parse a string for CSS properties and then merge them into an existing SPCSSAttr.
Definition repr-css.cpp:341
void remove(std::vector< T > &vec, T const &val)
Definition sanitize.cpp:94
TODO: insert short description here.
SPCSSAttr - interface for CSS Attributes.
TODO: insert short description here.
Geom::Affine i2anc_affine(SPObject const *object, SPObject const *ancestor)
Definition sp-item.cpp:1787
Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx.
void sp_lpe_item_update_patheffect(SPLPEItem *lpeitem, bool wholetree, bool write, bool with_satellites)
Calls any registered handlers for the update_patheffect action.
void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
SPRoot: SVG <svg> implementation.
Simplified management of enumerations of svg items with UI labels.
Definition enums.h:27
Interface for XML documents.
Definition document.h:43
virtual Node * createTextNode(char const *content)=0
virtual Node * createElement(char const *name)=0
Definition curve.h:24
std::string sp_svg_transform_write(Geom::Affine const &transform)
static void sp_svg_write_path(Inkscape::SVG::PathString &str, Geom::Path const &p, bool normalize=false)
Definition svg-path.cpp:109
TODO: insert short description here.
Inkscape::Text::Layout const * te_get_layout(SPItem const *item)
Glib::ustring name
Definition toolbars.cpp:55
Interface for XML nodes.