Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
spiral-tool.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Spiral drawing context
4 *
5 * Authors:
6 * Mitsuru Oka
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 * Jon A. Cruz <jon@joncruz.org>
10 * Abhishek Sharma
11 *
12 * Copyright (C) 1999-2001 Lauris Kaplinski
13 * Copyright (C) 2001-2002 Mitsuru Oka
14 *
15 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
16 */
17
18#include <cstring>
19#include <string>
20
21#include <gdk/gdkkeysyms.h>
22#include <glibmm/i18n.h>
23
24#include "spiral-tool.h"
25
26#include "context-fns.h"
27#include "desktop-style.h"
28#include "desktop.h"
29#include "document-undo.h"
30#include "document.h"
31#include "message-context.h"
32#include "selection.h"
33
34#include "object/sp-namedview.h"
35#include "object/sp-spiral.h"
36
37#include "ui/icon-names.h"
38#include "ui/shape-editor.h"
40
41#include "util/units.h"
42
44
45namespace Inkscape::UI::Tools {
46
48 : ToolBase(desktop, "/tools/shapes/spiral", "spiral.svg")
49{
50 sp_event_context_read(this, "expansion");
51 sp_event_context_read(this, "revolution");
52 sp_event_context_read(this, "t0");
53
54 this->shape_editor = new ShapeEditor(desktop);
55
57 if (item) {
58 this->shape_editor->set_item(item);
59 }
60
62 this->sel_changed_connection.disconnect();
63
64 this->sel_changed_connection = selection->connectChanged(sigc::mem_fun(*this, &SpiralTool::selection_changed));
65
67
68 if (prefs->getBool("/tools/shapes/selcue")) {
69 this->enableSelectionCue();
70 }
71
72 if (prefs->getBool("/tools/shapes/gradientdrag")) {
73 this->enableGrDrag();
74 }
75}
76
79
80 this->finishItem();
81 this->sel_changed_connection.disconnect();
82
83 this->enableGrDrag(false);
84
85 delete this->shape_editor;
86 this->shape_editor = nullptr;
87
88 /* fixme: This is necessary because we do not grab */
89 if (this->spiral) {
90 this->finishItem();
91 }
92}
93
99 this->shape_editor->unset_item();
100 this->shape_editor->set_item(selection->singleItem());
101}
102
103
105 Glib::ustring name = val.getEntryName();
106
107 if (name == "expansion") {
108 this->exp = CLAMP(val.getDouble(), 0.0, 1000.0);
109 } else if (name == "revolution") {
110 this->revo = CLAMP(val.getDouble(3.0), 0.05, 40.0);
111 } else if (name == "t0") {
112 this->t0 = CLAMP(val.getDouble(), 0.0, 0.999);
113 }
114}
115
117{
118 auto selection = _desktop->getSelection();
119 auto prefs = Inkscape::Preferences::get();
120
121 tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
122
123 bool ret = false;
124
125 inspect_event(event,
126 [&] (ButtonPressEvent const &event) {
127 if (event.num_press == 1 && event.button == 1) {
128 dragging = true;
129
130 setup_for_drag_start(event);
131 center = _desktop->w2d(event.pos);
132
133 // Snap center
134 auto &m = _desktop->getNamedView()->snap_manager;
135 m.setup(_desktop);
136 m.freeSnapReturnByRef(center, SNAPSOURCE_NODE_HANDLE);
137 m.unSetup();
138
139 grabCanvasEvents();
140 ret = true;
141 }
142 },
143 [&] (MotionEvent const &event) {
144 if (dragging && (event.modifiers & GDK_BUTTON1_MASK)) {
145 if (!checkDragMoved(event.pos)) {
146 return;
147 }
148
149 auto motion_dt = _desktop->w2d(event.pos);
150
151 auto &m = _desktop->getNamedView()->snap_manager;
152 m.setup(_desktop, true, spiral.get());
153 m.freeSnapReturnByRef(motion_dt, Inkscape::SNAPSOURCE_NODE_HANDLE);
154 m.unSetup();
155
156 drag(motion_dt, event.modifiers);
157
158 gobble_motion_events(GDK_BUTTON1_MASK);
159
160 ret = true;
161 } else if (!sp_event_context_knot_mouseover()) {
162 auto &m = _desktop->getNamedView()->snap_manager;
163 m.setup(_desktop);
164
165 auto const motion_dt = _desktop->w2d(event.pos);
166 m.preSnap(SnapCandidatePoint(motion_dt, SNAPSOURCE_NODE_HANDLE));
167 m.unSetup();
168 }
169 },
170 [&] (ButtonReleaseEvent const &event) {
171 xyp = {};
172 if (dragging && event.button == 1) {
173 dragging = false;
175
176 if (spiral) {
177 // We've been dragging, finish the spiral.
178 finishItem();
179 } else if (item_to_select) {
180 // No dragging, select clicked item if any.
181 if (event.modifiers & GDK_SHIFT_MASK) {
182 selection->toggle(item_to_select);
183 } else if (!selection->includes(item_to_select)) {
184 selection->set(item_to_select);
185 }
186 } else {
187 // Click in an empty space.
188 selection->clear();
189 }
190
191 item_to_select = nullptr;
192 ret = true;
193 }
195 },
196 [&] (KeyPressEvent const &event) {
197 switch (get_latin_keyval (event)) {
198 case GDK_KEY_Alt_L:
199 case GDK_KEY_Alt_R:
200 case GDK_KEY_Control_L:
201 case GDK_KEY_Control_R:
202 case GDK_KEY_Shift_L:
203 case GDK_KEY_Shift_R:
204 case GDK_KEY_Meta_L: // Meta is when you press Shift+Alt (at least on my machine)
205 case GDK_KEY_Meta_R:
207 _("<b>Ctrl</b>: snap angle"),
208 nullptr,
209 _("<b>Alt</b>: lock spiral radius"));
210 break;
211
212 case GDK_KEY_x:
213 case GDK_KEY_X:
214 if (mod_alt_only(event)) {
215 _desktop->setToolboxFocusTo("spiral-revolutions");
216 ret = true;
217 }
218 break;
219
220 case GDK_KEY_Escape:
221 if (dragging) {
222 dragging = false;
224 // If drawing, cancel, otherwise pass it up for deselecting.
225 cancel();
226 ret = true;
227 }
228 break;
229
230 case GDK_KEY_space:
231 if (dragging) {
233 dragging = false;
235
236 if (!within_tolerance) {
237 // We've been dragging, finish the spiral.
238 finishItem();
239 }
240 // Do not return true, so that space would work switching to selector.
241 }
242 break;
243
244 case GDK_KEY_Delete:
245 case GDK_KEY_KP_Delete:
246 case GDK_KEY_BackSpace:
247 ret = deleteSelectedDrag(mod_ctrl_only(event));
248 break;
249
250 default:
251 break;
252 }
253 },
254 [&] (KeyReleaseEvent const &event) {
255 switch (get_latin_keyval(event)) {
256 case GDK_KEY_Alt_L:
257 case GDK_KEY_Alt_R:
258 case GDK_KEY_Control_L:
259 case GDK_KEY_Control_R:
260 case GDK_KEY_Shift_L:
261 case GDK_KEY_Shift_R:
262 case GDK_KEY_Meta_L: // Meta is when you press Shift+Alt
263 case GDK_KEY_Meta_R:
265 break;
266 default:
267 break;
268 }
269 },
270 [&] (CanvasEvent const &event) {}
271 );
272
273 return ret || ToolBase::root_handler(event);
274}
275
276void SpiralTool::drag(Geom::Point const &p, guint state) {
277
279 int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
280
281 if (!this->spiral) {
283 return;
284 }
285
286 // Create object
288 Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
289 repr->setAttribute("sodipodi:type", "spiral");
290
291 // Set style
292 sp_desktop_apply_style_tool(_desktop, repr, "/tools/shapes/spiral", false);
293
294 this->spiral = cast<SPSpiral>(currentLayer()->appendChildRepr(repr));
297 this->spiral->updateRepr();
298 }
299
301 m.setup(_desktop, true, spiral.get());
302 Geom::Point pt2g = p;
304 m.unSetup();
305 Geom::Point const p0 = _desktop->dt2doc(this->center);
306 Geom::Point const p1 = _desktop->dt2doc(pt2g);
307
308 Geom::Point const delta = p1 - p0;
309 gdouble const rad = Geom::L2(delta);
310
311 // Start angle calculated from end angle and number of revolutions.
312 gdouble arg = Geom::atan2(delta) - 2.0*M_PI * spiral->revo;
313
314 if (state & GDK_CONTROL_MASK) {
315 /* Snap start angle */
316 double snaps_radian = M_PI/snaps;
317 arg = std::round(arg/snaps_radian) * snaps_radian;
318 }
319
320 /* Fixme: these parameters should be got from dialog box */
321 this->spiral->setPosition(p0[Geom::X], p0[Geom::Y],
322 /*expansion*/ this->exp,
323 /*revolution*/ this->revo,
324 rad, arg,
325 /*t0*/ this->t0);
326
327 /* status text */
329 Glib::ustring rads = q.string(_desktop->getNamedView()->display_units);
331 _("<b>Spiral</b>: radius %s, angle %.2f&#176;; with <b>Ctrl</b> to snap angle"),
332 rads.c_str(), arg * 180/M_PI + 360*spiral->revo);
333}
334
336 this->message_context->clear();
337
338 if (spiral) {
339 if (this->spiral->rad == 0) {
340 this->cancel(); // Don't allow the creating of zero sized spiral, for example when the start and and point snap to the snap grid point
341 return;
342 }
343
344 // Set the *transform* center, so select tool rotations work from the spiral's
345 // center instead of the bbox center.
347 spiral->set_shape();
348 spiral->updateRepr(SP_OBJECT_WRITE_EXT);
349 // compensate stroke scaling couldn't be done in doWriteTransform
350 double const expansion = spiral->transform.descrim();
351 spiral->doWriteTransform(spiral->transform, nullptr, true);
353 // update while creating inside a LPE group
354 sp_lpe_item_update_patheffect(this->spiral.get(), true, true);
356 DocumentUndo::done(_desktop->getDocument(), _("Create spiral"), INKSCAPE_ICON("draw-spiral"));
357
358 this->spiral = nullptr;
359 }
360}
361
365
366 if (spiral) {
368 }
369
370 within_tolerance = false;
371 xyp = {};
372 item_to_select = nullptr;
373
375}
376
377} // namespace Inkscape::UI::Tools
378
379/*
380 Local Variables:
381 mode:c++
382 c-file-style:"stroustrup"
383 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
384 indent-tabs-mode:nil
385 fill-column:99
386 End:
387*/
388// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
Two-dimensional point that doubles as a vector.
Definition point.h:66
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
static void cancel(SPDocument *document)
void clear()
removes our current message from the stack
void clear()
Unselects all selected objects.
SPItem * singleItem()
Returns a single selected item.
Data type representing a typeless value of a preference.
double getDouble(double def=0.0, Glib::ustring const &unit="") const
Interpret the preference as a floating point value.
Glib::ustring getEntryName() const
Get the last component of the preference's path.
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.
int getInt(Glib::ustring const &pref_path, int def=0)
Retrieve an integer.
T * get() const
Definition weakptr.h:26
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
void set(XML::Node *repr)
Set the selection to an XML node's SPObject.
Definition selection.h:118
sigc::connection connectChanged(sigc::slot< void(Selection *)> slot)
Connects a slot to be notified of selection changes.
Definition selection.h:179
Class to store data for points which are snap candidates, either as a source or as a target.
void set_item(SPItem *item)
void unset_item(bool keep_knotholder=false)
bool root_handler(CanvasEvent const &event) override
SpiralTool(SPDesktop *desktop)
sigc::connection sel_changed_connection
Definition spiral-tool.h:46
void drag(Geom::Point const &p, unsigned state)
void selection_changed(Selection *selection)
Callback that processes the "changed" signal on the selection; destroys old and creates new knotholde...
SPWeakPtr< SPSpiral > spiral
Definition spiral-tool.h:40
void set(Preferences::Entry const &val) override
Called by our pref_observer if a preference has been changed.
Base class for Event processors.
Definition tool-base.h:107
bool sp_event_context_knot_mouseover() const
Returns true if we're hovering above a knot (needed because we don't want to pre-snap in that case).
void ungrabCanvasEvents()
Ungrab events from the Canvas Catchall.
SPGroup * currentLayer() const
bool within_tolerance
are we still within tolerance of origin
Definition tool-base.h:148
bool dragging
are we dragging?
Definition tool-base.h:146
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
Geom::IntPoint xyp
where drag started
Definition tool-base.h:145
virtual bool root_handler(CanvasEvent const &event)
bool checkDragMoved(Geom::Point const &pos)
Analyse the current position and return true once it has moved farther than tolerance from the drag o...
void enableGrDrag(bool enable=true)
void enableSelectionCue(bool enable=true)
Enables/disables the ToolBase's SelCue.
void discard_delayed_snap_event()
If a delayed snap event has been scheduled, this function will cancel it.
MessageContext * defaultMessageContext() const
Definition tool-base.h:123
bool deleteSelectedDrag(bool just_one)
Delete a selected GrDrag point.
Glib::ustring string(Unit const *u) const
Return a printable string of the value in the specified unit.
Definition units.cpp:515
Interface for refcounted XML nodes.
Definition node.h:80
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
To do: update description of desktop.
Definition desktop.h:149
SPDocument * getDocument() const
Definition desktop.h:189
void setToolboxFocusTo(char const *label)
Definition desktop.cpp:1128
Geom::Affine const & dt2doc() const
Definition desktop.cpp:1343
SPNamedView * getNamedView() const
Definition desktop.h:191
Inkscape::Selection * getSelection() const
Definition desktop.h:188
SPDocument * doc() const
Definition desktop.h:159
Geom::Affine const & w2d() const
Transformation from window to desktop coordinates (zoom/rotate).
Definition desktop.h:416
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:213
Base class for visual SVG elements.
Definition sp-item.h:109
void adjust_stroke_width_recursive(double ex)
Recursively scale stroke width in item and its children by expansion.
Definition sp-item.cpp:1515
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:1816
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
void setCenter(Geom::Point const &object_centre)
Sets the transform_center_x and transform_center_y properties to retain the rotation center.
Definition sp-item.cpp:331
SnapManager snap_manager
Inkscape::Util::Unit const * display_units
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
void deleteObject(bool propagate, bool propagate_descendants)
Deletes an object, unparenting it from its parent.
void set_shape() override
float revo
Spiral revolution factor.
Definition sp-spiral.h:47
float rad
Spiral radius.
Definition sp-spiral.h:48
void setPosition(double cx, double cy, double exp, double revo, double rad, double arg, double t0)
Set spiral properties and update display.
Class to coordinate snapping operations.
Definition snap.h:80
void setup(SPDesktop const *desktop, bool snapindicator=true, SPObject const *item_to_ignore=nullptr, std::vector< Inkscape::SnapCandidatePoint > *unselected_nodes=nullptr)
Convenience shortcut when there is only one item to ignore.
Definition snap.cpp:663
void freeSnapReturnByRef(Geom::Point &p, Inkscape::SnapSourceType const source_type, Geom::OptRect const &bbox_to_snap=Geom::OptRect()) const
Try to snap a point to grids, guides or objects.
Definition snap.cpp:135
void unSetup()
Definition snap.h:147
void sp_desktop_apply_style_tool(SPDesktop *desktop, Inkscape::XML::Node *repr, Glib::ustring const &tool_path, bool with_text)
Apply the desktop's current style or the tool style to repr.
Editable view implementation.
TODO: insert short description here.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Macro for icon names used in Inkscape.
SPItem * item
Interface for locally managing a current status message.
double atan2(Point const &p)
SBasis L2(D2< SBasis > const &a, unsigned k)
Definition d2-sbasis.cpp:42
static R & release(R &r)
Decrements the reference count of a anchored object.
void sp_event_show_modifier_tip(MessageContext *message_context, KeyEvent const &event, char const *ctrl_tip, char const *shift_tip, char const *alt_tip)
Show tool context specific modifier tip.
void gobble_motion_events(unsigned mask)
Definition tool-base.h:244
unsigned get_latin_keyval(GtkEventControllerKey const *const controller, unsigned const keyval, unsigned const keycode, GdkModifierType const state, unsigned *consumed_modifiers)
Return the keyval corresponding to the event controller key in Latin group.
void sp_event_context_read(ToolBase *tool, char const *key)
Calls virtual set() function of ToolBase.
void inspect_event(E &&event, Fs... funcs)
Perform pattern-matching on a CanvasEvent.
@ SNAPSOURCE_NODE_HANDLE
Definition snap-enums.h:43
bool mod_ctrl_only(unsigned modifiers)
bool mod_alt_only(unsigned modifiers)
@ IMMEDIATE_MESSAGE
Definition message.h:27
bool have_viable_layer(SPDesktop *desktop, MessageContext *message)
Check to see if the current layer is both unhidden and unlocked.
Inkscape::ShapeEditor This is a container class which contains a knotholder for shapes.
void sp_lpe_item_update_patheffect(SPLPEItem *lpeitem, bool wholetree, bool write, bool with_satellites)
Calls any registered handlers for the update_patheffect action.
Spiral drawing context.
A mouse button (left/right/middle) is pressed.
A mouse button (left/right/middle) is released.
Abstract base class for events.
unsigned modifiers
The modifiers mask immediately before the event.
A key has been pressed.
A key has been released.
Movement of the mouse pointer.
Interface for XML documents.
Definition document.h:43
virtual Node * createElement(char const *name)=0
int delta
SPDesktop * desktop
Glib::ustring name
Definition toolbars.cpp:55