Inkscape
Vector Graphics Editor
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages Concepts
control-point-selection.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
6/* Authors:
7 * Krzysztof Kosiński <tweenk.pl@gmail.com>
8 *
9 * Copyright (C) 2009 Authors
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11 */
12
13#include <boost/none.hpp>
14#include <gdk/gdkkeysyms.h>
15#include <2geom/transforms.h>
16
17#include "desktop.h"
22#include "ui/tool/node.h"
23#include "ui/widget/canvas.h"
25
26namespace Inkscape {
27namespace UI {
28
54 : Manipulator(d)
55 , _handles(new TransformHandleSet(d, th_group))
56 , _dragging(false)
57 , _handles_visible(true)
58 , _one_node_handles(false)
59{
60 signal_update.connect( sigc::bind(
62 true));
64 sigc::hide(
65 sigc::mem_fun(*this, &ControlPointSelection::_mouseoverChanged)));
67 sigc::mem_fun(*this, &ControlPointSelection::transform));
68 _handles->signal_commit.connect(
70}
71
77
79std::pair<ControlPointSelection::iterator, bool> ControlPointSelection::insert(const value_type &x, bool notify, bool to_update)
80{
81 iterator found = _points.find(x);
82 if (found != _points.end()) {
83 return std::pair<iterator, bool>(found, false);
84 }
85
86 found = _points.insert(x).first;
87 _points_list.push_back(x);
88
89 x->updateState();
90
91 if (to_update) {
92 _update();
93 }
94 if (notify) {
95 signal_selection_changed.emit(std::vector<key_type>(1, x), true);
96 }
97
98 return std::pair<iterator, bool>(found, true);
99}
100
102void ControlPointSelection::erase(iterator pos, bool to_update)
103{
104 SelectableControlPoint *erased = *pos;
105 _points_list.remove(*pos);
106 _points.erase(pos);
107 erased->updateState();
108 if (to_update) {
109 _update();
110 }
111}
113{
114 iterator pos = _points.find(k);
115 if (pos == _points.end()) return 0;
116 erase(pos);
117
118 if (notify) {
119 signal_selection_changed.emit(std::vector<key_type>(1, k), false);
120 }
121 return 1;
122}
124{
125 std::vector<SelectableControlPoint *> out(first, last);
126 while (first != last) {
127 erase(first++, false);
128 }
129 _update();
130 signal_selection_changed.emit(out, false);
131}
132
135{
136 if (empty()) {
137 return;
138 }
139
140 std::vector<SelectableControlPoint *> out(begin(), end()); // begin() takes from _points
141 _points.clear();
142 _points_list.clear();
143 for (auto erased : out) {
144 erased->updateState();
145 }
146
147 _update();
148 signal_selection_changed.emit(out, false);
149}
150
153{
154 for (auto _all_point : _all_points) {
155 insert(_all_point, false, false);
156 }
157 std::vector<SelectableControlPoint *> out(_all_points.begin(), _all_points.end());
158 if (!out.empty()) {
159 _update();
160 signal_selection_changed.emit(out, true);
161 }
162}
165{
166 std::vector<SelectableControlPoint *> out;
167 for (auto _all_point : _all_points) {
168 if (path.winding(_all_point->position()) % 2 != 0) {
169 if (invert) {
170 erase(_all_point, false);
171 } else {
172 insert(_all_point, false, false);
173 }
174 out.push_back(_all_point);
175 }
176 }
177 if (!out.empty()) {
178 _update();
179 signal_selection_changed.emit(out, true);
180 }
181}
184{
185 std::vector<SelectableControlPoint *> in, out;
186 for (auto _all_point : _all_points) {
187 if (_all_point->selected()) {
188 in.push_back(_all_point);
189 erase(_all_point, false);
190 }
191 else {
192 out.push_back(_all_point);
193 insert(_all_point, false, false);
194 }
195 }
196 _update();
197 if (!in.empty())
198 signal_selection_changed.emit(in, false);
199 if (!out.empty())
200 signal_selection_changed.emit(out, true);
201}
203{
204 bool grow = (dir > 0);
205 Geom::Point p = origin->position();
206 double best_dist = grow ? HUGE_VAL : 0;
207 SelectableControlPoint *match = nullptr;
208 for (auto _all_point : _all_points) {
209 bool selected = _all_point->selected();
210 if (grow && !selected) {
211 double dist = Geom::distance(_all_point->position(), p);
212 if (dist < best_dist) {
213 best_dist = dist;
214 match = _all_point;
215 }
216 }
217 if (!grow && selected) {
218 double dist = Geom::distance(_all_point->position(), p);
219 // use >= to also deselect the origin node when it's the last one selected
220 if (dist >= best_dist) {
221 best_dist = dist;
222 match = _all_point;
223 }
224 }
225 }
226 if (match) {
227 if (grow) insert(match);
228 else erase(match);
229 signal_selection_changed.emit(std::vector<value_type>(1, match), grow);
230 }
231}
232
235{
236 for (auto cur : _points) {
237 cur->transform(m);
238 }
239 for (auto cur : _points) {
240 cur->fixNeighbors();
241 }
242
244 // TODO preserving the rotation radius needs some rethinking...
245 if (_rot_radius) (*_rot_radius) *= m.descrim();
246 if (_mouseover_rot_radius) (*_mouseover_rot_radius) *= m.descrim();
247 signal_update.emit();
248}
249
252{
253 if (empty()) return;
254 Geom::Dim2 d = static_cast<Geom::Dim2>((axis + 1) % 2);
255
256 Geom::OptInterval bound;
257 for (auto _point : _points) {
258 bound.unionWith(Geom::OptInterval(_point->position()[d]));
259 }
260
261 if (!bound) { return; }
262
263 double new_coord;
264 switch (target) {
266 new_coord=(_points_list.front())->position()[d];
267 break;
269 new_coord=(_points_list.back())->position()[d];
270 break;
272 new_coord=bound->middle();
273 break;
275 new_coord=bound->min();
276 break;
278 new_coord=bound->max();
279 break;
280 default:
281 return;
282 }
283
284 for (auto _point : _points) {
285 Geom::Point pos = _point->position();
286 pos[d] = new_coord;
287 _point->move(pos);
288 }
289}
290
293{
294 if (empty()) return;
295
296 // this needs to be a multimap, otherwise it will fail when some points have the same coord
297 typedef std::multimap<double, SelectableControlPoint*> SortMap;
298
299 SortMap sm;
300 Geom::OptInterval bound;
301 // first we insert all points into a multimap keyed by the aligned coord to sort them
302 // simultaneously we compute the extent of selection
303 for (auto _point : _points) {
304 Geom::Point pos = _point->position();
305 sm.insert(std::make_pair(pos[d], _point));
306 bound.unionWith(Geom::OptInterval(pos[d]));
307 }
308
309 if (!bound) { return; }
310
311 // now we iterate over the multimap and set aligned positions.
312 double step = size() == 1 ? 0 : bound->extent() / (size() - 1);
313 double start = bound->min();
314 unsigned num = 0;
315 for (SortMap::iterator i = sm.begin(); i != sm.end(); ++i, ++num) {
316 Geom::Point pos = i->second->position();
317 pos[d] = start + num * step;
318 i->second->move(pos);
319 }
320}
321
329
331{
332 return size() == 1 ? (*_points.begin())->bounds() : _bounds;
333}
334
339std::optional<Geom::Point> ControlPointSelection::firstSelectedPoint() const
340{
341 return _first_point;
342}
343
345{
346 _one_node_handles = one_node;
349}
350
359
371
373{
375 _dragging = true;
376 _grabbed_point = point;
377 _farthest_point = point;
378 double maxdist = 0;
379 Geom::Affine m;
380 m.setIdentity();
381 for (auto _point : _points) {
382 _original_positions.insert(std::make_pair(_point, _point->position()));
383 _last_trans.insert(std::make_pair(_point, m));
384 double dist = Geom::distance(_grabbed_point->position(), _point->position());
385 if (dist > maxdist) {
386 maxdist = dist;
387 _farthest_point = _point;
388 }
389 }
390}
391
393{
394 Geom::Point abs_delta = new_pos - _original_positions[_grabbed_point];
396 if (held_only_alt(event) && fdist > 0) {
397 // Sculpting
398 for (auto cur : _points) {
399 Geom::Affine trans;
400 trans.setIdentity();
402 double deltafrac = 0.5 + 0.5 * cos(M_PI * dist/fdist);
403 if (dist != 0.0) {
404 // The sculpting transformation is not affine, but it can be
405 // locally approximated by one. Here we compute the local
406 // affine approximation of the sculpting transformation near
407 // the currently transformed point. We then transform the point
408 // by this approximation. This gives us sensible behavior for node handles.
409 // NOTE: probably it would be better to transform the node handles,
410 // but ControlPointSelection is supposed to work for any
411 // SelectableControlPoints, not only Nodes. We could create a specialized
412 // NodeSelection class that inherits from this one and move sculpting there.
413 Geom::Point origdx(Geom::EPSILON, 0);
414 Geom::Point origdy(0, Geom::EPSILON);
415 Geom::Point origp = _original_positions[cur];
416 Geom::Point origpx = _original_positions[cur] + origdx;
417 Geom::Point origpy = _original_positions[cur] + origdy;
418 double distdx = Geom::distance(origpx, _original_positions[_grabbed_point]);
419 double distdy = Geom::distance(origpy, _original_positions[_grabbed_point]);
420 double deltafracdx = 0.5 + 0.5 * cos(M_PI * distdx/fdist);
421 double deltafracdy = 0.5 + 0.5 * cos(M_PI * distdy/fdist);
422 Geom::Point newp = origp + abs_delta * deltafrac;
423 Geom::Point newpx = origpx + abs_delta * deltafracdx;
424 Geom::Point newpy = origpy + abs_delta * deltafracdy;
425 Geom::Point newdx = (newpx - newp) / Geom::EPSILON;
426 Geom::Point newdy = (newpy - newp) / Geom::EPSILON;
427
428 Geom::Affine itrans(newdx[Geom::X], newdx[Geom::Y], newdy[Geom::X], newdy[Geom::Y], 0, 0);
429 if (itrans.isSingular())
430 itrans.setIdentity();
431
432 trans *= Geom::Translate(-cur->position());
433 trans *= _last_trans[cur].inverse();
434 trans *= itrans;
435 trans *= Geom::Translate(_original_positions[cur] + abs_delta * deltafrac);
436 _last_trans[cur] = itrans;
437 } else {
438 trans *= Geom::Translate(-cur->position() + _original_positions[cur] + abs_delta * deltafrac);
439 }
440 cur->transform(trans);
441 //cur->move(_original_positions[cur] + abs_delta * deltafrac);
442 }
443 } else {
445 for (auto cur : _points) {
446 cur->move(_original_positions[cur] + abs_delta);
447 }
449 }
450 for (auto cur : _points) {
451 cur->fixNeighbors();
452 }
453 signal_update.emit();
454}
455
467
469{
470 // clicking a selected node should toggle the transform handles between rotate and scale mode,
471 // if they are visible
474 return true;
475 }
476 return false;
477}
478
483
485{
488 if (_bounds) {
489 _handles->rotationCenter().move(_bounds->midpoint());
490 }
491 // This records the first node's position, ONLY if it was individually selected
492 // Any clearing and this first position is cleared too. Any more and we remember it unchanged.
493 if (empty()) {
494 _first_point = {};
495 } else if (size() == 1) {
496 _first_point = (*begin())->position();
497 }
498}
499
501{
502 _rot_radius = std::nullopt;
504 for (auto cur : _points) {
505 Geom::Point p = cur->position();
506 if (!_bounds) {
507 _bounds = Geom::Rect(p, p);
508 } else {
509 _bounds->expandTo(p);
510 }
511 }
512}
513
515{
516 if (_dragging) return;
517
518 if (_handles_visible && size() > 1) {
519 _handles->setBounds(*bounds(), preserve_center);
520 _handles->setVisible(true);
521 } else if (_one_node_handles && size() == 1) { // only one control point in selection
526 _handles->setVisible(true);
527 } else {
528 _handles->setVisible(false);
529 }
530}
531
535{
536 if (held_ctrl(event)) return false;
537 unsigned num = 1 + Tools::gobble_key_events(event.keyval, 0);
538
539 auto prefs = Preferences::get();
540
541 Geom::Point delta = dir * num;
542 if (held_shift(event)) delta *= 10;
543 if (held_alt(event)) {
545 } else {
546 double nudge = prefs->getDoubleLimited("/options/nudgedistance/value", 2, 0, 1000, "px");
547 delta *= nudge;
548 }
549
550 bool const rotated = prefs->getBool("/options/moverotated/value", true);
551 if (rotated) {
553 }
554
557 return true;
558}
559
565{
566 if (empty()) return 1.0; // some safe value
567 Geom::Rect b = *bounds();
568 double maxlen = 0;
569 for (unsigned i = 0; i < 4; ++i) {
570 double len = Geom::distance(b.corner(i), rc);
571 if (len > maxlen) maxlen = len;
572 }
573 return maxlen;
574}
575
583{
584 if (empty()) return false;
585
587
588 // rotate around the mouseovered point, or the selection's rotation center
589 // if nothing is mouseovered
590 double radius;
593 if (scp) {
594 rc = scp->position();
597 }
598 radius = *_mouseover_rot_radius;
599 } else {
601 if (!_rot_radius) {
603 }
604 radius = *_rot_radius;
605 }
606
607 double angle;
608 if (held_alt(event)) {
609 // Rotate by "one pixel". We interpret this as rotating by an angle that causes
610 // the topmost point of a circle circumscribed about the selection's bounding box
611 // to move on an arc 1 screen pixel long.
612 angle = atan2(1.0 / _desktop->current_zoom(), radius) * dir;
613 } else {
615 int snaps = prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000);
616 angle = M_PI * dir / snaps;
617 }
618
619 // translate to origin, rotate, translate back to original position
621 * Geom::Rotate(angle) * Geom::Translate(rc);
622 transform(m);
624 return true;
625}
626
628{
629 if (empty()) return false;
630
631 double maxext = bounds()->maxExtent();
632 if (Geom::are_near(maxext, 0)) return false;
633
634 Geom::Point center;
637 if (scp) {
638 center = scp->position();
639 } else {
640 center = _handles->rotationCenter().position();
641 }
642
643 double length_change;
644 if (held_alt(event)) {
645 // Scale by "one pixel". It means shrink/grow 1px for the larger dimension
646 // of the bounding box.
647 length_change = 1.0 / _desktop->current_zoom() * dir;
648 } else {
650 length_change = prefs->getDoubleLimited("/options/defaultscale/value", 2, 1, 1000, "px");
651 length_change *= dir;
652 }
653 double scale = (maxext + length_change) / maxext;
654
656 transform(m);
658 return true;
659}
660
662{
663 if (empty()) return false;
664
665 Geom::Scale scale_transform(1, 1);
666 if (d == Geom::X) {
667 scale_transform = Geom::Scale(-1, 1);
668 } else {
669 scale_transform = Geom::Scale(1, -1);
670 }
671
674 Geom::Point center = scp ? scp->position() : _handles->rotationCenter().position();
675
676 Geom::Affine m = Geom::Translate(-center) * scale_transform * Geom::Translate(center);
677 transform(m);
679 return true;
680}
681
688
690{
691 // implement generic event handling that should apply for all control point selections here;
692 // for example, keyboard moves and transformations. This way this functionality doesn't need
693 // to be duplicated in many places
694 // Later split out so that it can be reused in object selection
695
696 if (event.type() != EventType::KEY_PRESS || empty()) {
697 return false;
698 }
699
700 auto &keyevent = static_cast<KeyPressEvent const &>(event);
701
702 switch (keyevent.keyval) {
703 // moves
704 case GDK_KEY_Up:
705 case GDK_KEY_KP_Up:
706 case GDK_KEY_KP_8:
707 return _keyboardMove(keyevent, Geom::Point(0, -_desktop->yaxisdir()));
708 case GDK_KEY_Down:
709 case GDK_KEY_KP_Down:
710 case GDK_KEY_KP_2:
711 return _keyboardMove(keyevent, Geom::Point(0, _desktop->yaxisdir()));
712 case GDK_KEY_Right:
713 case GDK_KEY_KP_Right:
714 case GDK_KEY_KP_6:
715 return _keyboardMove(keyevent, Geom::Point(1, 0));
716 case GDK_KEY_Left:
717 case GDK_KEY_KP_Left:
718 case GDK_KEY_KP_4:
719 return _keyboardMove(keyevent, Geom::Point(-1, 0));
720
721 // rotates
722 case GDK_KEY_bracketleft:
723 return _keyboardRotate(keyevent, -_desktop->yaxisdir());
724 case GDK_KEY_bracketright:
725 return _keyboardRotate(keyevent, _desktop->yaxisdir());
726
727 // scaling
728 case GDK_KEY_less:
729 case GDK_KEY_comma:
730 return _keyboardScale(keyevent, -1);
731 case GDK_KEY_greater:
732 case GDK_KEY_period:
733 return _keyboardScale(keyevent, 1);
734
735 // TODO: skewing
736
737 // flipping
738 // NOTE: H is horizontal flip, while Shift+H switches transform handle mode!
739 case GDK_KEY_h:
740 case GDK_KEY_H:
741 if (held_shift(keyevent)) {
743 return true;
744 }
745 // any modifiers except shift should cause no action
746 if (held_any_modifiers(keyevent)) break;
747 return _keyboardFlip(Geom::X);
748 case GDK_KEY_v:
749 case GDK_KEY_V:
750 if (held_any_modifiers(keyevent)) break;
751 return _keyboardFlip(Geom::Y);
752 default:
753 break;
754 }
755
756 return false;
757}
758
759void ControlPointSelection::getOriginalPoints(std::vector<Inkscape::SnapCandidatePoint> &pts)
760{
761 pts.clear();
762 for (auto _point : _points) {
763 pts.emplace_back(_original_positions[_point], SNAPSOURCE_NODE_HANDLE);
764 }
765}
766
767void ControlPointSelection::getUnselectedPoints(std::vector<Inkscape::SnapCandidatePoint> &pts)
768{
769 pts.clear();
770 ControlPointSelection::Set &nodes = this->allPoints();
771 for (auto node : nodes) {
772 if (!node->selected()) {
773 Node *n = static_cast<Node*>(node);
774 pts.push_back(n->snapCandidatePoint());
775 }
776 }
777}
778
780{
781 _original_positions.clear();
782 for (auto _point : _points) {
783 _original_positions.insert(std::make_pair(_point, _point->position()));
784 }
785}
786
787} // namespace UI
788} // namespace Inkscape
789
790/*
791 Local Variables:
792 mode:c++
793 c-file-style:"stroustrup"
794 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
795 indent-tabs-mode:nil
796 fill-column:99
797 End:
798*/
799// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
double scale
Definition aa.cpp:228
Point origin
Definition aa.cpp:227
Inkscape canvas widget.
3x3 matrix representing an affine transformation.
Definition affine.h:70
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
bool isSingular(Coord eps=EPSILON) const
Check whether this matrix is singular.
Definition affine.cpp:377
void setIdentity()
Sets this matrix to be the Identity Affine.
Definition affine.cpp:96
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
constexpr void unionWith(GenericOptInterval< C > const &a)
Union with another interval, gracefully handling empty ones.
void expandTo(CPoint const &p)
Create or enlarge the rectangle to contain the given point.
CPoint corner(unsigned i) const
Return the n-th corner of the rectangle.
Range of real numbers that can be empty.
Definition interval.h:199
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Sequence of contiguous curves, aka spline.
Definition path.h:353
int winding(Point const &p) const
Determine the winding number at the specified point.
Definition path.cpp:595
Two-dimensional point that doubles as a vector.
Definition point.h:66
constexpr Coord x() const noexcept
Definition point.h:104
Axis aligned, non-empty rectangle.
Definition rect.h:92
Rotation around the origin.
Definition transforms.h:187
Rotate inverse() const
Definition transforms.h:209
Scaling from the origin.
Definition transforms.h:150
Translation by a vector.
Definition transforms.h:115
void remove_snaptarget(bool only_if_presnap=false)
Preference storage class.
Definition preferences.h:66
static Preferences * get()
Access the singleton Preferences object.
int getIntLimited(Glib::ustring const &pref_path, int def=0, int min=INT_MIN, int max=INT_MAX)
Retrieve a limited integer.
double getDoubleLimited(Glib::ustring const &pref_path, double def=0.0, double min=DBL_MIN, double max=DBL_MAX, Glib::ustring const &unit="")
Retrieve a limited floating point value.
std::pair< iterator, bool > insert(const value_type &x, bool notify=true, bool to_update=true)
Add a control point to the selection.
ControlPointSelection(SPDesktop *d, Inkscape::CanvasItemGroup *th_group)
std::optional< Geom::Point > firstSelectedPoint() const
The first selected point is the first selection a user makes, but only if they selected exactly one p...
std::list< SelectableControlPoint * > _points_list
void _updateTransformHandles(bool preserve_center)
sigc::signal< void()> signal_update
Fires when the display needs to be updated to reflect changes.
void getUnselectedPoints(std::vector< Inkscape::SnapCandidatePoint > &pts)
void selectArea(Geom::Path const &, bool invert=false)
Select all points inside the given rectangle (in desktop coordinates).
sigc::signal< void(std::vector< SelectableControlPoint * >, bool)> signal_selection_changed
void _pointDragged(Geom::Point &, MotionEvent const &)
bool _keyboardRotate(KeyPressEvent const &, int)
Rotates the selected points in the given direction according to the modifier state from the supplied ...
void align(Geom::Dim2 d, AlignTargetNode target=AlignTargetNode::MID_NODE)
Align control points on the specified axis.
std::unordered_map< SelectableControlPoint *, Geom::Point > _original_positions
void erase(iterator pos, bool to_update=true)
Remove a point from the selection.
bool _keyboardMove(KeyPressEvent const &, Geom::Point const &)
Moves the selected points along the supplied unit vector according to the modifier state of the suppl...
Geom::OptRect pointwiseBounds()
Get the bounds of the selection.
std::optional< Geom::Point > _first_point
bool _keyboardScale(KeyPressEvent const &, int)
std::unordered_map< SelectableControlPoint *, Geom::Affine > _last_trans
void invertSelection()
Unselect all selected points and select all unselected points.
void getOriginalPoints(std::vector< Inkscape::SnapCandidatePoint > &pts)
bool event(Inkscape::UI::Tools::ToolBase *tool, CanvasEvent const &event) override
Handle input event. Returns true if handled.
double _rotationRadius(Geom::Point const &)
Computes the distance to the farthest corner of the bounding box.
bool _pointClicked(SelectableControlPoint *, ButtonReleaseEvent const &)
void _pointGrabbed(SelectableControlPoint *)
void showTransformHandles(bool v, bool one_node)
void transform(Geom::Affine const &m)
Transform all selected control points by the given affine transformation.
sigc::signal< void(CommitEvent)> signal_commit
Fires when a change that needs to be committed to XML happens.
void clear()
Remove all points from the selection, making it empty.
void spatialGrow(SelectableControlPoint *origin, int dir)
void selectAll()
Select all points that this selection can contain.
void distribute(Geom::Dim2 d)
Equdistantly distribute control points by moving them in the specified dimension.
Geom::Point const & position() const
Current position of the control point.
static sigc::signal< void(ControlPoint *)> signal_mouseover_change
Emitted when the mouseovered point changes.
virtual void move(Geom::Point const &pos)
Move the control point to new position with side effects.
static ControlPoint * mouseovered_point
Holds the currently mouseovered control point.
virtual void setVisible(bool v)
Set the visibility of the control point.
Tool component that processes events and does something in response to them.
Definition manipulator.h:34
SPDesktop *const _desktop
Definition manipulator.h:43
Desktop-bound selectable control object.
Base class for Event processors.
Definition tool-base.h:107
sigc::signal< void(CommitEvent)> signal_commit
void setMode(Mode m)
Sets the mode of transform handles (scale or rotate).
void setBounds(Geom::Rect const &, bool preserve_center=false)
ControlPoint const & rotationCenter() const
sigc::signal< void(Geom::Affine const &)> signal_transform
To do: update description of desktop.
Definition desktop.h:149
double current_zoom() const
Definition desktop.h:335
Inkscape::Display::SnapIndicator * getSnapIndicator() const
Definition desktop.h:193
Geom::Rotate const & current_rotation() const
Definition desktop.h:366
double yaxisdir() const
Definition desktop.h:426
RectangularCluster rc
Control point selection - stores a set of control points and applies transformations to them.
Editable view implementation.
Dim2
2D axis enumeration (X or Y).
Definition coord.h:48
constexpr Coord EPSILON
Default "acceptably small" value.
Definition coord.h:84
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Inkscape::XML::Node * node
Angle distance(Angle const &a, Angle const &b)
Definition angle.h:163
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
int gobble_key_events(unsigned keyval, unsigned mask)
Definition tool-base.h:243
CommitEvent
This is used to provide sensible messages on the undo stack.
@ COMMIT_KEYBOARD_SCALE_UNIFORM
Helper class to stream background task notifications as a series of messages.
bool held_ctrl(CanvasEvent const &event)
@ SNAPSOURCE_NODE_HANDLE
Definition snap-enums.h:43
bool held_shift(CanvasEvent const &event)
bool held_any_modifiers(CanvasEvent const &event)
bool held_alt(CanvasEvent const &event)
bool held_no_modifiers(CanvasEvent const &event)
bool held_only_alt(CanvasEvent const &event)
auto len
Definition safe-printf.h:21
int num
Definition scribble.cpp:47
Provides a class that shows a temporary indicator on the canvas of where the snap was,...
void invert(const double v[16], double alpha[16])
A mouse button (left/right/middle) is released.
Abstract base class for events.
A key has been pressed.
Movement of the mouse pointer.
int delta
Affine transform handles component.
Affine transformation classes.
Editable node and associated data structures.