Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
marker-tool.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
/*
5 * Authors:
6 * see git history
7 * Rachana Podaralla <rpodaralla3@gatech.edu>
8 *
9 * Copyright (C) 2018 Authors
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11 */
12
13#include "display/curve.h"
14
15#include "desktop.h"
16#include "document.h"
17#include "style.h"
18#include "message-context.h"
19#include "selection.h"
20
21#include "object/sp-path.h"
22#include "object/sp-shape.h"
23#include "object/sp-marker.h"
24
25#include "ui/shape-editor.h"
30
31
32namespace Inkscape::UI::Tools {
33
35 : ToolBase(desktop, "/tools/marker", "select.svg")
36{
38
39 sel_changed_connection.disconnect();
41 sigc::mem_fun(*this, &MarkerTool::selection_changed)
42 );
43 selection_changed(selection);
44
46 if (prefs->getBool("/tools/marker/selcue")) enableSelectionCue();
47 if (prefs->getBool("/tools/marker/gradientdrag")) enableGrDrag();
48}
49
51{
53
54 message_context->clear();
55 _shape_editors.clear();
56
57 enableGrDrag(false);
58 sel_changed_connection.disconnect();
59}
60
61/*
62- cycles through all the selected items to see if any have a marker in the right location (based on enterMarkerMode)
63- if a matching item is found, loads the corresponding marker on the shape into the shape-editor and exits the loop
64- forces user to only edit one marker at a time
65*/
67 using namespace Inkscape::UI;
68
69 g_assert(_desktop != nullptr);
70
72 g_assert(doc != nullptr);
73
74 auto selected_items = selection->items();
75 _shape_editors.clear();
76
77 for(auto i = selected_items.begin(); i != selected_items.end(); ++i){
78 SPItem *item = *i;
79
80 if(item) {
81 auto shape = cast<SPShape>(item);
82
83 if(shape && shape->hasMarkers() && (editMarkerMode != -1)) {
84 SPObject *obj = shape->_marker[editMarkerMode];
85
86 if(obj) {
87
88 auto sp_marker = cast<SPMarker>(obj);
89 g_assert(sp_marker != nullptr);
90
91 sp_validate_marker(sp_marker, doc);
92
93 ShapeRecord sr;
94 switch(editMarkerMode) {
96 sr = get_marker_transform(shape, item, sp_marker, SP_MARKER_LOC_START);
97 break;
98
100 sr = get_marker_transform(shape, item, sp_marker, SP_MARKER_LOC_MID);
101 break;
102
104 sr = get_marker_transform(shape, item, sp_marker, SP_MARKER_LOC_END);
105 break;
106
107 default:
108 break;
109 }
110
111 auto si = std::make_unique<ShapeEditor>(_desktop, sr.edit_transform, sr.edit_rotation, editMarkerMode);
112 si->set_item(cast<SPItem>(sr.object));
113
114 _shape_editors.insert({item, std::move(si)});
115 break;
116 }
117 }
118 }
119 }
120}
121
122// handles selection of new items
124{
125 auto selection = _desktop->getSelection();
126 bool ret = false;
127
128 inspect_event(event,
129 [&] (ButtonPressEvent const &event) {
130 if (event.num_press == 1 && event.button == 1) {
131
132 item_to_select = sp_event_context_find_item (_desktop, event.pos, event.modifiers & GDK_ALT_MASK, true);
133
134 grabCanvasEvents();
135 ret = true;
136 }
137 },
138 [&] (ButtonReleaseEvent const &event) {
139 if (event.button == 1) {
140 if (item_to_select) {
141 // unselect all items, except for newly selected item
142 selection->set(item_to_select);
143 } else {
144 // clicked into empty space, deselect any selected items
145 selection->clear();
146 }
147
148 item_to_select = nullptr;
150 ret = true;
151 }
152 },
153 [&] (CanvasEvent const &event) {}
154 );
155
156 return ret || ToolBase::root_handler(event);
157}
158
159/*
160- this function uses similar logic that exists in sp_shape_update_marker_view
161- however, the tangent angle needs to be saved here and parent_item->i2dt_affine() needs to also be accounted for in the right places
162- calculate where the shape-editor knotholders need to go based on the reference shape
163*/
165{
166
167 // scale marker transform with parent stroke width
168 SPStyle *style = shape->style;
171
172 if(sp_marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
173 scale *= Geom::Scale(style->stroke_width.computed);
174 }
175
176 Geom::PathVector const &pathv = shape->curve()->get_pathvector();
177 Geom::Affine ret = Geom::identity(); //edit_transform
178 double angle = 0.0; // edit_rotation - tangent angle used for auto orientation
179 Geom::Point p;
180
181 if(marker_type == SP_MARKER_LOC_START) {
182
183 Geom::Curve const &c = pathv.begin()->front();
184 p = c.pointAt(0);
185 ret = Geom::Translate(p * parent_item->i2doc_affine());
186
187 if (!c.isDegenerate()) {
188 Geom::Point tang = c.unitTangentAt(0);
189 angle = Geom::atan2(tang);
190 ret = Geom::Rotate(angle) * ret;
191 }
192
193 } else if(marker_type == SP_MARKER_LOC_MID) {
194 /*
195 - a shape can have multiple mid markers - only one is needed
196 - once a valid mid marker is found, save edit_transfom and edit_rotation and break out of loop
197 */
198 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
199
200 // mid marker start position
201 if (path_it != pathv.begin() && ! ((path_it == (pathv.end()-1)) && (path_it->size_default() == 0)))
202 {
203 Geom::Curve const &c = path_it->front();
204 p = c.pointAt(0);
205 ret = Geom::Translate(p * parent_item->i2doc_affine());
206
207 if (!c.isDegenerate()) {
208 Geom::Point tang = c.unitTangentAt(0);
209 angle = Geom::atan2(tang);
210 ret = Geom::Rotate(angle) * ret;
211 break;
212 }
213 }
214
215 // mid marker mid positions
216 if ( path_it->size_default() > 1) {
217 Geom::Path::const_iterator curve_it1 = path_it->begin();
218 Geom::Path::const_iterator curve_it2 = ++(path_it->begin());
219 while (curve_it2 != path_it->end_default())
220 {
221 Geom::Curve const & c1 = *curve_it1;
222 Geom::Curve const & c2 = *curve_it2;
223
224 p = c1.pointAt(1);
225 Geom::Curve * c1_reverse = c1.reverse();
226 Geom::Point tang1 = - c1_reverse->unitTangentAt(0);
227 delete c1_reverse;
228 Geom::Point tang2 = c2.unitTangentAt(0);
229
230 double const angle1 = Geom::atan2(tang1);
231 double const angle2 = Geom::atan2(tang2);
232
233 angle = .5 * (angle1 + angle2);
234
235 if ( fabs( angle2 - angle1 ) > M_PI ) {
236 angle += M_PI;
237 }
238
239 ret = Geom::Rotate(angle) * Geom::Translate(p * parent_item->i2doc_affine());
240
241 ++curve_it1;
242 ++curve_it2;
243 break;
244 }
245 }
246
247 // mid marker end position
248 if ( path_it != (pathv.end()-1) && !path_it->empty()) {
249 Geom::Curve const &c = path_it->back_default();
250 p = c.pointAt(1);
251 ret = Geom::Translate(p * parent_item->i2doc_affine());
252
253 if ( !c.isDegenerate() ) {
254 Geom::Curve * c_reverse = c.reverse();
255 Geom::Point tang = - c_reverse->unitTangentAt(0);
256 delete c_reverse;
257 angle = Geom::atan2(tang);
258 ret = Geom::Rotate(angle) * ret;
259 break;
260 }
261 }
262 }
263
264 } else if (marker_type == SP_MARKER_LOC_END) {
265
266 Geom::Path const &path_last = pathv.back();
267 unsigned int index = path_last.size_default();
268 if (index > 0) index--;
269
270 Geom::Curve const &c = path_last[index];
271 p = c.pointAt(1);
272 ret = Geom::Translate(p * parent_item->i2doc_affine());
273
274 if ( !c.isDegenerate() ) {
275 Geom::Curve * c_reverse = c.reverse();
276 Geom::Point tang = - c_reverse->unitTangentAt(0);
277 delete c_reverse;
278 angle = Geom::atan2(tang);
279 ret = Geom::Rotate(angle) * ret;
280 }
281 }
282
283 /* scale by stroke width */
284 ret = scale * ret;
285 /* account for parent transform */
286 ret = parent_item->transform.withoutTranslation() * ret;
287
288 ShapeRecord sr;
289 sr.object = sp_marker;
290 sr.edit_transform = ret;
291 sr.edit_rotation = angle * 180.0/M_PI;
293 return sr;
294}
295
296} // namespace Inkscape::UI::Tools
297
298/*
299 Local Variables:
300 mode:c++
301 c-file-style:"stroustrup"
302 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
303 indent-tabs-mode:nil
304 fill-column:99
305 End:
306*/
307// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
double scale
Definition aa.cpp:228
3x3 matrix representing an affine transformation.
Definition affine.h:70
Affine withoutTranslation() const
Definition affine.h:169
Abstract continuous curve on a plane defined on [0,1].
Definition curve.h:78
virtual Curve * reverse() const
Create a reversed version of this curve.
Definition curve.h:234
virtual Point unitTangentAt(Coord t, unsigned n=3) const
Compute a vector tangent to the curve.
Definition curve.cpp:201
virtual Point pointAt(Coord t) const
Evaluate the curve at a specified time value.
Definition curve.h:110
Sequence of subpaths.
Definition pathvector.h:122
Sequence::const_iterator const_iterator
Definition pathvector.h:127
iterator begin()
Definition pathvector.h:151
iterator end()
Definition pathvector.h:152
Sequence of contiguous curves, aka spline.
Definition path.h:353
size_type size_default() const
Natural size of the path.
Definition path.h:486
Two-dimensional point that doubles as a vector.
Definition point.h:66
Rotation around the origin.
Definition transforms.h:187
Scaling from the origin.
Definition transforms.h:150
Translation by a vector.
Definition transforms.h:115
SPItemRange items()
Returns a range of selected SPItems.
Definition object-set.h:255
Preference storage class.
Definition preferences.h:66
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
static Preferences * get()
Access the singleton Preferences object.
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
sigc::connection connectChanged(sigc::slot< void(Selection *)> slot)
Connects a slot to be notified of selection changes.
Definition selection.h:179
bool root_handler(CanvasEvent const &event) override
void selection_changed(Inkscape::Selection *selection)
ShapeRecord get_marker_transform(SPShape *shape, SPItem *parent_item, SPMarker *sp_marker, SPMarkerLoc marker_type)
sigc::connection sel_changed_connection
Definition marker-tool.h:44
std::map< SPItem *, std::unique_ptr< ShapeEditor > > _shape_editors
Definition marker-tool.h:39
MarkerTool(SPDesktop *desktop)
Base class for Event processors.
Definition tool-base.h:107
void ungrabCanvasEvents()
Ungrab events from the Canvas Catchall.
std::unique_ptr< MessageContext > message_context
Definition tool-base.h:193
SPItem * item_to_select
the item where mouse_press occurred, to be selected if this is a click not drag
Definition tool-base.h:152
virtual bool root_handler(CanvasEvent const &event)
void enableGrDrag(bool enable=true)
void enableSelectionCue(bool enable=true)
Enables/disables the ToolBase's SelCue.
Geom::PathVector const & get_pathvector() const
Definition curve.cpp:52
To do: update description of desktop.
Definition desktop.h:149
SPDocument * getDocument() const
Definition desktop.h:189
Inkscape::Selection * getSelection() const
Definition desktop.h:188
Typed SVG document implementation.
Definition document.h:103
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
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::Affine transform
Definition sp-item.h:138
Geom::Affine i2doc_affine() const
Returns the accumulated transformation of the item and all its ancestors, including root's viewport.
Definition sp-item.cpp:1823
unsigned int markerUnits
Definition sp-marker.h:49
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
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
An SVG style object.
Definition style.h:45
T< SPAttr::STROKE_WIDTH, SPILength > stroke_width
stroke-width
Definition style.h:249
double c[8][4]
Editable view implementation.
@ SP_MARKER_UNITS_STROKEWIDTH
Definition enums.h:70
SPItem * item
Marker edit mode - onCanvas marker editing of marker orientation, position, scale.
Interface for locally managing a current status message.
Multi path manipulator - a tool component that edits multiple paths at once.
double atan2(Point const &p)
Affine identity()
Create an identity matrix.
Definition affine.h:210
User interface code.
Definition desktop.h:113
void inspect_event(E &&event, Fs... funcs)
Perform pattern-matching on a CanvasEvent.
Path manipulator - a component that edits a single path on-canvas.
Inkscape::ShapeEditor This is a container class which contains a knotholder for shapes.
SPMarkerLoc
These enums are to allow us to have 4-element arrays that represent a set of marker locations (all,...
@ SP_MARKER_LOC_START
@ SP_MARKER_LOC_END
@ SP_MARKER_LOC_MID
void sp_validate_marker(SPMarker *sp_marker, SPDocument *doc)
A mouse button (left/right/middle) is pressed.
A mouse button (left/right/middle) is released.
Abstract base class for events.
SPStyle - a style object for SPItem objects.
int index
SPDesktop * desktop