sketchingpy.transform

Data structures providing transformation matrix functionality if native logic is not available.

Data structures providing Python-based transformation matrix functionality if native logic is not available in the underlying renderer.

License:

BSD

  1"""Data structures providing transformation matrix functionality if native logic is not available.
  2
  3Data structures providing Python-based transformation matrix functionality if native logic is not
  4available in the underlying renderer.
  5
  6License:
  7    BSD
  8"""
  9
 10import math
 11import typing
 12
 13has_numpy = False
 14try:
 15    import numpy
 16    has_numpy = True
 17except:
 18    pass
 19
 20
 21class TransformedPoint:
 22    """A point which has gone through zero or more transformations."""
 23
 24    def __init__(self, x: float, y: float, scale: float, rotation: float):
 25        """Create a new transformed point.
 26
 27        Args:
 28            x: Horizontal coordinate for this point after transformation.
 29            y: Vertical coordinate for this point after transformation.
 30            scale: The overall scale factor applied to this point.
 31            rotation: The overall rotation applied to this point.
 32        """
 33        self._x = x
 34        self._y = y
 35        self._scale = scale
 36        self._rotation = rotation
 37
 38    def get_x(self) -> float:
 39        """Get the post-transformation x coordinate.
 40
 41        Returns:
 42            Horizontal coordinate for this point after transformation.
 43        """
 44        return self._x
 45
 46    def get_y(self) -> float:
 47        """Get the post-transformation y coordinate.
 48
 49        Returns:
 50            Vertical coordinate for this point after transformation.
 51        """
 52        return self._y
 53
 54    def get_scale(self) -> float:
 55        """Get the overall scale reflected in this point.
 56
 57        Returns:
 58            The overall scale factor applied to this point.
 59        """
 60        return self._scale
 61
 62    def get_rotation(self) -> float:
 63        """Get the overall rotation reflected in this point.
 64
 65        Returns:
 66            The overall rotation factor applied to this point.
 67        """
 68        return self._rotation
 69
 70
 71class Transformer:
 72    """Utility to transform points."""
 73
 74    def __init__(self, matrix: typing.Optional['numpy.ndarray'] = None, scale: float = 1,
 75        rotation: float = 0):
 76        """Create a new transformer.
 77
 78        Args:
 79            matrix: Starting transformation matrix.
 80            scale: Starting overall scale.
 81            rotation: Starting overall rotation.
 82        """
 83
 84        if not has_numpy:
 85            raise RuntimeError('Need numpy in order to use transformer on this renderer.')
 86
 87        matrix_unset = matrix is None
 88        scale_unset = abs(scale - 1) < 0.00001
 89        rotation_unset = abs(rotation - 0) < 0.00001
 90
 91        self._is_default = matrix_unset and scale_unset and rotation_unset
 92        self._matrix = numpy.identity(3) if matrix is None else matrix
 93        self._scale = scale
 94        self._rotation = rotation
 95
 96    def translate(self, x: float, y: float):
 97        """Apply a translation to the current transformation matrix.
 98
 99        Args:
100            x: By how much to offset the horizontal coordinate.
101            y: By how much to offset the vertical coordinate.
102        """
103        transformation = numpy.identity(3)
104        transformation[0][2] = x
105        transformation[1][2] = y
106        self._matrix = numpy.dot(self._matrix, transformation)
107        self._is_default = False
108
109    def scale(self, scale: float):
110        """Apply a scale to the current transformation matrix.
111
112        Args:
113            scale: The scale to apply.
114        """
115        transformation = numpy.identity(3)
116        transformation[0][0] = scale
117        transformation[1][1] = scale
118        self._matrix = numpy.dot(self._matrix, transformation)
119        self._scale *= scale
120        self._is_default = False
121
122    def rotate(self, angle: float):
123        """Apply a rotation to the current transforation matrix counter-clockwise.
124
125        Args:
126            angle: The angle of rotation to apply as radians.
127        """
128        cos_angle = math.cos(angle)
129        sin_angle = math.sin(angle)
130
131        transformation = numpy.identity(3)
132        transformation[0][0] = cos_angle
133        transformation[1][1] = cos_angle
134        transformation[1][0] = -1 * sin_angle
135        transformation[0][1] = sin_angle
136
137        self._matrix = numpy.dot(self._matrix, transformation)
138        self._rotation += angle
139        self._is_default = False
140
141    def transform(self, x: float, y: float) -> TransformedPoint:
142        """Transform a point.
143
144        Args:
145            x: The horizontal coordinate to be transformed.
146            y: The vertical coordinate to be transformed.
147
148        Returns:
149            Point after transformation.
150        """
151        if self._is_default:
152            return TransformedPoint(x, y, self._scale, self._rotation)
153
154        input_array = numpy.array([x, y, 1])
155        output = numpy.dot(self._matrix, input_array)
156
157        x = output[0]
158        y = output[1]
159
160        return TransformedPoint(x, y, self._scale, self._rotation)
161
162    def quick_copy(self) -> 'Transformer':
163        """Create a shallow copy of this transformer.
164
165        Returns:
166            Transformer which has the same transform matrix as this original transformer.
167        """
168        return Transformer(self._matrix, self._scale, self._rotation)
has_numpy = True
class TransformedPoint:
22class TransformedPoint:
23    """A point which has gone through zero or more transformations."""
24
25    def __init__(self, x: float, y: float, scale: float, rotation: float):
26        """Create a new transformed point.
27
28        Args:
29            x: Horizontal coordinate for this point after transformation.
30            y: Vertical coordinate for this point after transformation.
31            scale: The overall scale factor applied to this point.
32            rotation: The overall rotation applied to this point.
33        """
34        self._x = x
35        self._y = y
36        self._scale = scale
37        self._rotation = rotation
38
39    def get_x(self) -> float:
40        """Get the post-transformation x coordinate.
41
42        Returns:
43            Horizontal coordinate for this point after transformation.
44        """
45        return self._x
46
47    def get_y(self) -> float:
48        """Get the post-transformation y coordinate.
49
50        Returns:
51            Vertical coordinate for this point after transformation.
52        """
53        return self._y
54
55    def get_scale(self) -> float:
56        """Get the overall scale reflected in this point.
57
58        Returns:
59            The overall scale factor applied to this point.
60        """
61        return self._scale
62
63    def get_rotation(self) -> float:
64        """Get the overall rotation reflected in this point.
65
66        Returns:
67            The overall rotation factor applied to this point.
68        """
69        return self._rotation

A point which has gone through zero or more transformations.

TransformedPoint(x: float, y: float, scale: float, rotation: float)
25    def __init__(self, x: float, y: float, scale: float, rotation: float):
26        """Create a new transformed point.
27
28        Args:
29            x: Horizontal coordinate for this point after transformation.
30            y: Vertical coordinate for this point after transformation.
31            scale: The overall scale factor applied to this point.
32            rotation: The overall rotation applied to this point.
33        """
34        self._x = x
35        self._y = y
36        self._scale = scale
37        self._rotation = rotation

Create a new transformed point.

Arguments:
  • x: Horizontal coordinate for this point after transformation.
  • y: Vertical coordinate for this point after transformation.
  • scale: The overall scale factor applied to this point.
  • rotation: The overall rotation applied to this point.
def get_x(self) -> float:
39    def get_x(self) -> float:
40        """Get the post-transformation x coordinate.
41
42        Returns:
43            Horizontal coordinate for this point after transformation.
44        """
45        return self._x

Get the post-transformation x coordinate.

Returns:

Horizontal coordinate for this point after transformation.

def get_y(self) -> float:
47    def get_y(self) -> float:
48        """Get the post-transformation y coordinate.
49
50        Returns:
51            Vertical coordinate for this point after transformation.
52        """
53        return self._y

Get the post-transformation y coordinate.

Returns:

Vertical coordinate for this point after transformation.

def get_scale(self) -> float:
55    def get_scale(self) -> float:
56        """Get the overall scale reflected in this point.
57
58        Returns:
59            The overall scale factor applied to this point.
60        """
61        return self._scale

Get the overall scale reflected in this point.

Returns:

The overall scale factor applied to this point.

def get_rotation(self) -> float:
63    def get_rotation(self) -> float:
64        """Get the overall rotation reflected in this point.
65
66        Returns:
67            The overall rotation factor applied to this point.
68        """
69        return self._rotation

Get the overall rotation reflected in this point.

Returns:

The overall rotation factor applied to this point.

class Transformer:
 72class Transformer:
 73    """Utility to transform points."""
 74
 75    def __init__(self, matrix: typing.Optional['numpy.ndarray'] = None, scale: float = 1,
 76        rotation: float = 0):
 77        """Create a new transformer.
 78
 79        Args:
 80            matrix: Starting transformation matrix.
 81            scale: Starting overall scale.
 82            rotation: Starting overall rotation.
 83        """
 84
 85        if not has_numpy:
 86            raise RuntimeError('Need numpy in order to use transformer on this renderer.')
 87
 88        matrix_unset = matrix is None
 89        scale_unset = abs(scale - 1) < 0.00001
 90        rotation_unset = abs(rotation - 0) < 0.00001
 91
 92        self._is_default = matrix_unset and scale_unset and rotation_unset
 93        self._matrix = numpy.identity(3) if matrix is None else matrix
 94        self._scale = scale
 95        self._rotation = rotation
 96
 97    def translate(self, x: float, y: float):
 98        """Apply a translation to the current transformation matrix.
 99
100        Args:
101            x: By how much to offset the horizontal coordinate.
102            y: By how much to offset the vertical coordinate.
103        """
104        transformation = numpy.identity(3)
105        transformation[0][2] = x
106        transformation[1][2] = y
107        self._matrix = numpy.dot(self._matrix, transformation)
108        self._is_default = False
109
110    def scale(self, scale: float):
111        """Apply a scale to the current transformation matrix.
112
113        Args:
114            scale: The scale to apply.
115        """
116        transformation = numpy.identity(3)
117        transformation[0][0] = scale
118        transformation[1][1] = scale
119        self._matrix = numpy.dot(self._matrix, transformation)
120        self._scale *= scale
121        self._is_default = False
122
123    def rotate(self, angle: float):
124        """Apply a rotation to the current transforation matrix counter-clockwise.
125
126        Args:
127            angle: The angle of rotation to apply as radians.
128        """
129        cos_angle = math.cos(angle)
130        sin_angle = math.sin(angle)
131
132        transformation = numpy.identity(3)
133        transformation[0][0] = cos_angle
134        transformation[1][1] = cos_angle
135        transformation[1][0] = -1 * sin_angle
136        transformation[0][1] = sin_angle
137
138        self._matrix = numpy.dot(self._matrix, transformation)
139        self._rotation += angle
140        self._is_default = False
141
142    def transform(self, x: float, y: float) -> TransformedPoint:
143        """Transform a point.
144
145        Args:
146            x: The horizontal coordinate to be transformed.
147            y: The vertical coordinate to be transformed.
148
149        Returns:
150            Point after transformation.
151        """
152        if self._is_default:
153            return TransformedPoint(x, y, self._scale, self._rotation)
154
155        input_array = numpy.array([x, y, 1])
156        output = numpy.dot(self._matrix, input_array)
157
158        x = output[0]
159        y = output[1]
160
161        return TransformedPoint(x, y, self._scale, self._rotation)
162
163    def quick_copy(self) -> 'Transformer':
164        """Create a shallow copy of this transformer.
165
166        Returns:
167            Transformer which has the same transform matrix as this original transformer.
168        """
169        return Transformer(self._matrix, self._scale, self._rotation)

Utility to transform points.

Transformer( matrix: Optional[numpy.ndarray] = None, scale: float = 1, rotation: float = 0)
75    def __init__(self, matrix: typing.Optional['numpy.ndarray'] = None, scale: float = 1,
76        rotation: float = 0):
77        """Create a new transformer.
78
79        Args:
80            matrix: Starting transformation matrix.
81            scale: Starting overall scale.
82            rotation: Starting overall rotation.
83        """
84
85        if not has_numpy:
86            raise RuntimeError('Need numpy in order to use transformer on this renderer.')
87
88        matrix_unset = matrix is None
89        scale_unset = abs(scale - 1) < 0.00001
90        rotation_unset = abs(rotation - 0) < 0.00001
91
92        self._is_default = matrix_unset and scale_unset and rotation_unset
93        self._matrix = numpy.identity(3) if matrix is None else matrix
94        self._scale = scale
95        self._rotation = rotation

Create a new transformer.

Arguments:
  • matrix: Starting transformation matrix.
  • scale: Starting overall scale.
  • rotation: Starting overall rotation.
def translate(self, x: float, y: float):
 97    def translate(self, x: float, y: float):
 98        """Apply a translation to the current transformation matrix.
 99
100        Args:
101            x: By how much to offset the horizontal coordinate.
102            y: By how much to offset the vertical coordinate.
103        """
104        transformation = numpy.identity(3)
105        transformation[0][2] = x
106        transformation[1][2] = y
107        self._matrix = numpy.dot(self._matrix, transformation)
108        self._is_default = False

Apply a translation to the current transformation matrix.

Arguments:
  • x: By how much to offset the horizontal coordinate.
  • y: By how much to offset the vertical coordinate.
def scale(self, scale: float):
110    def scale(self, scale: float):
111        """Apply a scale to the current transformation matrix.
112
113        Args:
114            scale: The scale to apply.
115        """
116        transformation = numpy.identity(3)
117        transformation[0][0] = scale
118        transformation[1][1] = scale
119        self._matrix = numpy.dot(self._matrix, transformation)
120        self._scale *= scale
121        self._is_default = False

Apply a scale to the current transformation matrix.

Arguments:
  • scale: The scale to apply.
def rotate(self, angle: float):
123    def rotate(self, angle: float):
124        """Apply a rotation to the current transforation matrix counter-clockwise.
125
126        Args:
127            angle: The angle of rotation to apply as radians.
128        """
129        cos_angle = math.cos(angle)
130        sin_angle = math.sin(angle)
131
132        transformation = numpy.identity(3)
133        transformation[0][0] = cos_angle
134        transformation[1][1] = cos_angle
135        transformation[1][0] = -1 * sin_angle
136        transformation[0][1] = sin_angle
137
138        self._matrix = numpy.dot(self._matrix, transformation)
139        self._rotation += angle
140        self._is_default = False

Apply a rotation to the current transforation matrix counter-clockwise.

Arguments:
  • angle: The angle of rotation to apply as radians.
def transform(self, x: float, y: float) -> TransformedPoint:
142    def transform(self, x: float, y: float) -> TransformedPoint:
143        """Transform a point.
144
145        Args:
146            x: The horizontal coordinate to be transformed.
147            y: The vertical coordinate to be transformed.
148
149        Returns:
150            Point after transformation.
151        """
152        if self._is_default:
153            return TransformedPoint(x, y, self._scale, self._rotation)
154
155        input_array = numpy.array([x, y, 1])
156        output = numpy.dot(self._matrix, input_array)
157
158        x = output[0]
159        y = output[1]
160
161        return TransformedPoint(x, y, self._scale, self._rotation)

Transform a point.

Arguments:
  • x: The horizontal coordinate to be transformed.
  • y: The vertical coordinate to be transformed.
Returns:

Point after transformation.

def quick_copy(self) -> Transformer:
163    def quick_copy(self) -> 'Transformer':
164        """Create a shallow copy of this transformer.
165
166        Returns:
167            Transformer which has the same transform matrix as this original transformer.
168        """
169        return Transformer(self._matrix, self._scale, self._rotation)

Create a shallow copy of this transformer.

Returns:

Transformer which has the same transform matrix as this original transformer.