Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
transform-handle-set.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/* Authors:
6 * Krzysztof KosiƄski <tweenk.pl@gmail.com>
7 * Jon A. Cruz <jon@joncruz.org>
8 *
9 * Copyright (C) 2009 Authors
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11 */
12
13#include <cmath>
14#include <algorithm>
15
16#include <glib/gi18n.h>
17
18#include <2geom/transforms.h>
19
20#include "control-point.h"
21#include "desktop.h"
22#include "pure-transform.h"
23#include "seltrans.h"
24#include "snap.h"
25
27
28#include "object/sp-namedview.h"
29
32#include "ui/tool/node.h"
34#include "ui/tools/node-tool.h"
36
37
39
40namespace Inkscape {
41namespace UI {
42
43namespace {
44
45SPAnchorType corner_to_anchor(unsigned c) {
46 switch (c % 4) {
47 case 0: return SP_ANCHOR_NE;
48 case 1: return SP_ANCHOR_NW;
49 case 2: return SP_ANCHOR_SW;
50 default: return SP_ANCHOR_SE;
51 }
52}
53
54SPAnchorType side_to_anchor(unsigned s) {
55 switch (s % 4) {
56 case 0: return SP_ANCHOR_N;
57 case 1: return SP_ANCHOR_W;
58 case 2: return SP_ANCHOR_S;
59 default: return SP_ANCHOR_E;
60 }
61}
62
63// TODO move those two functions into a common place
64double snap_angle(double a) {
66 int snaps = prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000);
67 double unit_angle = M_PI / snaps;
68 return CLAMP(unit_angle * round(a / unit_angle), -M_PI, M_PI);
69}
70
73 int snaps = prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000);
74 return 180.0 / snaps;
75}
76
77} // anonymous namespace
78
80 : ControlPoint(th._desktop, Geom::Point(), anchor, type, th._transform_handle_group)
81 , _th(th)
82{
83 _canvas_item_ctrl->set_name("CanvasItemCtrl:TransformHandle");
84 setVisible(false);
85}
86
87// TODO: This code is duplicated in seltrans.cpp; fix this!
89{
91 if (prefs->getBool("/options/snapclosestonly/value", false)) {
92 if (!_all_snap_sources_sorted.empty()) {
93 if (reverse) { // Shift-tab will find a closer point
96 }
98 } else { // Tab will find a point further away
102 }
103 }
104
105 _snap_points.clear();
107
108 // Show the updated snap source now; otherwise it won't be shown until the selection is being moved again
110 m.setup(_desktop);
112 m.unSetup();
113 }
114 }
115}
116
118{
119 _origin = position();
122
123 _th._setActiveHandle(this);
124 setVisible(false);
126
127 // Collect the snap-candidates, one for each selected node. These will be stored in the _snap_points vector.
128 auto nt = dynamic_cast<Tools::NodeTool*>(_th._desktop->getTool());
129 auto selection = nt->_selected_nodes;
130
131 selection->setOriginalPoints();
132 selection->getOriginalPoints(_snap_points);
133 selection->getUnselectedPoints(_unselected_points);
134
136 if (prefs->getBool("/options/snapclosestonly/value", false)) {
137 // Find the closest snap source candidate
139
140 // Calculate and store the distance to the reference point for each snap candidate point
141 for(auto & i : _all_snap_sources_sorted) {
142 i.setDistance(Geom::L2(i.getPoint() - _origin));
143 }
144
145 // Sort them ascending, using the distance calculated above as the single criteria
146 std::sort(_all_snap_sources_sorted.begin(), _all_snap_sources_sorted.end());
147
148 // Now get the closest snap source
149 _snap_points.clear();
150 if (!_all_snap_sources_sorted.empty()) {
152 _snap_points.push_back(_all_snap_sources_sorted.front());
153 }
154 }
155
156 return false;
157}
158
160{
161 auto const t = computeTransform(new_pos, event);
162 // protect against degeneracies
163 if (t.isSingular()) return;
165 if (incr.isSingular()) return;
166 _th.signal_transform.emit(incr);
167 _last_transform = t;
168}
169
171{
172 _snap_points.clear();
174 setVisible(true);
176 endTransform();
178
179 //updates the positions of the nodes
180 auto nt = dynamic_cast<Tools::NodeTool*>(_th._desktop->getTool());
181 auto selection = nt->_selected_nodes;
182 selection->setOriginalPoints();
183}
184
185class ScaleHandle : public TransformHandle
186{
187public:
189 : TransformHandle(th, anchor, type)
190 {}
191
192protected:
193 Glib::ustring _getTip(unsigned state) const override
194 {
195 if (state_held_ctrl(state)) {
196 if (state_held_shift(state)) {
197 return C_("Transform handle tip",
198 "<b>Shift+Ctrl</b>: scale uniformly about the rotation center");
199 }
200 return C_("Transform handle tip", "<b>Ctrl:</b> scale uniformly");
201 }
202 if (state_held_shift(state)) {
203 if (state_held_alt(state)) {
204 return C_("Transform handle tip",
205 "<b>Shift+Alt</b>: scale using an integer ratio about the rotation center");
206 }
207 return C_("Transform handle tip", "<b>Shift</b>: scale from the rotation center");
208 }
209 if (state_held_alt(state)) {
210 return C_("Transform handle tip", "<b>Alt</b>: scale using an integer ratio");
211 }
212 return C_("Transform handle tip", "<b>Scale handle</b>: drag to scale the selection");
213 }
214
215 Glib::ustring _getDragTip(MotionEvent const &/*event*/) const override
216 {
217 return format_tip(C_("Transform handle tip",
218 "Scale by %.2f%% x %.2f%%"), _last_scale_x * 100, _last_scale_y * 100);
219 }
220
221 bool _hasDragTips() const override { return true; }
222
223 static double _last_scale_x, _last_scale_y;
224};
225double ScaleHandle::_last_scale_x = 1.0;
226double ScaleHandle::_last_scale_y = 1.0;
227
231class ScaleCornerHandle : public ScaleHandle
232{
233public:
234 ScaleCornerHandle(TransformHandleSet &th, unsigned corner, unsigned d_corner)
235 : ScaleHandle(th, corner_to_anchor(d_corner), Inkscape::CANVAS_ITEM_CTRL_TYPE_ADJ_HANDLE)
236 , _corner(corner)
237 {}
238
239protected:
240 void startTransform() override
241 {
242 _sc_center = _th.rotationCenter().position();
243 _sc_opposite = _th.bounds().corner(_corner + 2);
244 _last_scale_x = _last_scale_y = 1.0;
245 }
246
247 Geom::Affine computeTransform(Geom::Point const &new_pos, MotionEvent const &event) override
248 {
249 Geom::Point scc = held_shift(event) ? _sc_center : _sc_opposite;
250 Geom::Point vold = _origin - scc, vnew = new_pos - scc;
251 // avoid exploding the selection
252 if (Geom::are_near(vold[Geom::X], 0) || Geom::are_near(vold[Geom::Y], 0))
253 return Geom::identity();
254
255 Geom::Scale scale = Geom::Scale(vnew[Geom::X] / vold[Geom::X], vnew[Geom::Y] / vold[Geom::Y]);
256
257 if (held_alt(event)) {
258 for (unsigned i = 0; i < 2; ++i) {
259 if (fabs(scale[i]) >= 1.0) {
260 scale[i] = round(scale[i]);
261 } else {
262 scale[i] = 1.0 / round(1.0 / MIN(scale[i],10));
263 }
264 }
265 } else {
268
270 if (held_ctrl(event)) {
271 scale[0] = scale[1] = std::min(scale[0], scale[1]);
272 ptr = new Inkscape::PureScaleConstrained(Geom::Scale(scale[0], scale[1]), scc);
273 } else {
274 ptr = new Inkscape::PureScale(Geom::Scale(scale[0], scale[1]), scc, false);
275 }
277 m.unSetup();
278 if (ptr->best_snapped_point.getSnapped()) {
279 scale = ptr->getScaleSnapped();
280 }
281
282 delete ptr;
283 }
284
285 _last_scale_x = scale[0];
286 _last_scale_y = scale[1];
288 * Geom::Scale(scale[0], scale[1])
289 * Geom::Translate(scc);
290 return t;
291 }
292
293 CommitEvent getCommitEvent() const override
294 {
298 }
299
300private:
301 Geom::Point _sc_center;
302 Geom::Point _sc_opposite;
303 unsigned _corner;
304};
305
309class ScaleSideHandle : public ScaleHandle
310{
311public:
312 ScaleSideHandle(TransformHandleSet &th, unsigned side, unsigned d_side)
313 : ScaleHandle(th, side_to_anchor(d_side), Inkscape::CANVAS_ITEM_CTRL_TYPE_ADJ_HANDLE)
314 , _side(side)
315 {}
316
317protected:
318 void startTransform() override
319 {
320 _sc_center = _th.rotationCenter().position();
321 Geom::Rect b = _th.bounds();
322 _sc_opposite = Geom::middle_point(b.corner(_side + 2), b.corner(_side + 3));
323 _last_scale_x = _last_scale_y = 1.0;
324 }
325
326 Geom::Affine computeTransform(Geom::Point const &new_pos, MotionEvent const &event) override
327 {
328 Geom::Point scc = held_shift(event) ? _sc_center : _sc_opposite;
329 Geom::Point vs;
330 Geom::Dim2 d1 = static_cast<Geom::Dim2>((_side + 1) % 2);
331 Geom::Dim2 d2 = static_cast<Geom::Dim2>(_side % 2);
332
333 // avoid exploding the selection
334 if (Geom::are_near(scc[d1], _origin[d1]))
335 return Geom::identity();
336
337 vs[d1] = (new_pos - scc)[d1] / (_origin - scc)[d1];
338 if (held_alt(event)) {
339 if (std::abs(vs[d1]) >= 1.0) {
340 vs[d1] = std::round(vs[d1]);
341 } else {
342 vs[d1] = 1.0 / std::round(1.0 / std::min(vs[d1], 10.0));
343 }
344 vs[d2] = 1.0;
345 } else {
348
349 bool uniform = held_ctrl(event);
350 auto psc = Inkscape::PureStretchConstrained(vs[d1], scc, d1, uniform);
352 m.unSetup();
353
354 if (psc.best_snapped_point.getSnapped()) {
355 Geom::Point result = psc.getStretchSnapped().vector(); //best_snapped_point.getTransformation();
356 vs[d1] = result[d1];
357 vs[d2] = result[d2];
358 } else {
359 // on ctrl, apply uniform scaling instead of stretching
360 // Preserve aspect ratio, but never flip in the dimension not being edited (by using fabs())
361 vs[d2] = uniform ? fabs(vs[d1]) : 1.0;
362 }
363 }
364
365 _last_scale_x = vs[Geom::X];
366 _last_scale_y = vs[Geom::Y];
368 * Geom::Scale(vs)
369 * Geom::Translate(scc);
370 return t;
371 }
372
373 CommitEvent getCommitEvent() const override
374 {
378 }
379
380private:
381 Geom::Point _sc_center;
382 Geom::Point _sc_opposite;
383 unsigned _side;
384};
385
389class RotateHandle : public TransformHandle
390{
391public:
392 RotateHandle(TransformHandleSet &th, unsigned corner, unsigned d_corner)
393 : TransformHandle(th, corner_to_anchor(d_corner), Inkscape::CANVAS_ITEM_CTRL_TYPE_ADJ_ROTATE)
394 , _corner(corner)
395 {}
396
397protected:
398 void startTransform() override
399 {
400 _rot_center = _th.rotationCenter().position();
401 _rot_opposite = _th.bounds().corner(_corner + 2);
402 _last_angle = 0;
403 }
404
405 Geom::Affine computeTransform(Geom::Point const &new_pos, MotionEvent const &event) override
406 {
407 Geom::Point rotc = held_shift(event) ? _rot_opposite : _rot_center;
408 double angle = Geom::angle_between(_origin - rotc, new_pos - rotc);
409 if (held_ctrl(event)) {
410 angle = snap_angle(angle);
411 } else {
416 m.unSetup();
417
418 if (prc.best_snapped_point.getSnapped()) {
419 angle = prc.getAngleSnapped(); //best_snapped_point.getTransformation()[0];
420 }
421 }
422
423 _last_angle = angle;
425 * Geom::Rotate(angle)
426 * Geom::Translate(rotc);
427 return t;
428 }
429
430 CommitEvent getCommitEvent() const override { return COMMIT_MOUSE_ROTATE; }
431
432 Glib::ustring _getTip(unsigned state) const override
433 {
434 if (state_held_shift(state)) {
435 if (state_held_ctrl(state)) {
436 return format_tip(C_("Transform handle tip",
437 "<b>Shift+Ctrl</b>: rotate around the opposite corner and snap "
438 "angle to %f° increments"), snap_increment_degrees());
439 }
440 return C_("Transform handle tip", "<b>Shift</b>: rotate around the opposite corner");
441 }
442 if (state_held_ctrl(state)) {
443 return format_tip(C_("Transform handle tip",
444 "<b>Ctrl</b>: snap angle to %f° increments"), snap_increment_degrees());
445 }
446 return C_("Transform handle tip", "<b>Rotation handle</b>: drag to rotate "
447 "the selection around the rotation center");
448 }
449
450 Glib::ustring _getDragTip(MotionEvent const &/*event*/) const override
451 {
452 return format_tip(C_("Transform handle tip", "Rotate by %.2f°"),
453 _last_angle * 180.0 / M_PI);
454 }
455
456 bool _hasDragTips() const override { return true; }
457
458private:
459 Geom::Point _rot_center;
460 Geom::Point _rot_opposite;
461 unsigned _corner;
462 static double _last_angle;
463};
464double RotateHandle::_last_angle = 0;
465
466class SkewHandle : public TransformHandle
467{
468public:
469 SkewHandle(TransformHandleSet &th, unsigned side, unsigned d_side)
470 : TransformHandle(th, side_to_anchor(d_side), Inkscape::CANVAS_ITEM_CTRL_TYPE_ADJ_SKEW)
471 , _side(side)
472 {}
473
474protected:
475 void startTransform() override
476 {
477 _skew_center = _th.rotationCenter().position();
478 Geom::Rect b = _th.bounds();
479 _skew_opposite = Geom::middle_point(b.corner(_side + 2), b.corner(_side + 3));
480 _last_angle = 0;
481 _last_horizontal = _side % 2;
482 }
483
484 Geom::Affine computeTransform(Geom::Point const &new_pos, MotionEvent const &event) override
485 {
486 Geom::Point scc = held_shift(event) ? _skew_center : _skew_opposite;
487 Geom::Dim2 d1 = static_cast<Geom::Dim2>((_side + 1) % 2);
488 Geom::Dim2 d2 = static_cast<Geom::Dim2>(_side % 2);
489
490 Geom::Point const initial_delta = _origin - scc;
491
492 if (fabs(initial_delta[d1]) < 1e-15) {
493 return Geom::Affine();
494 }
495
496 // Calculate the scale factors, which can be either visual or geometric
497 // depending on which type of bbox is currently being used (see preferences -> selector tool)
498 Geom::Scale scale = calcScaleFactors(_origin, new_pos, scc, false);
499 Geom::Scale skew = calcScaleFactors(_origin, new_pos, scc, true);
500 scale[d2] = 1;
501 skew[d2] = 1;
502
503 // Skew handles allow scaling up to integer multiples of the original size
504 // in the second direction; prevent explosions
505
506 if (fabs(scale[d1]) < 1) {
507 // Prevent shrinking of the selected object, while allowing mirroring
508 scale[d1] = copysign(1.0, scale[d1]);
509 } else {
510 // Allow expanding of the selected object by integer multiples
511 scale[d1] = floor(scale[d1] + 0.5);
512 }
513
514 double angle = atan(skew[d1] / scale[d1]);
515
516 if (held_ctrl(event)) {
517 angle = snap_angle(angle);
518 skew[d1] = tan(angle) * scale[d1];
519 } else {
522
523 Inkscape::PureSkewConstrained psc = Inkscape::PureSkewConstrained(skew[d1], scale[d1], scc, d2);
525 m.unSetup();
526
527 if (psc.best_snapped_point.getSnapped()) {
528 skew[d1] = psc.getSkewSnapped(); //best_snapped_point.getTransformation()[0];
529 }
530 }
531
532 _last_angle = angle;
533
534 // Update the handle position
535 Geom::Point new_new_pos;
536 new_new_pos[d2] = initial_delta[d1] * skew[d1] + _origin[d2];
537 new_new_pos[d1] = initial_delta[d1] * scale[d1] + scc[d1];
538
539 // Calculate the relative affine
540 Geom::Affine relative_affine = Geom::identity();
541 relative_affine[2*d1 + d1] = (new_new_pos[d1] - scc[d1]) / initial_delta[d1];
542 relative_affine[2*d1 + (d2)] = (new_new_pos[d2] - _origin[d2]) / initial_delta[d1];
543 relative_affine[2*(d2) + (d1)] = 0;
544 relative_affine[2*(d2) + (d2)] = 1;
545
546 for (int i = 0; i < 2; i++) {
547 if (fabs(relative_affine[3*i]) < 1e-15) {
548 relative_affine[3*i] = 1e-15;
549 }
550 }
551
553 * relative_affine
554 * Geom::Translate(scc);
555
556 return t;
557 }
558
559 CommitEvent getCommitEvent() const override
560 {
561 return (_side % 2)
564 }
565
566 Glib::ustring _getTip(unsigned state) const override
567 {
568 if (state_held_shift(state)) {
569 if (state_held_ctrl(state)) {
570 return format_tip(C_("Transform handle tip",
571 "<b>Shift+Ctrl</b>: skew about the rotation center with snapping "
572 "to %f° increments"), snap_increment_degrees());
573 }
574 return C_("Transform handle tip", "<b>Shift</b>: skew about the rotation center");
575 }
576 if (state_held_ctrl(state)) {
577 return format_tip(C_("Transform handle tip",
578 "<b>Ctrl</b>: snap skew angle to %f° increments"), snap_increment_degrees());
579 }
580 return C_("Transform handle tip",
581 "<b>Skew handle</b>: drag to skew (shear) selection about "
582 "the opposite handle");
583 }
584
585 Glib::ustring _getDragTip(MotionEvent const &/*event*/) const override
586 {
587 if (_last_horizontal) {
588 return format_tip(C_("Transform handle tip", "Skew horizontally by %.2f°"),
589 _last_angle * 360.0);
590 } else {
591 return format_tip(C_("Transform handle tip", "Skew vertically by %.2f°"),
592 _last_angle * 360.0);
593 }
594 }
595
596 bool _hasDragTips() const override { return true; }
597
598private:
599 Geom::Point _skew_center;
600 Geom::Point _skew_opposite;
601 unsigned _side;
602 static bool _last_horizontal;
603 static double _last_angle;
604};
605bool SkewHandle::_last_horizontal = false;
606double SkewHandle::_last_angle = 0;
607
608class RotationCenter : public ControlPoint
609{
610public:
611 RotationCenter(TransformHandleSet &th)
614 th._transform_handle_group)
615 , _th(th)
616 {
617 setVisible(false);
618 }
619
620protected:
621 void dragged(Geom::Point &new_pos, MotionEvent const &event) override
622 {
623 auto &sm = _th._desktop->getNamedView()->snap_manager;
624 sm.setup(_th._desktop);
625 bool snap = !held_shift(event) && sm.someSnapperMightSnap();
626 if (held_ctrl(event)) {
627 // constrain to axes
629 std::vector<Inkscape::Snapper::SnapConstraint> constraints;
630 constraints.emplace_back(origin, Geom::Point(1, 0));
631 constraints.emplace_back(origin, Geom::Point(0, 1));
632 new_pos = sm.multipleConstrainedSnaps(Inkscape::SnapCandidatePoint(new_pos,
633 SNAPSOURCE_ROTATION_CENTER), constraints, held_shift(event)).getPoint();
634 } else if (snap) {
635 sm.freeSnapReturnByRef(new_pos, SNAPSOURCE_ROTATION_CENTER);
636 }
637 sm.unSetup();
638 }
639
640 Glib::ustring _getTip(unsigned /*state*/) const override
641 {
642 return C_("Transform handle tip", "<b>Rotation center</b>: drag to change the origin of transforms");
643 }
644
645private:
646 TransformHandleSet &_th;
647};
648
650 : Manipulator(d)
651 , _active(nullptr)
652 , _transform_handle_group(th_group)
653 , _mode(MODE_SCALE)
654 , _in_transform(false)
655 , _visible(true)
656{
658 _trans_outline->set_name("CanvasItemRect:Transform");
661
662 bool y_inverted = !d->is_yaxisdown();
663 for (unsigned i = 0; i < 4; ++i) {
664 unsigned d_c = y_inverted ? i : 3 - i;
665 unsigned d_s = y_inverted ? i : 6 - i;
666 _scale_corners[i] = new ScaleCornerHandle(*this, i, d_c);
667 _scale_sides[i] = new ScaleSideHandle(*this, i, d_s);
668 _rot_corners[i] = new RotateHandle(*this, i, d_c);
669 _skew_sides[i] = new SkewHandle(*this, i, d_s);
670 }
671 _center = new RotationCenter(*this);
672 // when transforming, update rotation center position
673 signal_transform.connect(sigc::mem_fun(*_center, &RotationCenter::transform));
674}
675
677{
678 for (auto &_handle : _handles) {
679 delete _handle;
680 }
681}
682
688
690{
691 return Geom::Rect(_scale_corners[0]->position(), _scale_corners[2]->position());
692}
693
695{
696 return *_center;
697}
698
703
705{
706 if (_visible != v) {
707 _visible = v;
709 }
710}
711
712void TransformHandleSet::setBounds(Geom::Rect const &r, bool preserve_center)
713{
714 if (_in_transform) {
716 } else {
717 for (unsigned i = 0; i < 4; ++i) {
718 _scale_corners[i]->move(r.corner(i));
719 _scale_sides[i]->move(Geom::middle_point(r.corner(i), r.corner(i+1)));
720 _rot_corners[i]->move(r.corner(i));
721 _skew_sides[i]->move(Geom::middle_point(r.corner(i), r.corner(i+1)));
722 }
723 if (!preserve_center) _center->move(r.midpoint());
724 if (_visible) _updateVisibility(true);
725 }
726}
727
729{
730 return false;
731}
732
734{
735 signal_transform.emit(t);
736 _center->transform(t);
737}
738
740{
741 _active = th;
742 if (_in_transform)
743 throw std::logic_error("Transform initiated when another transform in progress");
744 _in_transform = true;
745 // hide all handles except the active one
746 _updateVisibility(false);
748}
749
751{
752 // This can only be called from handles, so they had to be visible before _setActiveHandle
754 _active = nullptr;
755 _in_transform = false;
757}
758
760{
761 if (v) {
762 Geom::Rect b = bounds();
763
764 // Roughly estimate handle size.
766 int handle_index = prefs->getIntLimited("/options/grabsize/value", 3, 1, 15);
767 int handle_size = handle_index * 2 + 1; // Handle pixmaps are actually larger but that's to allow space when handle is rotated.
768
770
771 // do not scale when the bounding rectangle has zero width or height
772 bool show_scale = (_mode == MODE_SCALE) && !Geom::are_near(b.minExtent(), 0);
773 // do not rotate if the bounding rectangle is degenerate
774 bool show_rotate = (_mode == MODE_ROTATE_SKEW) && !Geom::are_near(b.maxExtent(), 0);
775 bool show_scale_side[2], show_skew[2];
776
777 // show sides if:
778 // a) there is enough space between corner handles, or
779 // b) corner handles are not shown, but side handles make sense
780 // this affects horizontal and vertical scale handles; skew handles never
781 // make sense if rotate handles are not shown
782 for (unsigned i = 0; i < 2; ++i) {
783 Geom::Dim2 d = static_cast<Geom::Dim2>(i);
784 Geom::Dim2 otherd = static_cast<Geom::Dim2>((i+1)%2);
785 show_scale_side[i] = (_mode == MODE_SCALE);
786 show_scale_side[i] &= (show_scale ? bp[d] >= handle_size
787 : !Geom::are_near(bp[otherd], 0));
788 show_skew[i] = (show_rotate && bp[d] >= handle_size
789 && !Geom::are_near(bp[otherd], 0));
790 }
791
792 for (unsigned i = 0; i < 4; ++i) {
793 _scale_corners[i]->setVisible(show_scale);
794 _rot_corners[i]->setVisible(show_rotate);
795 _scale_sides[i]->setVisible(show_scale_side[i%2]);
796 _skew_sides[i]->setVisible(show_skew[i%2]);
797 }
798
799 // show rotation center
800 _center->setVisible(show_rotate);
801 } else {
802 for (auto & _handle : _handles) {
803 if (_handle != _active)
804 _handle->setVisible(false);
805 }
806 }
807
808}
809
810} // namespace UI
811} // namespace Inkscape
812
813/*
814 Local Variables:
815 mode:c++
816 c-file-style:"stroustrup"
817 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
818 indent-tabs-mode:nil
819 fill-column:99
820 End:
821*/
822// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
pair< double, double > Point
Definition parser.cpp:7
double scale
Definition aa.cpp:228
Point origin
Definition aa.cpp:227
3x3 matrix representing an affine transformation.
Definition affine.h:70
bool isUniformScale(Coord eps=EPSILON) const
Check whether this matrix represents pure uniform scaling.
Definition affine.cpp:174
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
CPoint midpoint() const
Get the point in the geometric center of the rectangle.
C minExtent() const
Get the smaller extent (width or height) of the rectangle.
C maxExtent() const
Get the larger extent (width or height) of the rectangle.
CPoint dimensions() const
Get rectangle's width and height as a point.
CPoint corner(unsigned i) const
Return the n-th corner of the rectangle.
Two-dimensional point that doubles as a vector.
Definition point.h:66
Axis aligned, non-empty rectangle.
Definition rect.h:92
Rotation around the origin.
Definition transforms.h:187
Scaling from the origin.
Definition transforms.h:150
Translation by a vector.
Definition transforms.h:115
void set_rect(Geom::Rect const &rect)
Set a control rect.
void set_dashed(bool dash=true)
virtual void set_visible(bool visible)
void set_name(std::string &&name)
Definition canvas-item.h:97
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 getIntLimited(Glib::ustring const &pref_path, int def=0, int min=INT_MIN, int max=INT_MAX)
Retrieve a limited integer.
Geom::Scale getScaleSnapped()
SnappedPoint best_snapped_point
Class to store data for points which are snap candidates, either as a source or as a target.
Draggable point, the workhorse of on-canvas editing.
Geom::Point const & position() const
Current position of the control point.
static Glib::ustring format_tip(char const *format,...) G_GNUC_PRINTF(1
ControlPoint(ControlPoint const &other)=delete
virtual void setVisible(bool v)
Set the visibility of the control point.
virtual void _setState(State state)
Change the state of the knot.
SPDesktop *const _desktop
The desktop this control point resides on.
static Geom::Point const & _last_drag_origin()
CanvasItemPtr< Inkscape::CanvasItemCtrl > _canvas_item_ctrl
Visual representation of the control point.
virtual void transform(Geom::Affine const &m)
Apply an arbitrary affine transformation to a 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
Inkscape::UI::ControlPointSelection * _selected_nodes
Definition node-tool.h:50
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)
bool event(Inkscape::UI::Tools::ToolBase *tool, CanvasEvent const &event) override
Handle input event. Returns true if handled.
Inkscape::CanvasItemRect * _trans_outline
TransformHandleSet(SPDesktop *d, Inkscape::CanvasItemGroup *th_group)
void _updateVisibility(bool v)
Update the visibility of transformation handles according to settings and the dimensions of the bound...
void _emitTransform(Geom::Affine const &)
ControlPoint const & rotationCenter() const
sigc::signal< void(Geom::Affine const &)> signal_transform
Base class for node transform handles to simplify implementation.
std::vector< Inkscape::SnapCandidatePoint >::iterator _all_snap_sources_iter
virtual CommitEvent getCommitEvent() const =0
virtual Geom::Affine computeTransform(Geom::Point const &pos, MotionEvent const &event)=0
TransformHandle(TransformHandleSet &th, SPAnchorType anchor, Inkscape::CanvasItemCtrlType type)
bool grabbed(MotionEvent const &event) override
Called when the user moves the point beyond the drag tolerance with the first button held down.
std::vector< Inkscape::SnapCandidatePoint > _all_snap_sources_sorted
void dragged(Geom::Point &new_pos, MotionEvent const &event) override
Called while dragging, but before moving the knot to new position.
void ungrabbed(ButtonReleaseEvent const *event) override
Called when the control point finishes a drag.
std::vector< Inkscape::SnapCandidatePoint > _unselected_points
std::vector< Inkscape::SnapCandidatePoint > _snap_points
To do: update description of desktop.
Definition desktop.h:149
double current_zoom() const
Definition desktop.h:335
Inkscape::CanvasItemGroup * getCanvasControls() const
Definition desktop.h:196
SPNamedView * getNamedView() const
Definition desktop.h:191
Inkscape::UI::Tools::ToolBase * getTool() const
Definition desktop.h:187
bool is_yaxisdown() const
Definition desktop.h:427
SnapManager snap_manager
Class to coordinate snapping operations.
Definition snap.h:80
void displaySnapsource(Inkscape::SnapCandidatePoint const &p) const
Mark the location of the snap source (not the snap target!) on the canvas by drawing a symbol.
Definition snap.cpp:781
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 setupIgnoreSelection(SPDesktop const *desktop, bool snapindicator=true, std::vector< Inkscape::SnapCandidatePoint > *unselected_nodes=nullptr)
Setup, taking the list of items to ignore from the desktop's selection.
Definition snap.cpp:701
void snapTransformed(std::vector< Inkscape::SnapCandidatePoint > const &points, Geom::Point const &pointer, Inkscape::PureTransform &transform)
Method for snapping sets of points while they are being transformed.
Definition snap.cpp:465
void unSetup()
Definition snap.h:147
Commit events.
Control point selection - stores a set of control points and applies transformations to them.
Css & result
double c[8][4]
Editable view implementation.
SPAnchorType
Definition enums.h:18
@ SP_ANCHOR_W
Definition enums.h:33
@ SP_ANCHOR_CENTER
Definition enums.h:28
@ SP_ANCHOR_E
Definition enums.h:29
@ SP_ANCHOR_NW
Definition enums.h:34
@ SP_ANCHOR_SE
Definition enums.h:30
@ SP_ANCHOR_N
Definition enums.h:35
@ SP_ANCHOR_S
Definition enums.h:31
@ SP_ANCHOR_NE
Definition enums.h:36
@ SP_ANCHOR_SW
Definition enums.h:32
Dim2
2D axis enumeration (X or Y).
Definition coord.h:48
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
auto floor(Geom::Rect const &rect)
Definition geom.h:130
double angle(std::vector< Point > const &A)
Various utility functions.
Definition affine.h:22
double angle_between(Line const &l1, Line const &l2)
Definition line.h:456
Affine identity()
Create an identity matrix.
Definition affine.h:210
SBasis L2(D2< SBasis > const &a, unsigned k)
Definition d2-sbasis.cpp:42
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
Point middle_point(LineSegment const &_segment)
static double snap_increment_degrees()
Definition node.cpp:612
CommitEvent
This is used to provide sensible messages on the undo stack.
@ COMMIT_MOUSE_SCALE_UNIFORM
Helper class to stream background task notifications as a series of messages.
bool held_ctrl(CanvasEvent const &event)
@ SNAPSOURCE_ROTATION_CENTER
Definition snap-enums.h:52
bool state_held_shift(unsigned state)
Geom::Scale calcScaleFactors(Geom::Point const &initial_point, Geom::Point const &new_point, Geom::Point const &origin, bool const skew=false)
bool held_shift(CanvasEvent const &event)
bool held_alt(CanvasEvent const &event)
bool state_held_alt(unsigned state)
@ CANVAS_ITEM_CTRL_TYPE_ADJ_ROTATE
@ CANVAS_ITEM_CTRL_TYPE_ADJ_HANDLE
@ CANVAS_ITEM_CTRL_TYPE_ADJ_SKEW
@ CANVAS_ITEM_CTRL_TYPE_ADJ_CENTER
bool state_held_ctrl(unsigned state)
New node tool with support for multiple path editing.
A mouse button (left/right/middle) is released.
Abstract base class for events.
Movement of the mouse pointer.
double uniform()
GType sp_select_context_get_type()
Affine transform handles component.
Affine transformation classes.
Editable node and associated data structures.