16#include <glib/gi18n.h>
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);
73 int snaps = prefs->
getIntLimited(
"/options/rotationsnapsperpi/value", 12, 1, 1000);
91 if (prefs->
getBool(
"/options/snapclosestonly/value",
false)) {
136 if (prefs->
getBool(
"/options/snapclosestonly/value",
false)) {
163 if (t.isSingular())
return;
193 Glib::ustring _getTip(
unsigned state)
const override
197 return C_(
"Transform handle tip",
198 "<b>Shift+Ctrl</b>: scale uniformly about the rotation center");
200 return C_(
"Transform handle tip",
"<b>Ctrl:</b> scale uniformly");
204 return C_(
"Transform handle tip",
205 "<b>Shift+Alt</b>: scale using an integer ratio about the rotation center");
207 return C_(
"Transform handle tip",
"<b>Shift</b>: scale from the rotation center");
210 return C_(
"Transform handle tip",
"<b>Alt</b>: scale using an integer ratio");
212 return C_(
"Transform handle tip",
"<b>Scale handle</b>: drag to scale the selection");
215 Glib::ustring _getDragTip(MotionEvent
const &)
const override
218 "Scale by %.2f%% x %.2f%%"), _last_scale_x * 100, _last_scale_y * 100);
221 bool _hasDragTips()
const override {
return true; }
223 static double _last_scale_x, _last_scale_y;
225double ScaleHandle::_last_scale_x = 1.0;
226double ScaleHandle::_last_scale_y = 1.0;
231class ScaleCornerHandle :
public ScaleHandle
234 ScaleCornerHandle(TransformHandleSet &th,
unsigned corner,
unsigned d_corner)
240 void startTransform()
override
244 _last_scale_x = _last_scale_y = 1.0;
258 for (
unsigned i = 0; i < 2; ++i) {
259 if (fabs(scale[i]) >= 1.0) {
260 scale[i] = round(scale[i]);
262 scale[i] = 1.0 / round(1.0 /
MIN(scale[i],10));
271 scale[0] =
scale[1] = std::min(scale[0], scale[1]);
285 _last_scale_x =
scale[0];
286 _last_scale_y =
scale[1];
309class ScaleSideHandle :
public ScaleHandle
312 ScaleSideHandle(TransformHandleSet &th,
unsigned side,
unsigned d_side)
318 void startTransform()
override
323 _last_scale_x = _last_scale_y = 1.0;
337 vs[d1] = (new_pos - scc)[d1] / (
_origin - scc)[d1];
339 if (std::abs(vs[d1]) >= 1.0) {
340 vs[d1] = std::round(vs[d1]);
342 vs[d1] = 1.0 / std::round(1.0 / std::min(vs[d1], 10.0));
354 if (psc.best_snapped_point.getSnapped()) {
361 vs[d2] =
uniform ? fabs(vs[d1]) : 1.0;
389class RotateHandle :
public TransformHandle
392 RotateHandle(TransformHandleSet &th,
unsigned corner,
unsigned d_corner)
398 void startTransform()
override
410 angle = snap_angle(angle);
432 Glib::ustring _getTip(
unsigned state)
const override
437 "<b>Shift+Ctrl</b>: rotate around the opposite corner and snap "
440 return C_(
"Transform handle tip",
"<b>Shift</b>: rotate around the opposite corner");
446 return C_(
"Transform handle tip",
"<b>Rotation handle</b>: drag to rotate "
447 "the selection around the rotation center");
450 Glib::ustring _getDragTip(MotionEvent
const &)
const override
452 return format_tip(C_(
"Transform handle tip",
"Rotate by %.2f°"),
453 _last_angle * 180.0 / M_PI);
456 bool _hasDragTips()
const override {
return true; }
462 static double _last_angle;
464double RotateHandle::_last_angle = 0;
466class SkewHandle :
public TransformHandle
469 SkewHandle(TransformHandleSet &th,
unsigned side,
unsigned d_side)
475 void startTransform()
override
481 _last_horizontal = _side % 2;
492 if (fabs(initial_delta[d1]) < 1e-15) {
506 if (fabs(scale[d1]) < 1) {
508 scale[d1] = copysign(1.0, scale[d1]);
514 double angle = atan(skew[d1] / scale[d1]);
517 angle = snap_angle(angle);
518 skew[d1] = tan(angle) *
scale[d1];
536 new_new_pos[d2] = initial_delta[d1] * skew[d1] +
_origin[d2];
537 new_new_pos[d1] = initial_delta[d1] *
scale[d1] + scc[d1];
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;
546 for (
int i = 0; i < 2; i++) {
547 if (fabs(relative_affine[3*i]) < 1e-15) {
548 relative_affine[3*i] = 1e-15;
566 Glib::ustring _getTip(
unsigned state)
const override
571 "<b>Shift+Ctrl</b>: skew about the rotation center with snapping "
574 return C_(
"Transform handle tip",
"<b>Shift</b>: skew about the rotation center");
580 return C_(
"Transform handle tip",
581 "<b>Skew handle</b>: drag to skew (shear) selection about "
582 "the opposite handle");
585 Glib::ustring _getDragTip(MotionEvent
const &)
const override
587 if (_last_horizontal) {
588 return format_tip(C_(
"Transform handle tip",
"Skew horizontally by %.2f°"),
589 _last_angle * 360.0);
591 return format_tip(C_(
"Transform handle tip",
"Skew vertically by %.2f°"),
592 _last_angle * 360.0);
596 bool _hasDragTips()
const override {
return true; }
602 static bool _last_horizontal;
603 static double _last_angle;
605bool SkewHandle::_last_horizontal =
false;
606double SkewHandle::_last_angle = 0;
608class RotationCenter :
public ControlPoint
611 RotationCenter(TransformHandleSet &th)
614 th._transform_handle_group)
621 void dragged(
Geom::Point &new_pos, MotionEvent
const &event)
override
623 auto &sm = _th._desktop->getNamedView()->snap_manager;
624 sm.setup(_th._desktop);
625 bool snap = !
held_shift(event) && sm.someSnapperMightSnap();
629 std::vector<Inkscape::Snapper::SnapConstraint> constraints;
640 Glib::ustring _getTip(
unsigned )
const override
642 return C_(
"Transform handle tip",
"<b>Rotation center</b>: drag to change the origin of transforms");
646 TransformHandleSet &_th;
652 , _transform_handle_group(th_group)
654 , _in_transform(false)
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;
717 for (
unsigned i = 0; i < 4; ++i) {
743 throw std::logic_error(
"Transform initiated when another transform in progress");
766 int handle_index = prefs->
getIntLimited(
"/options/grabsize/value", 3, 1, 15);
767 int handle_size = handle_index * 2 + 1;
775 bool show_scale_side[2], show_skew[2];
782 for (
unsigned i = 0; i < 2; ++i) {
786 show_scale_side[i] &= (show_scale ? bp[d] >= handle_size
788 show_skew[i] = (show_rotate && bp[d] >= handle_size
792 for (
unsigned i = 0; i < 4; ++i) {
800 _center->setVisible(show_rotate);
804 _handle->setVisible(
false);
pair< double, double > Point
3x3 matrix representing an affine transformation.
bool isUniformScale(Coord eps=EPSILON) const
Check whether this matrix represents pure uniform scaling.
bool isSingular(Coord eps=EPSILON) const
Check whether this matrix is singular.
void setIdentity()
Sets this matrix to be the Identity Affine.
Affine inverse() const
Compute the inverse matrix.
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.
Axis aligned, non-empty rectangle.
Rotation around the origin.
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)
Preference storage class.
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()
Geom::Coord getSkewSnapped()
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.
SPDesktop *const _desktop
To do: update description of desktop.
double current_zoom() const
Inkscape::CanvasItemGroup * getCanvasControls() const
SPNamedView * getNamedView() const
Inkscape::UI::Tools::ToolBase * getTool() const
bool is_yaxisdown() const
Class to coordinate snapping operations.
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.
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.
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.
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.
Control point selection - stores a set of control points and applies transformations to them.
Editable view implementation.
Dim2
2D axis enumeration (X or Y).
auto floor(Geom::Rect const &rect)
double angle(std::vector< Point > const &A)
Various utility functions.
double angle_between(Line const &l1, Line const &l2)
Affine identity()
Create an identity matrix.
SBasis L2(D2< SBasis > const &a, unsigned k)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
Point middle_point(LineSegment const &_segment)
static double snap_increment_degrees()
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
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)
Abstract base class for events.
Movement of the mouse pointer.