From 55819bf4c49570a8c71a3b6fecb563a045e55a10 Mon Sep 17 00:00:00 2001 From: Rebecca Breu Date: Tue, 6 Apr 2021 16:19:02 +0200 Subject: [PATCH] Add snapping for rotating --- beeref/selection.py | 18 ++++++++++++++---- beeref/utils.py | 9 +++++++++ tests/test_selection.py | 9 ++++++++- tests/test_utils.py | 11 +++++++++++ 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/beeref/selection.py b/beeref/selection.py index 8d39411..07763d7 100644 --- a/beeref/selection.py +++ b/beeref/selection.py @@ -315,9 +315,17 @@ class SelectableMixin(BaseItemMixin): diff = pos - self.event_anchor return -math.degrees(math.atan2(diff.x(), diff.y())) - def get_rotate_delta(self, pos): - """Get the rotate delta for the current mouse movement""" - return self.get_rotate_angle(pos) - self.rotate_start_angle + def get_rotate_delta(self, pos, snap=False): + """Get the rotate delta for the current mouse movement. + + If ``snap`` is True, snap to 15 degree units.""" + + delta = self.get_rotate_angle(pos) - self.rotate_start_angle + if snap: + target = utils.round_to(self.rotate_orig_degrees + delta, 15) + delta = target - self.rotate_orig_degrees + + return delta def mouseMoveEvent(self, event): if self.scale_active: @@ -327,7 +335,9 @@ class SelectableMixin(BaseItemMixin): item.mapFromScene(self.event_anchor)) event.accept() elif self.rotate_active: - delta = self.get_rotate_delta(event.scenePos()) + snap = (event.modifiers() == Qt.KeyboardModifiers.ControlModifier + or event.modifiers() == Qt.KeyboardModifiers.ShiftModifier) + delta = self.get_rotate_delta(event.scenePos(), snap) for item in self.selection_action_items(): item.setRotation(item.rotate_orig_degrees + delta, item.mapFromScene(self.event_anchor)) diff --git a/beeref/utils.py b/beeref/utils.py index 86dc85c..5a551b6 100644 --- a/beeref/utils.py +++ b/beeref/utils.py @@ -28,3 +28,12 @@ def get_rect_from_points(point1, point2): max(point1.x(), point2.x()), max(point1.y(), point2.y())) return QtCore.QRectF(topleft, bottomright) + + +def round_to(number, base): + """Rounds to the given base. + + E.g. with ``base=5`` round to the nearest number divisible by 5. + """ + + return base * round(number / base) diff --git a/tests/test_selection.py b/tests/test_selection.py index 836f51b..6348856 100644 --- a/tests/test_selection.py +++ b/tests/test_selection.py @@ -344,7 +344,7 @@ class SelectableMixinTestCase(SelectableMixinBaseTestCase): assert shape.bottomRight().y() == 80 -class SelectableMixinScalingTestCase(SelectableMixinBaseTestCase): +class SelectableMixinCalculationsTestCase(SelectableMixinBaseTestCase): def test_get_scale_factor_bottomright(self): self.item.event_start = QtCore.QPointF(10, 10) @@ -423,6 +423,13 @@ class SelectableMixinScalingTestCase(SelectableMixinBaseTestCase): self.item.rotate_start_angle = -3 assert self.item.get_rotate_delta(QtCore.QPointF(15, 25)) == -42 + def test_get_rotate_delta_snaps(self): + self.item.event_anchor = QtCore.QPointF(10, 20) + self.item.rotate_start_angle = -3 + self.item.rotate_orig_degrees = 5 + assert self.item.get_rotate_delta( + QtCore.QPointF(15, 25), snap=True) == -35 + class SelectableMixinMouseEventsTestCase(SelectableMixinBaseTestCase): diff --git a/tests/test_utils.py b/tests/test_utils.py index 3c24f2c..b72e086 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,3 +1,5 @@ +import pytest + from PyQt6 import QtCore from beeref import utils @@ -21,3 +23,12 @@ class GetRectFromPointsTestCase(BeeTestCase): assert rect.topLeft().y() == -20 assert rect.bottomRight().x() == 50 assert rect.bottomRight().y() == 40 + + +@pytest.mark.parametrize('number,base,expected', + [(33, 5, 35), + (-33, 5, -35), + (50, 5, 50), + (3.1, 0.5, 3.0)]) +def test_round_to(number, base, expected): + assert utils.round_to(number, base) == expected