// SPDX-License-Identifier: GPL-2.0-or-later /* * Authors: * Jabier Arraiza Cenoz * * Copyright (C) Jabier Arraiza Cenoz 2014 * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include #include "live_effects/lpe-show_handles.h" #include <2geom/sbasis-to-bezier.h> #include <2geom/svg-path-parser.h> #include "helper/geom.h" #include "desktop-style.h" #include "display/curve.h" #include "svg/svg.h" #include "object/sp-shape.h" #include "style.h" // TODO due to internal breakage in glibmm headers, this must be last: #include namespace Inkscape { namespace LivePathEffect { LPEShowHandles::LPEShowHandles(LivePathEffectObject *lpeobject) : Effect(lpeobject), nodes(_("Show nodes"), _("Show nodes"), "nodes", &wr, this, true), handles(_("Show handles"), _("Show handles"), "handles", &wr, this, true), original_path(_("Show path"), _("Show path"), "original_path", &wr, this, true), show_center_node(_("Show center of node"), _("Show center of node"), "show_center_node", &wr, this, false), original_d(_("Show original"), _("Show original"), "original_d", &wr, this, false), scale_nodes_and_handles(_("Scale nodes and handles"), _("Scale nodes and handles"), "scale_nodes_and_handles", &wr, this, 10) { registerParameter(&nodes); registerParameter(&handles); registerParameter(&original_path); registerParameter(&show_center_node); registerParameter(&original_d); registerParameter(&scale_nodes_and_handles); scale_nodes_and_handles.param_set_range(0, 500.); scale_nodes_and_handles.param_set_increments(1, 1); scale_nodes_and_handles.param_set_digits(2); stroke_width = 1.0; } bool LPEShowHandles::alerts_off = false; /** * Sets default styles to element * this permanently remove.some styles of the element */ void LPEShowHandles::doOnApply(SPLPEItem const* lpeitem) { if(!alerts_off) { char *msg = _("The \"show handles\" path effect will remove any custom style on the object you are applying it to. If this is not what you want, click Cancel."); Gtk::MessageDialog dialog(msg, false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK_CANCEL, true); gint response = dialog.run(); alerts_off = true; if(response == GTK_RESPONSE_CANCEL) { SPLPEItem* item = const_cast(lpeitem); item->removeCurrentPathEffect(false); return; } } SPLPEItem* item = const_cast(lpeitem); SPCSSAttr *css = sp_repr_css_attr_new (); sp_repr_css_set_property (css, "stroke", "black"); sp_repr_css_set_property (css, "stroke-width", "1"); sp_repr_css_set_property (css, "stroke-linecap", "butt"); sp_repr_css_set_property(css, "fill", "none"); sp_desktop_apply_css_recursive(item, css, true); sp_repr_css_attr_unref (css); } void LPEShowHandles::doBeforeEffect (SPLPEItem const* lpeitem) { stroke_width = lpeitem->style->stroke_width.computed; } Geom::PathVector LPEShowHandles::doEffect_path (Geom::PathVector const & path_in) { Geom::PathVector path_out; Geom::PathVector original_pathv = pathv_to_linear_and_cubic_beziers(path_in); if(original_path) { for (const auto & i : path_in) { path_out.push_back(i); } } if(!outline_path.empty()) { outline_path.clear(); } if (original_d) { auto shape_curve = SPCurve::copy(current_shape->curveForEdit()); if (shape_curve) { Geom::PathVector original_curve = shape_curve->get_pathvector(); if(original_path) { for (const auto & i : original_curve) { path_out.push_back(i); } } original_pathv.insert(original_pathv.end(), original_curve.begin(), original_curve.end()); } generateHelperPath(original_pathv); } else { generateHelperPath(original_pathv); } for (const auto & i : outline_path) { path_out.push_back(i); } return path_out; } void LPEShowHandles::generateHelperPath(Geom::PathVector result) { if(!handles && !nodes) { return; } Geom::CubicBezier const *cubic = nullptr; for (auto & path_it : result) { //Si está vacío... if (path_it.empty()) { continue; } //Itreadores Geom::Path::iterator curve_it1 = path_it.begin(); // incoming curve Geom::Path::iterator curve_it2 = ++(path_it.begin()); // outgoing curve Geom::Path::iterator curve_endit = path_it.end_default(); // this determines when the loop has to stop if (path_it.closed()) { // if the path is closed, maybe we have to stop a bit earlier because the // closing line segment has zerolength. Geom::Curve const &closingline = path_it.back_closed(); // the closing line segment is always of type // Geom::LineSegment. if (are_near(closingline.initialPoint(), closingline.finalPoint())) { // closingline.isDegenerate() did not work, because it only checks for // *exact* zero length, which goes wrong for relative coordinates and // rounding errors... // the closing line segment has zero-length. So stop before that one! curve_endit = path_it.end_open(); } } if(nodes) { Geom::NodeType nodetype = Geom::NODE_CUSP; if(path_it.closed()) { nodetype = Geom::get_nodetype(path_it.finalCurve(), *curve_it1); } drawNode(curve_it1->initialPoint(), nodetype); } while (curve_it1 != curve_endit) { cubic = dynamic_cast(&*curve_it1); if (cubic) { if(handles) { if(!are_near((*cubic)[0],(*cubic)[1])) { drawHandle((*cubic)[1]); drawHandleLine((*cubic)[0],(*cubic)[1]); } if(!are_near((*cubic)[3],(*cubic)[2])) { drawHandle((*cubic)[2]); drawHandleLine((*cubic)[3],(*cubic)[2]); } } } if(nodes && (curve_it2 != curve_endit || !path_it.closed())) { Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2); drawNode(curve_it1->finalPoint(), nodetype); } ++curve_it1; if(curve_it2 != curve_endit) { ++curve_it2; } } } } void LPEShowHandles::drawNode(Geom::Point p, Geom::NodeType nodetype) { if(stroke_width * scale_nodes_and_handles > 0.0) { Geom::Rotate rotate = Geom::Rotate(0); if ( nodetype == Geom::NODE_CUSP) { rotate = Geom::Rotate::from_degrees(45); } double diameter = stroke_width * scale_nodes_and_handles; char const * svgd; if (show_center_node) { svgd = "M 0.05,0 A 0.05,0.05 0 0 1 0,0.05 0.05,0.05 0 0 1 -0.05,0 0.05,0.05 0 0 1 0,-0.05 0.05,0.05 0 0 1 0.05,0 Z M -0.5,-0.5 0.5,-0.5 0.5,0.5 -0.5,0.5 Z"; } else { svgd = "M -0.5,-0.5 0.5,-0.5 0.5,0.5 -0.5,0.5 Z"; } Geom::PathVector pathv = sp_svg_read_pathv(svgd); pathv *= rotate * Geom::Scale(diameter) * Geom::Translate(p); outline_path.push_back(pathv[0]); if (show_center_node) { outline_path.push_back(pathv[1]); } } } void LPEShowHandles::drawHandle(Geom::Point p) { if(stroke_width * scale_nodes_and_handles > 0.0) { double diameter = stroke_width * scale_nodes_and_handles; char const * svgd; svgd = "M 0.7,0.35 A 0.35,0.35 0 0 1 0.35,0.7 0.35,0.35 0 0 1 0,0.35 0.35,0.35 0 0 1 0.35,0 0.35,0.35 0 0 1 0.7,0.35 Z"; Geom::PathVector pathv = sp_svg_read_pathv(svgd); pathv *= Geom::Scale (diameter) * Geom::Translate(p - Geom::Point(diameter * 0.35,diameter * 0.35)); outline_path.push_back(pathv[0]); } } void LPEShowHandles::drawHandleLine(Geom::Point p,Geom::Point p2) { Geom::Path path; double diameter = stroke_width * scale_nodes_and_handles; if(diameter > 0.0 && Geom::distance(p,p2) > (diameter * 0.35)) { Geom::Ray ray2(p, p2); p2 = p2 - Geom::Point::polar(ray2.angle(),(diameter * 0.35)); } path.start( p ); path.appendNew( p2 ); outline_path.push_back(path); } }; //namespace LivePathEffect }; /* namespace Inkscape */ /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((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 :