From cca66b9ec4e494c1d919bff0f71a820d8afab1fa Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:24:48 +0200 Subject: Adding upstream version 1.2.2. Signed-off-by: Daniel Baumann --- src/live_effects/lpe-measure-segments.cpp | 1326 +++++++++++++++++++++++++++++ 1 file changed, 1326 insertions(+) create mode 100644 src/live_effects/lpe-measure-segments.cpp (limited to 'src/live_effects/lpe-measure-segments.cpp') diff --git a/src/live_effects/lpe-measure-segments.cpp b/src/live_effects/lpe-measure-segments.cpp new file mode 100644 index 0000000..ff229f1 --- /dev/null +++ b/src/live_effects/lpe-measure-segments.cpp @@ -0,0 +1,1326 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Author(s): + * Jabiertxo Arraiza Cenoz + * Some code and ideas migrated from dimensioning.py by + * Johannes B. Rutzmoser, johannes.rutzmoser (at) googlemail (dot) com + * https://github.com/Rutzmoser/inkscape_dimensioning + * Copyright (C) 2014 Author(s) + + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "live_effects/lpe-measure-segments.h" + +#include +#include +#include +#include + +#include "2geom/affine.h" +#include "2geom/angle.h" +#include "2geom/point.h" +#include "2geom/ray.h" +#include "display/curve.h" +#include "document-undo.h" +#include "document.h" +#include "helper/geom.h" +#include "inkscape.h" +#include "libnrtype/Layout-TNG.h" +#include "live_effects/lpeobject-reference.h" +#include "live_effects/lpeobject.h" +#include "live_effects/parameter/satellite-reference.h" +#include "object/sp-defs.h" +#include "object/sp-flowtext.h" +#include "object/sp-item-group.h" +#include "object/sp-item.h" +#include "object/sp-path.h" +#include "object/sp-root.h" +#include "object/sp-shape.h" +#include "object/sp-text.h" +#include "path-chemistry.h" +#include "preferences.h" +#include "style.h" +#include "svg/stringstream.h" +#include "svg/svg-color.h" +#include "svg/svg-length.h" +#include "svg/svg.h" +#include "text-editing.h" +#include "util/units.h" +#include "xml/node.h" +#include "xml/sp-css-attr.h" + +// TODO due to internal breakage in glibmm headers, this must be last: +#include + +using namespace Geom; +namespace Inkscape { +namespace LivePathEffect { + + +static const Util::EnumData OrientationMethodData[] = { + { OM_HORIZONTAL , N_("Horizontal"), "horizontal" }, + { OM_VERTICAL , N_("Vertical") , "vertical" }, + { OM_PARALLEL , N_("Parallel") , "parallel" } +}; +static const Util::EnumDataConverter OMConverter(OrientationMethodData, OM_END); + +LPEMeasureSegments::LPEMeasureSegments(LivePathEffectObject *lpeobject) : + Effect(lpeobject), + unit(_("Unit"), _("Unit of measurement"), "unit", &wr, this, "mm"), + orientation(_("Orientation"), _("Orientation of the line and labels"), "orientation", OMConverter, &wr, this, OM_PARALLEL, false), + coloropacity(_("Color and opacity"), _("Set color and opacity of the dimensions"), "coloropacity", &wr, this, 0x000000ff), + fontbutton(_("Font"), _("Select font for labels"), "fontbutton", &wr, this), + precision(_("Precision"), _("Number of digits after the decimal point"), "precision", &wr, this, 2), + 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), + position(_("Position"), _("Distance of dimension line from the path"), "position", &wr, this, 5), + text_top_bottom(_("Label position"), _("Distance of the labels from the dimension line"), "text_top_bottom", &wr, this, 0), + helpline_distance(_("Help line distance"), _("Distance of the perpendicular lines from the path"), "helpline_distance", &wr, this, 0.0), + helpline_overlap(_("Help line elongation"), _("Distance of the perpendicular lines' ends from the dimension line"), "helpline_overlap", &wr, this, 2.0), + line_width(_("Line width"), _("Dimension line width. DIN standard: 0.25 or 0.35 mm"), "line_width", &wr, this, 0.25), + scale(_("Scale"), _("Scaling factor"), "scale", &wr, this, 1.0), + + // TRANSLATORS: Don't translate "{measure}" and "{unit}" variables. + format(_("Label format"), _("Label text format, available variables: {measure}, {unit}"), "format", &wr, this,"{measure}{unit}"), + 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,""), + whitelist(_("Invert blacklist"), _("Use the blacklist as whitelist"), "whitelist", &wr, this, false), + showindex(_("Show segment index"), _("Display the index of the segments in the text label for easier blacklisting"), "showindex", &wr, this, false), + arrows_outside(_("Arrows outside"), _("Draw arrows pointing in the opposite direction outside the dimension line"), "arrows_outside", &wr, this, false), + flip_side(_("Flip side"), _("Draw dimension lines and labels on the other side of the path"), "flip_side", &wr, this, false), + scale_sensitive(_("Scale sensitive"), _("When the path is grouped and the group is then scaled, adjust the dimensions."), "scale_sensitive", &wr, this, true), + 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), + rotate_anotation(_("Rotate labels"), _("Labels are parallel to the dimension line"), "rotate_anotation", &wr, this, true), + hide_back(_("Hide line under label"), _("Hide the dimension line where the label overlaps it"), "hide_back", &wr, this, true), + hide_arrows(_("Hide arrows"), _("Don't show any arrows"), "hide_arrows", &wr, this, false), + // active for 1.1 + smallx100(_("Multiply values < 1"), _("Multiply values smaller than 1 by 100 and leave out the unit"), "smallx100", &wr, this, false), + linked_items(_("Linked objects:"), _("Objects whose nodes are projected onto the path and generate new measurements"), "linked_items", &wr, this, true), + distance_projection(_("Distance"), _("Distance of the dimension lines from the outermost node"), "distance_projection", &wr, this, 20.0), + angle_projection(_("Angle of projection"), _("Angle of projection in 90° steps"), "angle_projection", &wr, this, 0.0), + active_projection(_("Activate projection"), _("Activate projection mode"), "active_projection", &wr, this, false), + avoid_overlapping(_("Avoid label overlap"), _("Rotate labels if the segment is shorter than the label"), "avoid_overlapping", &wr, this, true), + onbbox(_("Measure bounding box"), _("Add measurements for the geometrical bounding box"), "onbbox", &wr, this, false), + bboxonly(_("Only bounding box"), _("Measure only the geometrical bounding box"), "bboxonly", &wr, this, false), + centers(_("Add object center"), _("Add the projected object center"), "centers", &wr, this, false), + maxmin(_("Only max and min"), _("Compute only max/min projection values"), "maxmin", &wr, this, false), + helpdata(_("Help"), _("Measure segments help"), "helpdata", &wr, this, "", "") +{ + //set to true the parameters you want to be changed his default values + registerParameter(&unit); + registerParameter(&orientation); + registerParameter(&coloropacity); + registerParameter(&fontbutton); + registerParameter(&precision); + registerParameter(&fix_overlaps); + registerParameter(&position); + registerParameter(&text_top_bottom); + registerParameter(&helpline_distance); + registerParameter(&helpline_overlap); + registerParameter(&line_width); + registerParameter(&scale); + registerParameter(&format); + registerParameter(&blacklist); + registerParameter(&active_projection); + registerParameter(&whitelist); + registerParameter(&showindex); + registerParameter(&arrows_outside); + registerParameter(&flip_side); + registerParameter(&scale_sensitive); + registerParameter(&local_locale); + registerParameter(&rotate_anotation); + registerParameter(&hide_back); + registerParameter(&hide_arrows); + // active for 1.1 + registerParameter(&smallx100); + registerParameter(&linked_items); + registerParameter(&distance_projection); + registerParameter(&angle_projection); + registerParameter(&avoid_overlapping); + registerParameter(&onbbox); + registerParameter(&bboxonly); + registerParameter(¢ers); + registerParameter(&maxmin); + registerParameter(&helpdata); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + Glib::ustring format_value = prefs->getString("/live_effects/measure-line/format"); + if(format_value.empty()){ + format_value = "{measure}{unit}"; + } + format.param_update_default(format_value.c_str()); + + format.param_hide_canvas_text(); + blacklist.param_hide_canvas_text(); + precision.param_set_range(0, 100); + precision.param_set_increments(1, 1); + precision.param_set_digits(0); + precision.param_make_integer(); + fix_overlaps.param_set_range(0, 180); + fix_overlaps.param_set_increments(1, 1); + fix_overlaps.param_set_digits(0); + fix_overlaps.param_make_integer(); + position.param_set_range(std::numeric_limits::lowest(), std::numeric_limits::max()); + position.param_set_increments(1, 1); + position.param_set_digits(2); + scale.param_set_range(std::numeric_limits::lowest(), std::numeric_limits::max()); + scale.param_set_increments(1, 1); + scale.param_set_digits(4); + text_top_bottom.param_set_range(std::numeric_limits::lowest(), std::numeric_limits::max()); + text_top_bottom.param_set_increments(1, 1); + text_top_bottom.param_set_digits(2); + line_width.param_set_range(0, std::numeric_limits::max()); + line_width.param_set_increments(0.1, 0.1); + line_width.param_set_digits(2); + helpline_distance.param_set_range(std::numeric_limits::lowest(), std::numeric_limits::max()); + helpline_distance.param_set_increments(1, 1); + helpline_distance.param_set_digits(2); + helpline_overlap.param_set_range(std::numeric_limits::lowest(), std::numeric_limits::max()); + helpline_overlap.param_set_increments(1, 1); + helpline_overlap.param_set_digits(2); + distance_projection.param_set_range(std::numeric_limits::lowest(), std::numeric_limits::max()); + distance_projection.param_set_increments(1, 1); + distance_projection.param_set_digits(5); + angle_projection.param_set_range(0.0, 360.0); + angle_projection.param_set_increments(90.0, 90.0); + angle_projection.param_set_digits(2); + locale_base = strdup(setlocale(LC_NUMERIC, nullptr)); + previous_size = 0; + pagenumber = 0; + anotation_width = 0; + fontsize = 0; + rgb32 = 0; + arrow_gap = 0; + //TODO: add newlines for 1.1 (not easy) + helpdata.param_update_default(_("General\n" + "Display and position dimension lines and labels\n\n" + "Projection\n" + "Show a line with measurements based on the selected items\n\n" + "Options\n" + "Options for color, precision, label formatting and display\n\n" + "Tips\n" + "Custom styling: To further customize the styles, " + "use the XML editor to find out the class or ID, then use the " + "Style dialog to apply a new style.\n" + "Blacklists: allow to hide some segments or projection steps.\n" + "Multiple Measure LPEs: In the same object, in conjunction with blacklists," + "this allows for labels and measurements with different orientations or additional projections.\n" + "Set Defaults: For every LPE, default values can be set at the bottom.")); +} + +LPEMeasureSegments::~LPEMeasureSegments() { + keep_paths = false; + doOnRemove(nullptr); +} + +Gtk::Widget * +LPEMeasureSegments::newWidget() +{ + // use manage here, because after deletion of Effect object, others might still be pointing to this widget. + Gtk::Box * vbox = Gtk::manage( new Gtk::Box(Gtk::ORIENTATION_VERTICAL) ); + vbox->set_border_width(0); + vbox->set_homogeneous(false); + vbox->set_spacing(0); + Gtk::Box *vbox0 = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); + vbox0->set_border_width(5); + vbox0->set_homogeneous(false); + vbox0->set_spacing(2); + Gtk::Box *vbox1 = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); + vbox1->set_border_width(5); + vbox1->set_homogeneous(false); + vbox1->set_spacing(2); + Gtk::Box *vbox2 = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); + vbox2->set_border_width(5); + vbox2->set_homogeneous(false); + vbox2->set_spacing(2); + //Help page + Gtk::Box *vbox3 = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); + vbox3->set_border_width(5); + vbox3->set_homogeneous(false); + vbox3->set_spacing(2); + std::vector::iterator it = param_vector.begin(); + while (it != param_vector.end()) { + if ((*it)->widget_is_visible) { + Parameter * param = *it; + Gtk::Widget * widg = param->param_newWidget(); + Glib::ustring * tip = param->param_getTooltip(); + if (widg) { + if ( param->param_key == "linked_items") { + vbox1->pack_start(*widg, true, true, 2); + } else if (param->param_key == "active_projection" || + param->param_key == "distance_projection" || + param->param_key == "angle_projection" || + param->param_key == "maxmin" || + param->param_key == "centers" || + param->param_key == "bboxonly" || + param->param_key == "onbbox" ) + { + vbox1->pack_start(*widg, false, true, 2); + } else if (param->param_key == "precision" || + param->param_key == "coloropacity" || + param->param_key == "font" || + param->param_key == "format" || + param->param_key == "blacklist" || + param->param_key == "whitelist" || + param->param_key == "showindex" || + param->param_key == "local_locale" || + param->param_key == "hide_arrows" ) + { + vbox2->pack_start(*widg, false, true, 2); + } else if (param->param_key == "helpdata") { + vbox3->pack_start(*widg, false, true, 2); + } else { + vbox0->pack_start(*widg, false, true, 2); + } + + if (tip) { + widg->set_tooltip_text(*tip); + } else { + widg->set_tooltip_text(""); + widg->set_has_tooltip(false); + } + } + } + + ++it; + } + + Gtk::Notebook * notebook = Gtk::manage(new Gtk::Notebook()); + notebook->append_page (*vbox0, Glib::ustring(_("General"))); + notebook->append_page (*vbox1, Glib::ustring(_("Projection"))); + notebook->append_page (*vbox2, Glib::ustring(_("Options"))); + notebook->append_page (*vbox3, Glib::ustring(_("Help"))); + vbox0->show_all(); + vbox1->show_all(); + vbox2->show_all(); + vbox3->show_all(); + vbox->pack_start(*notebook, true, true, 2); + notebook->set_current_page(pagenumber); + notebook->signal_switch_page().connect(sigc::mem_fun(*this, &LPEMeasureSegments::on_my_switch_page)); + if(Gtk::Widget* widg = defaultParamSet()) { + //Wrap to make it more omogenious + Gtk::Box *vbox4 = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); + vbox4->set_border_width(5); + vbox4->set_homogeneous(false); + vbox4->set_spacing(2); + vbox4->pack_start(*widg, true, true, 2); + vbox->pack_start(*vbox4, true, true, 2); + } + return dynamic_cast(vbox); +} + +void +LPEMeasureSegments::on_my_switch_page(Gtk::Widget* page, guint page_number) +{ + if(!page->get_parent()->in_destruction()) { + pagenumber = page_number; + } +} + +void +LPEMeasureSegments::createArrowMarker(Glib::ustring mode) +{ + SPDocument *document = getSPDoc(); + if (!document || !sp_lpe_item|| !sp_lpe_item->getId()) { + return; + } + Glib::ustring lpobjid = this->lpeobj->getId(); + Glib::ustring itemid = sp_lpe_item->getId(); + Glib::ustring style; + style = Glib::ustring("fill:context-stroke;"); + Inkscape::SVGOStringStream os; + os << SP_RGBA32_A_F(coloropacity.get_value()); + style = style + Glib::ustring(";fill-opacity:") + Glib::ustring(os.str()); + style = style + Glib::ustring(";stroke:none"); + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + SPObject *elemref = nullptr; + Inkscape::XML::Node *arrow = nullptr; + if ((elemref = document->getObjectById(mode.c_str()))) { + Inkscape::XML::Node *arrow= elemref->getRepr(); + if (arrow) { + arrow->setAttribute("sodipodi:insensitive", "true"); + arrow->removeAttribute("transform"); + Inkscape::XML::Node *arrow_data = arrow->firstChild(); + if (arrow_data) { + arrow_data->removeAttribute("transform"); + arrow_data->setAttribute("style", style); + } + } + } else { + arrow = xml_doc->createElement("svg:marker"); + arrow->setAttribute("id", mode); + Glib::ustring classarrow = itemid; + classarrow += " "; + classarrow += lpobjid; + classarrow += " measure-arrow-marker"; + arrow->setAttribute("class", classarrow); + arrow->setAttributeOrRemoveIfEmpty("inkscape:stockid", mode); + arrow->setAttribute("orient", "auto"); + arrow->setAttribute("refX", "0.0"); + arrow->setAttribute("refY", "0.0"); + + arrow->setAttribute("sodipodi:insensitive", "true"); + /* Create */ + Inkscape::XML::Node *arrow_path = xml_doc->createElement("svg:path"); + if (std::strcmp(mode.c_str(), "ArrowDIN-start") == 0) { + arrow_path->setAttribute("d", "M -8,0 8,-2.11 8,2.11 z"); + } else if (std::strcmp(mode.c_str(), "ArrowDIN-end") == 0) { + arrow_path->setAttribute("d", "M 8,0 -8,2.11 -8,-2.11 z"); + } else if (std::strcmp(mode.c_str(), "ArrowDINout-start") == 0) { + 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"); + } else { + 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"); + } + Glib::ustring classarrowpath = itemid; + classarrowpath += " "; + classarrowpath += lpobjid; + classarrowpath += " measure-arrow"; + arrow_path->setAttributeOrRemoveIfEmpty("class", classarrowpath); + Glib::ustring arrowpath = mode + Glib::ustring("_path"); + arrow_path->setAttribute("id", arrowpath); + arrow_path->setAttribute("style", style); + arrow->addChild(arrow_path, nullptr); + Inkscape::GC::release(arrow_path); + elemref = document->getDefs()->appendChildRepr(arrow); + Inkscape::GC::release(arrow); + } + items.push_back(mode); +} + +void +LPEMeasureSegments::createTextLabel(Geom::Point pos, size_t counter, double length, Geom::Coord angle, bool remove, bool valid) +{ + SPDocument *document = getSPDoc(); + if (!document || !sp_lpe_item || !sp_lpe_item->getId()) { + return; + } + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + Glib::ustring lpobjid = this->lpeobj->getId(); + Glib::ustring itemid = sp_lpe_item->getId(); + Glib::ustring id = Glib::ustring("text-on-"); + id += Glib::ustring::format(counter); + id += "-"; + id += lpobjid; + SPObject *elemref = nullptr; + Inkscape::XML::Node *rtext = nullptr; + Inkscape::XML::Node *rtspan = nullptr; + Inkscape::XML::Node *rstring = nullptr; + elemref = document->getObjectById(id.c_str()); + if (elemref) { + rtext = elemref->getRepr(); + rtext->setAttributeSvgDouble("x", pos[Geom::X]); + rtext->setAttributeSvgDouble("y", pos[Geom::Y]); + rtext->setAttribute("sodipodi:insensitive", "true"); + rtext->removeAttribute("transform"); + rtspan = rtext->firstChild(); + rstring = rtspan->firstChild(); + rtspan->removeAttribute("x"); + rtspan->removeAttribute("y"); + Glib::ustring classlabel = itemid; + classlabel += " "; + classlabel += lpobjid; + classlabel += " measure-label"; + rtext->setAttribute("class", classlabel); + } else { + rtext = xml_doc->createElement("svg:text"); + rtext->setAttribute("xml:space", "preserve"); + rtext->setAttribute("id", id); + Glib::ustring classlabel = itemid; + classlabel += " "; + classlabel += lpobjid; + classlabel += " measure-label"; + rtext->setAttribute("class", classlabel); + rtext->setAttribute("sodipodi:insensitive", "true"); + rtext->removeAttribute("transform"); + rtext->setAttributeSvgDouble("x", pos[Geom::X]); + rtext->setAttributeSvgDouble("y", pos[Geom::Y]); + rtspan = xml_doc->createElement("svg:tspan"); + rtspan->setAttribute("sodipodi:role", "line"); + rtspan->removeAttribute("x"); + rtspan->removeAttribute("y"); + elemref = document->getRoot()->appendChildRepr(rtext); + Inkscape::GC::release(rtext); + rtext->addChild(rtspan, nullptr); + Inkscape::GC::release(rtspan); + rstring = xml_doc->createTextNode(""); + rtspan->addChild(rstring, nullptr); + Inkscape::GC::release(rstring); + } + SPCSSAttr *css = sp_repr_css_attr_new(); + Inkscape::FontLister *fontlister = Inkscape::FontLister::get_instance(); + auto fontbutton_str = fontbutton.param_getSVGValue(); + fontlister->fill_css(css, fontbutton_str); + std::stringstream font_size; + setlocale (LC_NUMERIC, "C"); + font_size << fontsize << "px"; + setlocale (LC_NUMERIC, locale_base); + gchar c[32]; + sprintf(c, "#%06x", rgb32 >> 8); + sp_repr_css_set_property (css, "fill",c); + Inkscape::SVGOStringStream os; + os << SP_RGBA32_A_F(coloropacity.get_value()); + sp_repr_css_set_property (css, "fill-opacity",os.str().c_str()); + sp_repr_css_set_property (css, "font-size",font_size.str().c_str()); + sp_repr_css_unset_property (css, "-inkscape-font-specification"); + if (remove) { + sp_repr_css_set_property (css, "display","none"); + } + Glib::ustring css_str; + sp_repr_css_write_string(css,css_str); + rtext->setAttributeOrRemoveIfEmpty("style", css_str); + rtspan->setAttributeOrRemoveIfEmpty("style", css_str); + rtspan->removeAttribute("transform"); + sp_repr_css_attr_unref (css); + length = Inkscape::Util::Quantity::convert(length, display_unit.c_str(), unit.get_abbreviation()); + if (local_locale) { + setlocale (LC_NUMERIC, ""); + } else { + setlocale (LC_NUMERIC, "C"); + } + gchar length_str[64]; + bool x100 = false; + // active for 1.1 + if (smallx100 && length < 1 ) { + length *=100; + x100 = true; + g_snprintf(length_str, 64, "%.*f", (int)precision - 2, length); + } else { + g_snprintf(length_str, 64, "%.*f", (int)precision, length); + } + setlocale (LC_NUMERIC, locale_base); + auto label_value = format.param_getSVGValue(); + size_t s = label_value.find(Glib::ustring("{measure}"),0); + if(s < label_value.length()) { + label_value.replace(s, 9, length_str); + } + + s = label_value.find(Glib::ustring("{unit}"),0); + if(s < label_value.length()) { + if (x100) { + label_value.replace(s, 6, ""); + } else { + label_value.replace(s, 6, unit.get_abbreviation()); + } + } + + if (showindex) { + label_value = Glib::ustring("[") + Glib::ustring::format(counter) + Glib::ustring("] ") + label_value; + } + if (!valid) { + label_value = Glib::ustring(_("Non Uniform Scale")); + } + rstring->setContent(label_value.c_str()); + // this boring hack is to update the text with document scale inituialy loaded without root transform + if (elemref) { + Geom::OptRect bounds = SP_ITEM(elemref)->geometricBounds(); + if (bounds) { + anotation_width = bounds->width(); + rtext->setAttributeSvgDouble("x", pos[Geom::X] - (anotation_width / 2.0)); + rtspan->removeAttribute("style"); + } + } + + std::string transform; + if (rotate_anotation) { + Geom::Affine affine = Geom::Affine(Geom::Translate(pos).inverse()); + angle = std::fmod(angle, 2*M_PI); + if (angle < 0) angle += 2*M_PI; + if (angle >= rad_from_deg(90) && angle < rad_from_deg(270)) { + angle = std::fmod(angle + rad_from_deg(180), 2*M_PI); + if (angle < 0) angle += 2*M_PI; + } + affine *= Geom::Rotate(angle); + affine *= Geom::Translate(pos); + transform = sp_svg_transform_write(affine); + } + rtext->setAttributeOrRemoveIfEmpty("transform", transform); +} + +void +LPEMeasureSegments::createLine(Geom::Point start,Geom::Point end, Glib::ustring name, size_t counter, bool main, bool remove, bool arrows) +{ + SPDocument *document = getSPDoc(); + if (!document || !sp_lpe_item || !sp_lpe_item->getId()) { + return; + } + Glib::ustring lpobjid = this->lpeobj->getId(); + Glib::ustring itemid = sp_lpe_item->getId(); + Glib::ustring id = name; + id += Glib::ustring::format(counter); + id += "-"; + id += lpobjid; + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + SPObject *elemref = document->getObjectById(id.c_str()); + Inkscape::XML::Node *line = nullptr; + if (!main) { + Geom::Ray ray(start, end); + Geom::Coord angle = ray.angle(); + start = start + Point::polar(angle, helpline_distance ); + end = end + Point::polar(angle, helpline_overlap ); + } + Geom::PathVector line_pathv; + + double k = (Geom::distance(start,end)/2.0) - (anotation_width/1.7); + if (main && + std::abs(text_top_bottom) < fontsize/1.5 && + hide_back && + k > 0) + { + //k = std::max(k , arrow_gap -1); + Geom::Ray ray(end, start); + Geom::Coord angle = ray.angle(); + Geom::Path line_path(start); + line_path.appendNew(start - Point::polar(angle, k)); + line_pathv.push_back(line_path); + line_path.clear(); + line_path.start(end + Point::polar(angle, k)); + line_path.appendNew(end); + line_pathv.push_back(line_path); + } else { + Geom::Path line_path(start); + line_path.appendNew(end); + line_pathv.push_back(line_path); + } + if (elemref) { + line = elemref->getRepr(); + line->setAttribute("d", sp_svg_write_path(line_pathv)); + line->removeAttribute("transform"); + } else { + line = xml_doc->createElement("svg:path"); + line->setAttributeOrRemoveIfEmpty("id", id); + if (main) { + Glib::ustring classlinedim = itemid; + classlinedim += " "; + classlinedim += lpobjid; + classlinedim += " measure-DIM-line measure-line"; + line->setAttribute("class", classlinedim); + } else { + Glib::ustring classlinehelper = itemid; + classlinehelper += " "; + classlinehelper += lpobjid; + classlinehelper += " measure-helper-line measure-line"; + line->setAttribute("class", classlinehelper); + } + line->setAttribute("d", sp_svg_write_path(line_pathv)); + } + + line->setAttribute("sodipodi:insensitive", "true"); + line_pathv.clear(); + + Glib::ustring style; + if (remove) { + style ="display:none;"; + } + if (main) { + line->setAttribute("inkscape:label", "dinline"); + if (!hide_arrows) { + if (arrows_outside) { + style += "marker-start:url(#ArrowDINout-start);marker-end:url(#ArrowDINout-end);"; + } else { + style += "marker-start:url(#ArrowDIN-start);marker-end:url(#ArrowDIN-end);"; + } + } + } else { + line->setAttribute("inkscape:label", "dinhelpline"); + } + std::stringstream stroke_w; + setlocale (LC_NUMERIC, "C"); + + double stroke_width = Inkscape::Util::Quantity::convert(line_width, unit.get_abbreviation(), display_unit.c_str()); + stroke_w << stroke_width; + setlocale (LC_NUMERIC, locale_base); + style += "stroke-width:"; + style += stroke_w.str(); + gchar c[32]; + sprintf(c, "#%06x", rgb32 >> 8); + style += ";stroke:"; + style += Glib::ustring(c); + Inkscape::SVGOStringStream os; + os << SP_RGBA32_A_F(coloropacity.get_value()); + style = style + Glib::ustring(";stroke-opacity:") + Glib::ustring(os.str()); + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_attr_add_from_string(css, style.c_str()); + Glib::ustring css_str; + sp_repr_css_write_string(css,css_str); + line->setAttributeOrRemoveIfEmpty("style", css_str); + if (!elemref) { + elemref = document->getRoot()->appendChildRepr(line); + Inkscape::GC::release(line); + } + sp_repr_css_attr_unref (css); +} + +void +LPEMeasureSegments::doOnApply(SPLPEItem const* lpeitem) +{ + if (!SP_IS_SHAPE(lpeitem)) { + g_warning("LPE measure line can only be applied to shapes (not groups)."); + SPLPEItem * item = const_cast(lpeitem); + item->removeCurrentPathEffect(false); + return; + } + SPDocument *document = getSPDoc(); + bool saved = DocumentUndo::getUndoSensitive(document); + DocumentUndo::setUndoSensitive(document, false); + Inkscape::XML::Node *styleNode = nullptr; + Inkscape::XML::Node* textNode = nullptr; + Inkscape::XML::Node *root = document->getReprRoot(); + for (unsigned i = 0; i < root->childCount(); ++i) { + if (Glib::ustring(root->nthChild(i)->name()) == "svg:style") { + styleNode = root->nthChild(i); + for (unsigned j = 0; j < styleNode->childCount(); ++j) { + if (styleNode->nthChild(j)->type() == Inkscape::XML::NodeType::TEXT_NODE) { + textNode = styleNode->nthChild(j); + } + } + if (textNode == nullptr) { + // Style element found but does not contain text node! + std::cerr << "StyleDialog::_getStyleTextNode(): No text node!" << std::endl; + textNode = document->getReprDoc()->createTextNode(""); + styleNode->appendChild(textNode); + Inkscape::GC::release(textNode); + } + } + } + + if (styleNode == nullptr) { + // Style element not found, create one + styleNode = document->getReprDoc()->createElement("svg:style"); + textNode = document->getReprDoc()->createTextNode(""); + root->addChild(styleNode, nullptr); + Inkscape::GC::release(styleNode); + + styleNode->appendChild(textNode); + Inkscape::GC::release(textNode); + } + // To fix old meassuring files pre 1.0 + Glib::ustring styleContent = Glib::ustring(textNode->content()); + if (styleContent.find(".measure-arrow\n{\n") == std::string::npos) { + styleContent = styleContent + Glib::ustring("\n.measure-arrow") + Glib::ustring("\n{\n}"); + styleContent = styleContent + Glib::ustring("\n.measure-label") + Glib::ustring("\n{\n\n}"); + styleContent = styleContent + Glib::ustring("\n.measure-line") + Glib::ustring("\n{\n}"); + textNode->setContent(styleContent.c_str()); + } + linked_items.update_satellites(); + DocumentUndo::setUndoSensitive(document, saved); +} + +bool +LPEMeasureSegments::isWhitelist (size_t i, std::string listsegments, bool whitelist) +{ + size_t s = listsegments.find(std::to_string(i) + std::string(","), 0); + if (s != std::string::npos) { + if (whitelist) { + return true; + } else { + return false; + } + } else { + if (whitelist) { + return false; + } else { + return true; + } + } + return false; +} + +double getAngle(Geom::Point p1, Geom::Point p2, Geom::Point p3, bool flip_side, double fix_overlaps) +{ + Geom::Ray ray_1(p2,p1); + Geom::Ray ray_2(p3,p1); + bool ccw_toggle = cross(p1 - p2, p3 - p2) < 0; + double angle = angle_between(ray_1, ray_2, ccw_toggle); + if (Geom::deg_from_rad(angle) < fix_overlaps || + Geom::deg_from_rad(angle) > 180 || + ((ccw_toggle && flip_side) || (!ccw_toggle && !flip_side))) + { + angle = 0; + } + return angle; +} + +std::vector< Point > +transformNodes(std::vector< Point > nodes, Geom::Affine transform) +{ + std::vector< Point > result; + for (auto & node : nodes) { + Geom::Point point = node; + result.push_back(point * transform); + } + return result; +} + +std::vector< Point > +getNodes(SPItem * item, Geom::Affine transform, bool onbbox, bool centers, bool bboxonly, double angle_projection) +{ + std::vector< Point > current_nodes; + SPShape * shape = dynamic_cast (item); + SPText * text = dynamic_cast (item); + SPGroup * group = dynamic_cast (item); + SPFlowtext * flowtext = dynamic_cast (item); + //TODO handle clones/use + + if (group) { + std::vector const item_list = sp_item_group_item_list(group); + for (auto sub_item : item_list) { + std::vector< Point > nodes = transformNodes(getNodes(sub_item, sub_item->transform, onbbox, centers, bboxonly, angle_projection), transform); + current_nodes.insert(current_nodes.end(), nodes.begin(), nodes.end()); + } + } else if (shape && !bboxonly) { + SPCurve const *c = shape->curve(); + if (c) { + current_nodes = transformNodes(c->get_pathvector().nodes(), transform); + } + } else if ((text || flowtext) && !bboxonly) { + Inkscape::Text::Layout::iterator iter = te_get_layout(item)->begin(); + do { + Inkscape::Text::Layout::iterator iter_next = iter; + iter_next.nextGlyph(); // iter_next is one glyph ahead from iter + if (iter == iter_next) { + break; + } + // get path from iter to iter_next: + auto curve = te_get_layout(item)->convertToCurves(iter, iter_next); + iter = iter_next; // shift to next glyph + if (!curve) { + continue; // error converting this glyph + } + if (curve->is_empty()) { // whitespace glyph? + continue; + } + std::vector< Point > letter_nodes = transformNodes(curve->get_pathvector().nodes(), transform); + current_nodes.insert(current_nodes.end(),letter_nodes.begin(),letter_nodes.end()); + if (iter == te_get_layout(item)->end()) { + break; + } + } while (true); + } else { + onbbox = true; + } + if (onbbox || centers) { + Geom::OptRect bbox = item->geometricBounds(); + if (bbox && onbbox) { + current_nodes.push_back((*bbox).corner(0) * transform); + current_nodes.push_back((*bbox).corner(2) * transform); + if (!Geom::are_near(angle_projection, 0.0) && + !Geom::are_near(angle_projection, 90.0) && + !Geom::are_near(angle_projection, 180.0) && + !Geom::are_near(angle_projection, 360.0)) + { + current_nodes.push_back((*bbox).corner(1) * transform); + current_nodes.push_back((*bbox).corner(3) * transform); + } + + } + if (bbox && centers) { + current_nodes.push_back((*bbox).midpoint() * transform); + } + } + return current_nodes; +} + +static void extractFirstPoint(Geom::Point & dest, const Glib::ustring & lpobjid, const char *const prefix, const gint idx, SPDocument *const document) +{ + Glib::ustring id = Glib::ustring(prefix); + id += Glib::ustring::format(idx); + id += "-"; + id += lpobjid; + auto path = dynamic_cast(document->getObjectById(id)); + if (path) { + SPCurve const *curve = path->curve(); + if (curve) { + dest = *curve->first_point(); + } + } +} + +bool +LPEMeasureSegments::doOnOpen(SPLPEItem const* lpeitem) { + if (!is_load || is_applied) { + return false; + } + if (active_projection) { + linked_items.start_listening(); + linked_items.connect_selection_changed(); + } + return true; +} + +void +LPEMeasureSegments::doBeforeEffect (SPLPEItem const* lpeitem) +{ + if (isOnClipboard()) { + return; + } + if (!linked_items.data().size()) { + linked_items.read_from_SVG(); + if (linked_items.data().size()) { + linked_items.update_satellites(); + } + } + SPLPEItem * splpeitem = const_cast(lpeitem); + Glib::ustring lpobjid = this->lpeobj->getId(); + SPDocument *document = getSPDoc(); + if (!document) { + return; + } + bool active = !linked_items.data().size(); + for (auto lpereference : linked_items.data()) { + if (lpereference && lpereference.get()->isAttached() && lpereference.get()->getObject() != nullptr) { + active = true; + } + } + if (!active && !is_load && prev_active_projection) { + linked_items.clear(); + prevsatellitecount = 0; + return; + } + prev_active_projection = active_projection; + //Avoid crashes on previews + bool fontsizechanged = false; + Geom::Affine parentaffinetransform = i2anc_affine(lpeitem->parent, document->getRoot()); + Geom::Affine affinetransform = i2anc_affine(lpeitem, document->getRoot()); + Geom::Affine itemtransform = affinetransform * parentaffinetransform.inverse(); + ////Projection prepare + Geom::PathVector pathvector; + std::vector< Point > nodes; + if (active_projection) { + Geom::OptRect bbox = sp_lpe_item->geometricBounds(); + if (bbox) { + Geom::Point mid = bbox->midpoint(); + double angle = Geom::rad_from_deg(angle_projection); + Geom::Affine transform = itemtransform; + transform *= Geom::Translate(mid).inverse(); + transform *= Geom::Rotate(angle).inverse(); + transform *= Geom::Translate(mid); + std::vector< Point > current_nodes = getNodes(splpeitem, transform, onbbox, centers, bboxonly, angle_projection); + nodes.insert(nodes.end(),current_nodes.begin(), current_nodes.end()); + auto satellites = linked_items.data(); + if (satellites.size() != prevsatellitecount ) { + prevsatellitecount = satellites.size(); + linked_items.update_satellites(true); + } + prevsatellitecount = satellites.size(); + for (auto & iter : satellites) { + SPObject *obj; + if (iter && iter->isAttached() && iter->getActive() && (obj = iter->getObject()) && SP_IS_ITEM(obj)) { + SPItem * item = dynamic_cast(obj); + if (item) { + Geom::Affine affinetransform_sub = i2anc_affine(item, document->getRoot()); + Geom::Affine transform = affinetransform_sub ; + transform *= Geom::Translate(-mid); + transform *= Geom::Rotate(angle).inverse(); + transform *= Geom::Translate(mid); + std::vector< Point > current_nodes = getNodes(item, transform, onbbox, centers, bboxonly, angle_projection); + nodes.insert(nodes.end(),current_nodes.begin(), current_nodes.end()); + } + } + } + + double maxdistance = -std::numeric_limits::max(); + std::vector result; + for (auto & node : nodes) { + Geom::Point point = node; + if (point[Geom::X] > maxdistance) { + maxdistance = point[Geom::X]; + } + result.push_back(point[Geom::Y]); + } + double dproj = Inkscape::Util::Quantity::convert(distance_projection, display_unit.c_str(), unit.get_abbreviation()); + Geom::Coord xpos = maxdistance + dproj; + std::sort (result.begin(), result.end()); + Geom::Path path; + Geom::Point prevpoint(Geom::infinity(),Geom::infinity()); + bool started = false; + Geom::Point point = Geom::Point(); + for (auto & iter : result) { + point = Geom::Point(xpos, iter); + if (Geom::are_near(prevpoint, point)){ + continue; + } + if (!started) { + path.setInitial(point); + started = true; + } else { + if (!maxmin) { + path.appendNew(point); + } + } + prevpoint = point; + } + if (maxmin) { + path.appendNew(point); + } + pathvector.push_back(path); + pathvector *= Geom::Translate(-mid); + pathvector *= Geom::Rotate(angle); + pathvector *= Geom::Translate(mid); + } + + } + + //end projection prepare + SPShape *shape = dynamic_cast(splpeitem); + if (shape) { + //only check constrain viewbox on X + display_unit = document->getDisplayUnit()->abbr.c_str(); + guint32 color32 = coloropacity.get_value(); + bool colorchanged = false; + if (color32 != rgb32) { + colorchanged = true; + } + rgb32 = color32; + auto fontdesc_ustring = fontbutton.param_getSVGValue(); + Pango::FontDescription fontdesc(fontdesc_ustring); + double newfontsize = fontdesc.get_size() / (double)Pango::SCALE; + if (newfontsize != fontsize) { + fontsize = Inkscape::Util::Quantity::convert(newfontsize, "pt", display_unit.c_str()); + fontsizechanged = true; + } + Geom::Point prev_stored = Geom::Point(0,0); + Geom::Point start_stored = Geom::Point(0,0); + Geom::Point end_stored = Geom::Point(0,0); + Geom::Point next_stored = Geom::Point(0,0); + if (!active_projection) { + SPCurve const *c = shape->curve(); + pathvector = pathv_to_linear_and_cubic_beziers(c->get_pathvector()); + pathvector *= affinetransform; + } + auto format_str = format.param_getSVGValue(); + if (format_str.empty()) { + format.param_setValue(Glib::ustring("{measure}{unit}")); + } + size_t ncurves = pathvector.curveCount(); + items.clear(); + double start_angle_cross = 0; + double end_angle_cross = 0; + gint counter = -1; + bool previous_fix_overlaps = true; + for (size_t i = 0; i < pathvector.size(); i++) { + size_t count = pathvector[i].size_default(); + if (!pathvector[i].empty() && pathvector[i].closed()) { + const Geom::Curve &closingline = pathvector[i].back_closed(); + if (are_near(closingline.initialPoint(), closingline.finalPoint())) { + count = pathvector[i].size_open(); + } + } + for (size_t j = 0; j < count; j++) { + counter++; + gint fix_overlaps_degree = fix_overlaps; + Geom::Point prev = Geom::Point(0,0); + if (j == 0 && pathvector[i].closed()) { + prev = pathvector.pointAt(pathvector[i].size() - 1); + } else if (j != 0) { + prev = pathvector[i].pointAt(j - 1); + } + Geom::Point start = pathvector[i].pointAt(j); + Geom::Point end = pathvector[i].pointAt(j + 1); + Geom::Point next = Geom::Point(0,0); + if (pathvector[i].closed() && pathvector[i].size() == j+1){ + end = pathvector[i].pointAt(0); + next = pathvector[i].pointAt(1); + } else if (pathvector[i].size() > j + 1) { + next = pathvector[i].pointAt(j+2); + } + auto blacklist_str = blacklist.param_getSVGValue(); + std::string listsegments(blacklist_str.raw() + ","); + listsegments.erase(std::remove(listsegments.begin(), listsegments.end(), ' '), listsegments.end()); + if (isWhitelist(counter, listsegments, (bool)whitelist) && !Geom::are_near(start, end)) { + extractFirstPoint(prev_stored, lpobjid, "infoline-on-start-", counter-1, document); + extractFirstPoint(start_stored, lpobjid, "infoline-on-start-", counter, document); + extractFirstPoint(end_stored, lpobjid, "infoline-on-end-", counter, document); + extractFirstPoint(next_stored, lpobjid, "infoline-on-start-", counter+1, document); + Glib::ustring infoline_on_start = "infoline-on-start-"; + infoline_on_start += Glib::ustring::format(counter); + infoline_on_start += "-"; + infoline_on_start += lpobjid; + + Glib::ustring infoline_on_end = "infoline-on-end-"; + infoline_on_end += Glib::ustring::format(counter); + infoline_on_end += "-"; + infoline_on_end += lpobjid; + + Glib::ustring infoline = "infoline-"; + infoline += Glib::ustring::format(counter); + infoline += "-"; + infoline += lpobjid; + + Glib::ustring texton = "text-on-"; + texton += Glib::ustring::format(counter); + texton += "-"; + texton += lpobjid; + items.push_back(infoline_on_start); + items.push_back(infoline_on_end); + items.push_back(infoline); + items.push_back(texton); + if (!hide_arrows) { + if (arrows_outside) { + items.emplace_back("ArrowDINout-start"); + items.emplace_back("ArrowDINout-end"); + } else { + items.emplace_back("ArrowDIN-start"); + items.emplace_back("ArrowDIN-end"); + } + } + if (((Geom::are_near(prev, prev_stored, 0.01) && Geom::are_near(next, next_stored, 0.01)) || + fix_overlaps_degree == 180) && + Geom::are_near(start, start_stored, 0.01) && Geom::are_near(end, end_stored, 0.01) && + !this->refresh_widgets && !colorchanged && !fontsizechanged && !is_load && anotation_width) + { + continue; + } + Geom::Point hstart = start; + Geom::Point hend = end; + bool remove = false; + if (orientation == OM_VERTICAL) { + Coord xpos = std::max(hstart[Geom::X],hend[Geom::X]); + if (flip_side) { + xpos = std::min(hstart[Geom::X],hend[Geom::X]); + } + hstart[Geom::X] = xpos; + hend[Geom::X] = xpos; + if (hstart[Geom::Y] > hend[Geom::Y]) { + swap(hstart,hend); + swap(start,end); + } + if (Geom::are_near(hstart[Geom::Y], hend[Geom::Y])) { + remove = true; + } + } else if (orientation == OM_HORIZONTAL) { + Coord ypos = std::max(hstart[Geom::Y],hend[Geom::Y]); + if (flip_side) { + ypos = std::min(hstart[Geom::Y],hend[Geom::Y]); + } + hstart[Geom::Y] = ypos; + hend[Geom::Y] = ypos; + if (hstart[Geom::X] < hend[Geom::X]) { + swap(hstart,hend); + swap(start,end); + } + if (Geom::are_near(hstart[Geom::X], hend[Geom::X])) { + remove = true; + } + } else if (fix_overlaps_degree != 180) { + start_angle_cross = getAngle( start, prev, end, flip_side, fix_overlaps_degree); + if (prev == Geom::Point(0,0)) { + start_angle_cross = 0; + } + end_angle_cross = getAngle(end, start, next, flip_side, fix_overlaps_degree); + if (next == Geom::Point(0,0)) { + end_angle_cross = 0; + } + } + if (remove) { + createLine(Geom::Point(), Geom::Point(), Glib::ustring("infoline-"), counter, true, true, true); + createLine(Geom::Point(), Geom::Point(), Glib::ustring("infoline-on-start-"), counter, true, true, true); + createLine(Geom::Point(), Geom::Point(), Glib::ustring("infoline-on-end-"), counter, true, true, true); + createTextLabel(Geom::Point(), counter, 0, 0, true, true); + continue; + } + Geom::Ray ray(hstart,hend); + Geom::Coord angle = ray.angle(); + if (flip_side) { + angle = std::fmod(angle + rad_from_deg(180), 2*M_PI); + if (angle < 0) angle += 2*M_PI; + } + Geom::Coord angle_cross = std::fmod(angle + rad_from_deg(90), 2*M_PI); + if (angle_cross < 0) angle_cross += 2*M_PI; + angle = std::fmod(angle, 2*M_PI); + if (angle < 0) angle += 2*M_PI; + double turn = Geom::rad_from_deg(-90); + if (flip_side) { + end_angle_cross *= -1; + start_angle_cross *= -1; + //turn *= -1; + } + double position_turned_start = position / sin(start_angle_cross/2.0); + double length = Geom::distance(start,end); + if (fix_overlaps_degree != 180 && + start_angle_cross != 0 && + position_turned_start < length && + previous_fix_overlaps) + { + hstart = hstart - Point::polar(angle_cross - (start_angle_cross/2.0) - turn, position_turned_start); + } else { + hstart = hstart - Point::polar(angle_cross, position); + } + createLine(start, hstart, Glib::ustring("infoline-on-start-"), counter, false, false); + double position_turned_end = position / sin(end_angle_cross/2.0); + double endlength = Geom::distance(end,next); + if (fix_overlaps_degree != 180 && + end_angle_cross != 0 && + position_turned_end < length && + position_turned_end < endlength) + { + hend = hend - Point::polar(angle_cross + (end_angle_cross/2.0) + turn, position_turned_end); + previous_fix_overlaps = true; + } else { + hend = hend - Point::polar(angle_cross, position); + previous_fix_overlaps = false; + } + length = Geom::distance(start,end) * scale; + Geom::Point pos = Geom::middle_point(hstart, hend); + if (!hide_arrows) { + if (arrows_outside) { + createArrowMarker(Glib::ustring("ArrowDINout-start")); + createArrowMarker(Glib::ustring("ArrowDINout-end")); + } else { + createArrowMarker(Glib::ustring("ArrowDIN-start")); + createArrowMarker(Glib::ustring("ArrowDIN-end")); + } + } + if (angle >= rad_from_deg(90) && angle < rad_from_deg(270)) { + pos = pos - Point::polar(angle_cross, text_top_bottom + (fontsize/2.5)); + } else { + pos = pos + Point::polar(angle_cross, text_top_bottom + (fontsize/2.5)); + } + double parents_scale = (parentaffinetransform.expansionX() + parentaffinetransform.expansionY()) / 2.0; + if (!scale_sensitive) { + length /= parents_scale; + } + if ((anotation_width/2) > Geom::distance(hstart,hend)/2.0) { + if (avoid_overlapping) { + pos = pos - Point::polar(angle_cross, position + (anotation_width/2.0)); + angle += Geom::rad_from_deg(90); + } else { + pos = pos - Point::polar(angle_cross, position); + } + } + if (!scale_sensitive && !parentaffinetransform.preservesAngles()) { + createTextLabel(pos, counter, length, angle, remove, false); + } else { + createTextLabel(pos, counter, length, angle, remove, true); + } + arrow_gap = 8 * Inkscape::Util::Quantity::convert(line_width, unit.get_abbreviation(), display_unit.c_str()); + if(flip_side) { + arrow_gap *= -1; + } + if(hide_arrows) { + arrow_gap *= 0; + } + createLine(end, hend, Glib::ustring("infoline-on-end-"), counter, false, false); + if (!arrows_outside) { + hstart = hstart + Point::polar(angle, arrow_gap); + hend = hend - Point::polar(angle, arrow_gap ); + } + if ((Geom::distance(hstart, hend) / 2.0) > (anotation_width / 1.9) + arrow_gap) { + createLine(hstart, hend, Glib::ustring("infoline-"), counter, true, false, true); + } else { + createLine(hstart, hend, Glib::ustring("infoline-"), counter, true, true, true); + } + } else { + createLine(Geom::Point(), Geom::Point(), Glib::ustring("infoline-"), counter, true, true, true); + createLine(Geom::Point(), Geom::Point(), Glib::ustring("infoline-on-start-"), counter, true, true, true); + createLine(Geom::Point(), Geom::Point(), Glib::ustring("infoline-on-end-"), counter, true, true, true); + createTextLabel(Geom::Point(), counter, 0, 0, true, true); + } + } + } + if (previous_size) { + for (size_t counter = ncurves; counter < previous_size; counter++) { + createLine(Geom::Point(), Geom::Point(), Glib::ustring("infoline-"), counter, true, true, true); + createLine(Geom::Point(), Geom::Point(), Glib::ustring("infoline-on-start-"), counter, true, true, true); + createLine(Geom::Point(), Geom::Point(), Glib::ustring("infoline-on-end-"), counter, true, true, true); + createTextLabel(Geom::Point(), counter, 0, 0, true, true); + } + } + previous_size = ncurves; + } +} + +void +LPEMeasureSegments::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) +{ + processObjects(LPE_VISIBILITY); +} + +void +LPEMeasureSegments::doOnRemove (SPLPEItem const* /*lpeitem*/) +{ + //set "keep paths" hook on sp-lpe-item.cpp + if (keep_paths) { + processObjects(LPE_TO_OBJECTS); + items.clear(); + return; + } + processObjects(LPE_ERASE); + items.clear(); +} + +// we override processObjects because satellite items are not selectable and dont surf any issues +void +LPEMeasureSegments::processObjects(LPEAction lpe_action) +{ + if (lpe_action == LPE_UPDATE && _lpe_action != LPE_ERASE) { + _lpe_action = lpe_action; + return; + } + SPDocument *document = getSPDoc(); + if (!document) { + return; + } + sp_lpe_item = dynamic_cast(*getLPEObj()->hrefList.begin()); + if (!document || !sp_lpe_item) { + return; + } + sp_lpe_item_enable_path_effects(sp_lpe_item, false); + for (auto id : items) { + SPObject *elemref = nullptr; + if ((elemref = document->getObjectById(id.c_str()))) { + Inkscape::XML::Node * elemnode = elemref->getRepr(); + auto item = dynamic_cast(elemref); + SPCSSAttr *css; + Glib::ustring css_str; + switch (lpe_action){ + case LPE_TO_OBJECTS: + if (item->isHidden()) { + item->deleteObject(true); + } else { + elemnode->removeAttribute("sodipodi:insensitive"); + if (!SP_IS_DEFS(item->parent)) { + item->moveTo(sp_lpe_item, false); + } + } + break; + + case LPE_ERASE: + item->deleteObject(true); + break; + + case LPE_VISIBILITY: + css = sp_repr_css_attr_new(); + sp_repr_css_attr_add_from_string(css, elemref->getRepr()->attribute("style")); + if (!this->isVisible()/* && std::strcmp(elemref->getId(),sp_lpe_item->getId()) != 0*/) { + css->setAttribute("display", "none"); + } else { + css->removeAttribute("display"); + } + sp_repr_css_write_string(css,css_str); + elemnode->setAttributeOrRemoveIfEmpty("style", css_str); + sp_repr_css_attr_unref (css); + break; + + default: + break; + } + } + } + if (lpe_action == LPE_ERASE || lpe_action == LPE_TO_OBJECTS) { + items.clear(); + } + sp_lpe_item_enable_path_effects(sp_lpe_item, true); +} + +}; //namespace LivePathEffect +}; /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offset:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : -- cgit v1.2.3