1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Authors:
4  *   Jabier Arraiza Cenoz
5  *
6  * Copyright (C) Jabier Arraiza Cenoz 2014 <jabier.arraiza@marker.es>
7  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
8  */
9 
10 #include <gtkmm.h>
11 #include "live_effects/lpe-show_handles.h"
12 #include <2geom/sbasis-to-bezier.h>
13 #include <2geom/svg-path-parser.h>
14 #include "helper/geom.h"
15 #include "desktop-style.h"
16 #include "display/curve.h"
17 #include "svg/svg.h"
18 
19 #include "object/sp-shape.h"
20 #include "style.h"
21 
22 // TODO due to internal breakage in glibmm headers, this must be last:
23 #include <glibmm/i18n.h>
24 
25 namespace Inkscape {
26 namespace LivePathEffect {
27 
LPEShowHandles(LivePathEffectObject * lpeobject)28 LPEShowHandles::LPEShowHandles(LivePathEffectObject *lpeobject)
29     : Effect(lpeobject),
30       nodes(_("Show nodes"), _("Show nodes"), "nodes", &wr, this, true),
31       handles(_("Show handles"), _("Show handles"), "handles", &wr, this, true),
32       original_path(_("Show path"), _("Show path"), "original_path", &wr, this, true),
33       show_center_node(_("Show center of node"), _("Show center of node"), "show_center_node", &wr, this, false),
34       original_d(_("Show original"), _("Show original"), "original_d", &wr, this, false),
35       scale_nodes_and_handles(_("Scale nodes and handles"), _("Scale nodes and handles"), "scale_nodes_and_handles", &wr, this, 10)
36 {
37     registerParameter(&nodes);
38     registerParameter(&handles);
39     registerParameter(&original_path);
40     registerParameter(&show_center_node);
41     registerParameter(&original_d);
42     registerParameter(&scale_nodes_and_handles);
43     scale_nodes_and_handles.param_set_range(0, 500.);
44     scale_nodes_and_handles.param_set_increments(1, 1);
45     scale_nodes_and_handles.param_set_digits(2);
46     stroke_width = 1.0;
47 }
48 
49 bool LPEShowHandles::alerts_off = false;
50 
51 /**
52  * Sets default styles to element
53  * this permanently remove.some styles of the element
54  */
55 
doOnApply(SPLPEItem const * lpeitem)56 void LPEShowHandles::doOnApply(SPLPEItem const* lpeitem)
57 {
58     if(!alerts_off) {
59         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.");
60         Gtk::MessageDialog dialog(msg, false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK_CANCEL, true);
61         gint response = dialog.run();
62         alerts_off = true;
63         if(response == GTK_RESPONSE_CANCEL) {
64             SPLPEItem* item = const_cast<SPLPEItem*>(lpeitem);
65             item->removeCurrentPathEffect(false);
66             return;
67         }
68     }
69     SPLPEItem* item = const_cast<SPLPEItem*>(lpeitem);
70     SPCSSAttr *css = sp_repr_css_attr_new ();
71     sp_repr_css_set_property (css, "stroke", "black");
72     sp_repr_css_set_property (css, "stroke-width", "1");
73     sp_repr_css_set_property (css, "stroke-linecap", "butt");
74     sp_repr_css_set_property(css, "fill", "none");
75 
76     sp_desktop_apply_css_recursive(item, css, true);
77     sp_repr_css_attr_unref (css);
78 }
79 
doBeforeEffect(SPLPEItem const * lpeitem)80 void LPEShowHandles::doBeforeEffect (SPLPEItem const* lpeitem)
81 {
82     SPItem const* item = SP_ITEM(lpeitem);
83     stroke_width = item->style->stroke_width.computed;
84 }
85 
doEffect_path(Geom::PathVector const & path_in)86 Geom::PathVector LPEShowHandles::doEffect_path (Geom::PathVector const & path_in)
87 {
88     Geom::PathVector path_out;
89     Geom::PathVector original_pathv = pathv_to_linear_and_cubic_beziers(path_in);
90     if(original_path) {
91         for (const auto & i : path_in) {
92             path_out.push_back(i);
93         }
94     }
95     if(!outline_path.empty()) {
96         outline_path.clear();
97     }
98     if (original_d) {
99         auto shape_curve = SPCurve::copy(current_shape->curveForEdit());
100         if (shape_curve) {
101             Geom::PathVector original_curve = shape_curve->get_pathvector();
102             if(original_path) {
103                 for (const auto & i : original_curve) {
104                     path_out.push_back(i);
105                 }
106             }
107             original_pathv.insert(original_pathv.end(), original_curve.begin(), original_curve.end());
108         }
109         generateHelperPath(original_pathv);
110     } else {
111         generateHelperPath(original_pathv);
112     }
113     for (const auto & i : outline_path) {
114         path_out.push_back(i);
115     }
116 
117     return path_out;
118 }
119 
120 void
generateHelperPath(Geom::PathVector result)121 LPEShowHandles::generateHelperPath(Geom::PathVector result)
122 {
123     if(!handles && !nodes) {
124         return;
125     }
126 
127     Geom::CubicBezier const *cubic = nullptr;
128     for (auto & path_it : result) {
129         //Si está vacío...
130         if (path_it.empty()) {
131             continue;
132         }
133         //Itreadores
134         Geom::Path::iterator curve_it1 = path_it.begin(); // incoming curve
135         Geom::Path::iterator curve_it2 = ++(path_it.begin()); // outgoing curve
136         Geom::Path::iterator curve_endit = path_it.end_default(); // this determines when the loop has to stop
137 
138         if (path_it.closed()) {
139             // if the path is closed, maybe we have to stop a bit earlier because the
140             // closing line segment has zerolength.
141             Geom::Curve const &closingline = path_it.back_closed(); // the closing line segment is always of type
142             // Geom::LineSegment.
143             if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
144                 // closingline.isDegenerate() did not work, because it only checks for
145                 // *exact* zero length, which goes wrong for relative coordinates and
146                 // rounding errors...
147                 // the closing line segment has zero-length. So stop before that one!
148                 curve_endit = path_it.end_open();
149             }
150         }
151         if(nodes) {
152             Geom::NodeType nodetype = Geom::NODE_CUSP;
153             if(path_it.closed()) {
154                 nodetype = Geom::get_nodetype(path_it.finalCurve(), *curve_it1);
155             }
156             drawNode(curve_it1->initialPoint(), nodetype);
157         }
158         while (curve_it1 != curve_endit) {
159             cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
160             if (cubic) {
161                 if(handles) {
162                     if(!are_near((*cubic)[0],(*cubic)[1])) {
163                         drawHandle((*cubic)[1]);
164                         drawHandleLine((*cubic)[0],(*cubic)[1]);
165                     }
166                     if(!are_near((*cubic)[3],(*cubic)[2])) {
167                         drawHandle((*cubic)[2]);
168                         drawHandleLine((*cubic)[3],(*cubic)[2]);
169                     }
170                 }
171             }
172             if(nodes && (curve_it2 != curve_endit || !path_it.closed())) {
173                 Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
174                 drawNode(curve_it1->finalPoint(), nodetype);
175             }
176             ++curve_it1;
177             if(curve_it2 != curve_endit) {
178                 ++curve_it2;
179             }
180         }
181     }
182 }
183 
184 void
drawNode(Geom::Point p,Geom::NodeType nodetype)185 LPEShowHandles::drawNode(Geom::Point p, Geom::NodeType nodetype)
186 {
187     if(stroke_width * scale_nodes_and_handles > 0.0) {
188         Geom::Rotate rotate = Geom::Rotate(0);
189         if ( nodetype == Geom::NODE_CUSP) {
190             rotate = Geom::Rotate::from_degrees(45);
191         }
192         double diameter = stroke_width * scale_nodes_and_handles;
193         char const * svgd;
194         if (show_center_node) {
195             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";
196         } else {
197             svgd = "M -0.5,-0.5 0.5,-0.5 0.5,0.5 -0.5,0.5 Z";
198         }
199         Geom::PathVector pathv = sp_svg_read_pathv(svgd);
200         pathv *= rotate * Geom::Scale(diameter) * Geom::Translate(p);
201         outline_path.push_back(pathv[0]);
202         if (show_center_node) {
203             outline_path.push_back(pathv[1]);
204         }
205     }
206 }
207 
208 void
drawHandle(Geom::Point p)209 LPEShowHandles::drawHandle(Geom::Point p)
210 {
211     if(stroke_width * scale_nodes_and_handles > 0.0) {
212         double diameter = stroke_width * scale_nodes_and_handles;
213         char const * svgd;
214         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";
215         Geom::PathVector pathv = sp_svg_read_pathv(svgd);
216         pathv *= Geom::Scale (diameter) * Geom::Translate(p - Geom::Point(diameter * 0.35,diameter * 0.35));
217         outline_path.push_back(pathv[0]);
218     }
219 }
220 
221 
222 void
drawHandleLine(Geom::Point p,Geom::Point p2)223 LPEShowHandles::drawHandleLine(Geom::Point p,Geom::Point p2)
224 {
225     Geom::Path path;
226     double diameter = stroke_width * scale_nodes_and_handles;
227     if(diameter > 0.0 && Geom::distance(p,p2) > (diameter * 0.35)) {
228         Geom::Ray ray2(p, p2);
229         p2 =  p2 - Geom::Point::polar(ray2.angle(),(diameter * 0.35));
230     }
231     path.start( p );
232     path.appendNew<Geom::LineSegment>( p2 );
233     outline_path.push_back(path);
234 }
235 
236 }; //namespace LivePathEffect
237 }; /* namespace Inkscape */
238 
239 /*
240   Local Variables:
241   mode:c++
242   c-file-style:"stroustrup"
243   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
244   indent-tabs-mode:nil
245   fill-column:99
246   End:
247 */
248 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
249