svg-ultralight 0.38.0__py3-none-any.whl → 0.39.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of svg-ultralight might be problematic. Click here for more details.
- svg_ultralight/bounding_boxes/bound_helpers.py +3 -10
- svg_ultralight/bounding_boxes/supports_bounds.py +4 -9
- svg_ultralight/bounding_boxes/type_bound_collection.py +1 -1
- svg_ultralight/bounding_boxes/type_bound_element.py +1 -4
- svg_ultralight/bounding_boxes/type_bounding_box.py +149 -266
- svg_ultralight/bounding_boxes/type_padded_text.py +96 -199
- svg_ultralight/layout.py +22 -13
- svg_ultralight/query.py +33 -23
- svg_ultralight/transformations.py +19 -6
- {svg_ultralight-0.38.0.dist-info → svg_ultralight-0.39.1.dist-info}/METADATA +1 -1
- {svg_ultralight-0.38.0.dist-info → svg_ultralight-0.39.1.dist-info}/RECORD +13 -13
- {svg_ultralight-0.38.0.dist-info → svg_ultralight-0.39.1.dist-info}/WHEEL +1 -1
- {svg_ultralight-0.38.0.dist-info → svg_ultralight-0.39.1.dist-info}/top_level.txt +0 -0
|
@@ -10,8 +10,9 @@ from typing import TYPE_CHECKING
|
|
|
10
10
|
|
|
11
11
|
from lxml.etree import _Element as EtreeElement # pyright: ignore[reportPrivateUsage]
|
|
12
12
|
|
|
13
|
+
from svg_ultralight.bounding_boxes.supports_bounds import SupportsBounds
|
|
13
14
|
from svg_ultralight.bounding_boxes.type_bound_element import BoundElement
|
|
14
|
-
from svg_ultralight.bounding_boxes.type_bounding_box import BoundingBox
|
|
15
|
+
from svg_ultralight.bounding_boxes.type_bounding_box import BoundingBox, HasBoundingBox
|
|
15
16
|
from svg_ultralight.bounding_boxes.type_padded_text import PaddedText
|
|
16
17
|
from svg_ultralight.constructors import new_element
|
|
17
18
|
|
|
@@ -66,15 +67,7 @@ def new_bbox_union(*blems: SupportsBounds | EtreeElement) -> BoundingBox:
|
|
|
66
67
|
|
|
67
68
|
Will used the padded_box attribute of PaddedText instances.
|
|
68
69
|
"""
|
|
69
|
-
bboxes
|
|
70
|
-
for blem in blems:
|
|
71
|
-
if isinstance(blem, BoundingBox):
|
|
72
|
-
bboxes.append(blem)
|
|
73
|
-
elif isinstance(blem, BoundElement):
|
|
74
|
-
bboxes.append(blem.bbox)
|
|
75
|
-
elif isinstance(blem, PaddedText):
|
|
76
|
-
bboxes.append(blem.padded_bbox)
|
|
77
|
-
|
|
70
|
+
bboxes = [x.bbox for x in blems if isinstance(x, HasBoundingBox)]
|
|
78
71
|
if not bboxes:
|
|
79
72
|
msg = (
|
|
80
73
|
"Cannot find any bounding boxes to union. "
|
|
@@ -37,7 +37,7 @@ class SupportsBounds(Protocol):
|
|
|
37
37
|
cy (float): The center y coordinate.
|
|
38
38
|
width (float): The width of the object.
|
|
39
39
|
height(float): The height of the object.
|
|
40
|
-
scale (float): The scale of the object.
|
|
40
|
+
scale ((float, float)): The x and yx and y scale of the object.
|
|
41
41
|
|
|
42
42
|
There is no setter for scale. Scale is a function of width and height.
|
|
43
43
|
Setting scale would be ambiguous. because the typical implementation of
|
|
@@ -45,16 +45,11 @@ class SupportsBounds(Protocol):
|
|
|
45
45
|
set width and height.
|
|
46
46
|
"""
|
|
47
47
|
|
|
48
|
-
@property
|
|
49
|
-
def transformation(self) -> _Matrix:
|
|
50
|
-
"""Return an svg-style transformation matrix."""
|
|
51
|
-
...
|
|
52
|
-
|
|
53
48
|
def transform(
|
|
54
49
|
self,
|
|
55
50
|
transformation: _Matrix | None = None,
|
|
56
51
|
*,
|
|
57
|
-
scale: float | None = None,
|
|
52
|
+
scale: tuple[float, float] | float | None = None,
|
|
58
53
|
dx: float | None = None,
|
|
59
54
|
dy: float | None = None,
|
|
60
55
|
):
|
|
@@ -158,12 +153,12 @@ class SupportsBounds(Protocol):
|
|
|
158
153
|
"""
|
|
159
154
|
|
|
160
155
|
@property
|
|
161
|
-
def scale(self) -> float:
|
|
156
|
+
def scale(self) -> tuple[float, float]:
|
|
162
157
|
"""Return scale of the object."""
|
|
163
158
|
...
|
|
164
159
|
|
|
165
160
|
@scale.setter
|
|
166
|
-
def scale(self, value: float):
|
|
161
|
+
def scale(self, value: tuple[float, float]):
|
|
167
162
|
"""Return scale of the object.
|
|
168
163
|
|
|
169
164
|
:param value: The scale of the object.
|
|
@@ -45,14 +45,11 @@ class BoundElement(HasBoundingBox):
|
|
|
45
45
|
self.elem = element
|
|
46
46
|
self.bbox = bounding_box
|
|
47
47
|
|
|
48
|
-
def _update_elem(self):
|
|
49
|
-
self.elem.attrib["transform"] = self.bbox.transform_string
|
|
50
|
-
|
|
51
48
|
def transform(
|
|
52
49
|
self,
|
|
53
50
|
transformation: _Matrix | None = None,
|
|
54
51
|
*,
|
|
55
|
-
scale: float | None = None,
|
|
52
|
+
scale: tuple[float, float] | float | None = None,
|
|
56
53
|
dx: float | None = None,
|
|
57
54
|
dy: float | None = None,
|
|
58
55
|
):
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import dataclasses
|
|
10
|
+
import math
|
|
10
11
|
|
|
11
12
|
from svg_ultralight.bounding_boxes.supports_bounds import SupportsBounds
|
|
12
13
|
from svg_ultralight.string_conversion import format_number
|
|
@@ -15,85 +16,54 @@ from svg_ultralight.transformations import mat_apply, mat_dot, new_transformatio
|
|
|
15
16
|
_Matrix = tuple[float, float, float, float, float, float]
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
class
|
|
20
|
-
"""Mutable bounding box object for svg_ultralight.
|
|
21
|
-
|
|
22
|
-
:param x: left x value
|
|
23
|
-
:param y: top y value
|
|
24
|
-
:param width: width of the bounding box
|
|
25
|
-
:param height: height of the bounding box
|
|
26
|
-
|
|
27
|
-
The below optional parameter, in addition to the required parameters, captures
|
|
28
|
-
the entire state of a BoundingBox instance. It could be used to make a copy or
|
|
29
|
-
to initialize a transformed box with the same transform_string as another box.
|
|
30
|
-
Under most circumstances, it will not be used.
|
|
31
|
-
|
|
32
|
-
:param _transformation: transformation matrix
|
|
33
|
-
|
|
34
|
-
Functions that return a bounding box will return a BoundingBox instance. This
|
|
35
|
-
instance can be transformed (uniform scale and translate only). Transformations
|
|
36
|
-
will be combined and scored to be passed to new_element as a transform value.
|
|
37
|
-
|
|
38
|
-
Define the bbox with x=, y=, width=, height=
|
|
39
|
-
|
|
40
|
-
Transform the BoundingBox by setting these variables. Each time you set x, cx,
|
|
41
|
-
x2, y, cy, y2, width, or height, private transformation value _transformation
|
|
42
|
-
will be updated.
|
|
43
|
-
|
|
44
|
-
The ultimate transformation can be accessed through ``.transform_string``.
|
|
45
|
-
So the workflow will look like :
|
|
46
|
-
|
|
47
|
-
1. Get the bounding box of an svg element
|
|
48
|
-
2. Update the bounding box x, y, width, and height
|
|
49
|
-
3. Transform the original svg element with
|
|
50
|
-
update_element(elem, transform=bbox.transform_string)
|
|
51
|
-
4. The transformed element will lie in the transformed BoundingBox
|
|
52
|
-
|
|
53
|
-
In addition to x, y, width, and height, x2 and y2 can be set to establish the
|
|
54
|
-
right x value or bottom y value.
|
|
55
|
-
|
|
56
|
-
The point of all of this is to simplify stacking and aligning elements. To stack:
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
elem_a = new_element(*args)
|
|
60
|
-
bbox_a = get_bounding_box(elem_a)
|
|
61
|
-
|
|
62
|
-
elem_b = new_element(*args)
|
|
63
|
-
bbox_b = get_bounding_box(elem_b)
|
|
64
|
-
|
|
65
|
-
# align at same x
|
|
66
|
-
bbox_b.x = bbox_a.x
|
|
67
|
-
|
|
68
|
-
# make the same width
|
|
69
|
-
bbox_b.width = bbox_a.width
|
|
70
|
-
|
|
71
|
-
# stack a on top of b
|
|
72
|
-
bbox_a.y2 = bbox_b.y
|
|
73
|
-
|
|
74
|
-
update_element(elem_a, transform=bbox_a.transform_string)
|
|
75
|
-
update_element(elem_b, transform=bbox_b.transform_string)
|
|
76
|
-
"""
|
|
77
|
-
|
|
78
|
-
_x: float
|
|
79
|
-
_y: float
|
|
80
|
-
_width: float
|
|
81
|
-
_height: float
|
|
82
|
-
_transformation: _Matrix = (1, 0, 0, 1, 0, 0)
|
|
19
|
+
class HasBoundingBox(SupportsBounds):
|
|
20
|
+
"""A parent class for BoundElement and others that have a bbox attribute."""
|
|
83
21
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
22
|
+
def __init__(self, bbox: BoundingBox) -> None:
|
|
23
|
+
"""Initialize the HasBoundingBox instance."""
|
|
24
|
+
self.bbox = bbox
|
|
87
25
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
26
|
+
def _get_input_corners(
|
|
27
|
+
self,
|
|
28
|
+
) -> tuple[
|
|
29
|
+
tuple[float, float],
|
|
30
|
+
tuple[float, float],
|
|
31
|
+
tuple[float, float],
|
|
32
|
+
tuple[float, float],
|
|
33
|
+
]:
|
|
34
|
+
"""Get the input corners of the bounding box.
|
|
35
|
+
|
|
36
|
+
:return: four corners counter-clockwise starting at top left
|
|
37
|
+
"""
|
|
38
|
+
x = self.bbox.base_x
|
|
39
|
+
y = self.bbox.base_y
|
|
40
|
+
x2 = x + self.bbox.base_width
|
|
41
|
+
y2 = y + self.bbox.base_height
|
|
42
|
+
return (x, y), (x2, y), (x2, y2), (x, y2)
|
|
43
|
+
|
|
44
|
+
def _get_transformed_corners(
|
|
45
|
+
self,
|
|
46
|
+
) -> tuple[
|
|
47
|
+
tuple[float, float],
|
|
48
|
+
tuple[float, float],
|
|
49
|
+
tuple[float, float],
|
|
50
|
+
tuple[float, float],
|
|
51
|
+
]:
|
|
52
|
+
"""Get the transformed corners of the bounding box.
|
|
53
|
+
|
|
54
|
+
:return: four corners counter-clockwise starting at top left, transformed by
|
|
55
|
+
self.transformation
|
|
56
|
+
"""
|
|
57
|
+
c0, c1, c2, c3 = (
|
|
58
|
+
mat_apply(self.bbox.transformation, c) for c in self._get_input_corners()
|
|
59
|
+
)
|
|
60
|
+
return c0, c1, c2, c3
|
|
91
61
|
|
|
92
62
|
def transform(
|
|
93
63
|
self,
|
|
94
64
|
transformation: _Matrix | None = None,
|
|
95
65
|
*,
|
|
96
|
-
scale: float | None = None,
|
|
66
|
+
scale: tuple[float, float] | float | None = None,
|
|
97
67
|
dx: float | None = None,
|
|
98
68
|
dy: float | None = None,
|
|
99
69
|
):
|
|
@@ -112,10 +82,10 @@ class BoundingBox(SupportsBounds):
|
|
|
112
82
|
when applying a transformation from another bounding box instance.
|
|
113
83
|
"""
|
|
114
84
|
tmat = new_transformation_matrix(transformation, scale=scale, dx=dx, dy=dy)
|
|
115
|
-
self.
|
|
85
|
+
self.bbox.transformation = mat_dot(tmat, self.bbox.transformation)
|
|
116
86
|
|
|
117
87
|
@property
|
|
118
|
-
def scale(self) -> float:
|
|
88
|
+
def scale(self) -> tuple[float, float]:
|
|
119
89
|
"""Get scale of the bounding box.
|
|
120
90
|
|
|
121
91
|
:return: uniform scale of the bounding box
|
|
@@ -127,10 +97,11 @@ class BoundingBox(SupportsBounds):
|
|
|
127
97
|
width*scale, height => height*scale, scale => scale*scale. This matches how
|
|
128
98
|
scale works in almost every other context.
|
|
129
99
|
"""
|
|
130
|
-
|
|
100
|
+
xx, xy, yx, yy, *_ = self.bbox.transformation
|
|
101
|
+
return math.sqrt(xx * xx + xy * xy), math.sqrt(yx * yx + yy * yy)
|
|
131
102
|
|
|
132
103
|
@scale.setter
|
|
133
|
-
def scale(self, value: float) -> None:
|
|
104
|
+
def scale(self, value: tuple[float, float]) -> None:
|
|
134
105
|
"""Scale the bounding box by a uniform factor.
|
|
135
106
|
|
|
136
107
|
:param value: new scale value
|
|
@@ -142,7 +113,8 @@ class BoundingBox(SupportsBounds):
|
|
|
142
113
|
`scale = 2` -> ignore whatever scale was previously defined and set scale to 2
|
|
143
114
|
`scale *= 2` -> make it twice as big as it was.
|
|
144
115
|
"""
|
|
145
|
-
self.
|
|
116
|
+
new_scale = value[0] / self.scale[0], value[1] / self.scale[1]
|
|
117
|
+
self.transform(scale=new_scale)
|
|
146
118
|
|
|
147
119
|
@property
|
|
148
120
|
def x(self) -> float:
|
|
@@ -150,13 +122,13 @@ class BoundingBox(SupportsBounds):
|
|
|
150
122
|
|
|
151
123
|
:return: internal _x value transformed by scale and translation
|
|
152
124
|
"""
|
|
153
|
-
return
|
|
125
|
+
return min(x for x, _ in self._get_transformed_corners())
|
|
154
126
|
|
|
155
127
|
@x.setter
|
|
156
|
-
def x(self, value: float)
|
|
157
|
-
"""
|
|
128
|
+
def x(self, value: float):
|
|
129
|
+
"""Set the x coordinate of the left edge of the bounding box.
|
|
158
130
|
|
|
159
|
-
:param value: new x
|
|
131
|
+
:param value: the new x coordinate of the left edge of the bounding box
|
|
160
132
|
"""
|
|
161
133
|
self.transform(dx=value - self.x)
|
|
162
134
|
|
|
@@ -182,7 +154,7 @@ class BoundingBox(SupportsBounds):
|
|
|
182
154
|
|
|
183
155
|
:return: transformed x + transformed width
|
|
184
156
|
"""
|
|
185
|
-
return
|
|
157
|
+
return max(x for x, _ in self._get_transformed_corners())
|
|
186
158
|
|
|
187
159
|
@x2.setter
|
|
188
160
|
def x2(self, value: float) -> None:
|
|
@@ -198,7 +170,7 @@ class BoundingBox(SupportsBounds):
|
|
|
198
170
|
|
|
199
171
|
:return: internal _y value transformed by scale and translation
|
|
200
172
|
"""
|
|
201
|
-
return
|
|
173
|
+
return min(y for _, y in self._get_transformed_corners())
|
|
202
174
|
|
|
203
175
|
@y.setter
|
|
204
176
|
def y(self, value: float) -> None:
|
|
@@ -230,7 +202,7 @@ class BoundingBox(SupportsBounds):
|
|
|
230
202
|
|
|
231
203
|
:return: transformed y + transformed height
|
|
232
204
|
"""
|
|
233
|
-
return
|
|
205
|
+
return max(y for _, y in self._get_transformed_corners())
|
|
234
206
|
|
|
235
207
|
@y2.setter
|
|
236
208
|
def y2(self, value: float) -> None:
|
|
@@ -246,7 +218,7 @@ class BoundingBox(SupportsBounds):
|
|
|
246
218
|
|
|
247
219
|
:return: internal _width value transformed by scale
|
|
248
220
|
"""
|
|
249
|
-
return self.
|
|
221
|
+
return self.x2 - self.x
|
|
250
222
|
|
|
251
223
|
@width.setter
|
|
252
224
|
def width(self, value: float) -> None:
|
|
@@ -259,7 +231,7 @@ class BoundingBox(SupportsBounds):
|
|
|
259
231
|
"""
|
|
260
232
|
current_x = self.x
|
|
261
233
|
current_y = self.y
|
|
262
|
-
self.scale
|
|
234
|
+
self.transform(scale=value / self.width)
|
|
263
235
|
self.x = current_x
|
|
264
236
|
self.y = current_y
|
|
265
237
|
|
|
@@ -269,7 +241,7 @@ class BoundingBox(SupportsBounds):
|
|
|
269
241
|
|
|
270
242
|
:return: internal _height value transformed by scale
|
|
271
243
|
"""
|
|
272
|
-
return self.
|
|
244
|
+
return self.y2 - self.y
|
|
273
245
|
|
|
274
246
|
@height.setter
|
|
275
247
|
def height(self, value: float) -> None:
|
|
@@ -291,7 +263,96 @@ class BoundingBox(SupportsBounds):
|
|
|
291
263
|
Use with
|
|
292
264
|
``update_element(elem, transform=bbox.transform_string)``
|
|
293
265
|
"""
|
|
294
|
-
return f"matrix({' '.join(map(format_number, self.transformation))})"
|
|
266
|
+
return f"matrix({' '.join(map(format_number, self.bbox.transformation))})"
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
@dataclasses.dataclass
|
|
270
|
+
class BoundingBox(HasBoundingBox):
|
|
271
|
+
"""Mutable bounding box object for svg_ultralight.
|
|
272
|
+
|
|
273
|
+
:param x: left x value
|
|
274
|
+
:param y: top y value
|
|
275
|
+
:param width: width of the bounding box
|
|
276
|
+
:param height: height of the bounding box
|
|
277
|
+
|
|
278
|
+
The below optional parameter, in addition to the required parameters, captures
|
|
279
|
+
the entire state of a BoundingBox instance. It could be used to make a copy or
|
|
280
|
+
to initialize a transformed box with the same transform_string as another box.
|
|
281
|
+
Under most circumstances, it will not be used.
|
|
282
|
+
|
|
283
|
+
:param transformation: transformation matrix
|
|
284
|
+
|
|
285
|
+
Functions that return a bounding box will return a BoundingBox instance. This
|
|
286
|
+
instance can be transformed (uniform scale and translate only). Transformations
|
|
287
|
+
will be combined and scored to be passed to new_element as a transform value.
|
|
288
|
+
|
|
289
|
+
Define the bbox with x=, y=, width=, height=
|
|
290
|
+
|
|
291
|
+
Transform the BoundingBox by setting these variables. Each time you set x, cx,
|
|
292
|
+
x2, y, cy, y2, width, or height, private transformation value transformation
|
|
293
|
+
will be updated.
|
|
294
|
+
|
|
295
|
+
The ultimate transformation can be accessed through ``.transform_string``.
|
|
296
|
+
So the workflow will look like :
|
|
297
|
+
|
|
298
|
+
1. Get the bounding box of an svg element
|
|
299
|
+
2. Update the bounding box x, y, width, and height
|
|
300
|
+
3. Transform the original svg element with
|
|
301
|
+
update_element(elem, transform=bbox.transform_string)
|
|
302
|
+
4. The transformed element will lie in the transformed BoundingBox
|
|
303
|
+
|
|
304
|
+
In addition to x, y, width, and height, x2 and y2 can be set to establish the
|
|
305
|
+
right x value or bottom y value.
|
|
306
|
+
|
|
307
|
+
The point of all of this is to simplify stacking and aligning elements. To stack:
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
elem_a = new_element(*args)
|
|
311
|
+
bbox_a = get_bounding_box(elem_a)
|
|
312
|
+
|
|
313
|
+
elem_b = new_element(*args)
|
|
314
|
+
bbox_b = get_bounding_box(elem_b)
|
|
315
|
+
|
|
316
|
+
# align at same x
|
|
317
|
+
bbox_b.x = bbox_a.x
|
|
318
|
+
|
|
319
|
+
# make the same width
|
|
320
|
+
bbox_b.width = bbox_a.width
|
|
321
|
+
|
|
322
|
+
# stack a on top of b
|
|
323
|
+
bbox_a.y2 = bbox_b.y
|
|
324
|
+
|
|
325
|
+
update_element(elem_a, transform=bbox_a.transform_string)
|
|
326
|
+
update_element(elem_b, transform=bbox_b.transform_string)
|
|
327
|
+
"""
|
|
328
|
+
|
|
329
|
+
base_x: float = dataclasses.field(init=False)
|
|
330
|
+
base_y: float = dataclasses.field(init=False)
|
|
331
|
+
base_width: float = dataclasses.field(init=False)
|
|
332
|
+
base_height: float = dataclasses.field(init=False)
|
|
333
|
+
transformation: _Matrix = dataclasses.field(init=False)
|
|
334
|
+
|
|
335
|
+
def __init__(
|
|
336
|
+
self,
|
|
337
|
+
x: float,
|
|
338
|
+
y: float,
|
|
339
|
+
width: float,
|
|
340
|
+
height: float,
|
|
341
|
+
transformation: _Matrix = (1, 0, 0, 1, 0, 0),
|
|
342
|
+
) -> None:
|
|
343
|
+
"""Initialize a BoundingBox instance.
|
|
344
|
+
|
|
345
|
+
:param x: left x value
|
|
346
|
+
:param y: top y value
|
|
347
|
+
:param width: width of the bounding box
|
|
348
|
+
:param height: height of the bounding box
|
|
349
|
+
"""
|
|
350
|
+
self.base_x = x
|
|
351
|
+
self.base_y = y
|
|
352
|
+
self.base_width = width
|
|
353
|
+
self.base_height = height
|
|
354
|
+
self.transformation = transformation
|
|
355
|
+
self.bbox = self
|
|
295
356
|
|
|
296
357
|
def merge(self, *others: BoundingBox) -> BoundingBox:
|
|
297
358
|
"""Create a bounding box around all other bounding boxes.
|
|
@@ -321,181 +382,3 @@ class BoundingBox(SupportsBounds):
|
|
|
321
382
|
min_y = min(x.y for x in bboxes)
|
|
322
383
|
max_y = max(x.y + x.height for x in bboxes)
|
|
323
384
|
return BoundingBox(min_x, min_y, max_x - min_x, max_y - min_y)
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
class HasBoundingBox(SupportsBounds):
|
|
327
|
-
"""A parent class for BoundElement and others that have a bbox attribute."""
|
|
328
|
-
|
|
329
|
-
def __init__(self, bbox: BoundingBox) -> None:
|
|
330
|
-
"""Initialize the HasBoundingBox instance."""
|
|
331
|
-
self.bbox = bbox
|
|
332
|
-
|
|
333
|
-
@property
|
|
334
|
-
def transformation(self) -> _Matrix:
|
|
335
|
-
"""The transformation matrix of the bounding box."""
|
|
336
|
-
return self.bbox.transformation
|
|
337
|
-
|
|
338
|
-
def transform(
|
|
339
|
-
self,
|
|
340
|
-
transformation: _Matrix | None = None,
|
|
341
|
-
*,
|
|
342
|
-
scale: float | None = None,
|
|
343
|
-
dx: float | None = None,
|
|
344
|
-
dy: float | None = None,
|
|
345
|
-
):
|
|
346
|
-
"""Transform the element and bounding box.
|
|
347
|
-
|
|
348
|
-
:param transformation: a 6-tuple transformation matrix
|
|
349
|
-
:param scale: a scaling factor
|
|
350
|
-
:param dx: the x translation
|
|
351
|
-
:param dy: the y translation
|
|
352
|
-
"""
|
|
353
|
-
self.bbox.transform(transformation, scale=scale, dx=dx, dy=dy)
|
|
354
|
-
|
|
355
|
-
@property
|
|
356
|
-
def scale(self) -> float:
|
|
357
|
-
"""The scale of the bounding box.
|
|
358
|
-
|
|
359
|
-
:return: the scale of the bounding box
|
|
360
|
-
"""
|
|
361
|
-
return self.transformation[0]
|
|
362
|
-
|
|
363
|
-
@scale.setter
|
|
364
|
-
def scale(self, value: float):
|
|
365
|
-
"""Set the scale of the bounding box.
|
|
366
|
-
|
|
367
|
-
:param value: the scale of the bounding box
|
|
368
|
-
"""
|
|
369
|
-
self.transform(scale=value / self.scale)
|
|
370
|
-
|
|
371
|
-
@property
|
|
372
|
-
def x(self) -> float:
|
|
373
|
-
"""The x coordinate of the left edge of the bounding box.
|
|
374
|
-
|
|
375
|
-
:return: the x coordinate of the left edge of the bounding box
|
|
376
|
-
"""
|
|
377
|
-
return self.bbox.x
|
|
378
|
-
|
|
379
|
-
@x.setter
|
|
380
|
-
def x(self, value: float):
|
|
381
|
-
"""Set the x coordinate of the left edge of the bounding box.
|
|
382
|
-
|
|
383
|
-
:param value: the new x coordinate of the left edge of the bounding box
|
|
384
|
-
"""
|
|
385
|
-
self.transform(dx=value - self.x)
|
|
386
|
-
|
|
387
|
-
@property
|
|
388
|
-
def x2(self) -> float:
|
|
389
|
-
"""The x coordinate of the right edge of the bounding box.
|
|
390
|
-
|
|
391
|
-
:return: the x coordinate of the right edge of the bounding box
|
|
392
|
-
"""
|
|
393
|
-
return self.bbox.x2
|
|
394
|
-
|
|
395
|
-
@x2.setter
|
|
396
|
-
def x2(self, value: float):
|
|
397
|
-
"""Set the x coordinate of the right edge of the bounding box.
|
|
398
|
-
|
|
399
|
-
:param value: the new x coordinate of the right edge of the bounding box
|
|
400
|
-
"""
|
|
401
|
-
self.x += value - self.x2
|
|
402
|
-
|
|
403
|
-
@property
|
|
404
|
-
def cx(self) -> float:
|
|
405
|
-
"""The x coordinate of the center of the bounding box.
|
|
406
|
-
|
|
407
|
-
:return: the x coordinate of the center of the bounding box
|
|
408
|
-
"""
|
|
409
|
-
return self.bbox.cx
|
|
410
|
-
|
|
411
|
-
@cx.setter
|
|
412
|
-
def cx(self, value: float):
|
|
413
|
-
"""Set the x coordinate of the center of the bounding box.
|
|
414
|
-
|
|
415
|
-
:param value: the new x coordinate of the center of the bounding box
|
|
416
|
-
"""
|
|
417
|
-
self.x += value - self.cx
|
|
418
|
-
|
|
419
|
-
@property
|
|
420
|
-
def y(self) -> float:
|
|
421
|
-
"""The y coordinate of the top edge of the bounding box.
|
|
422
|
-
|
|
423
|
-
:return: the y coordinate of the top edge of the bounding box
|
|
424
|
-
"""
|
|
425
|
-
return self.bbox.y
|
|
426
|
-
|
|
427
|
-
@y.setter
|
|
428
|
-
def y(self, value: float):
|
|
429
|
-
"""Set the y coordinate of the top edge of the bounding box.
|
|
430
|
-
|
|
431
|
-
:param value: the new y coordinate of the top edge of the bounding box
|
|
432
|
-
"""
|
|
433
|
-
self.transform(dy=value - self.y)
|
|
434
|
-
|
|
435
|
-
@property
|
|
436
|
-
def y2(self) -> float:
|
|
437
|
-
"""The y coordinate of the bottom edge of the bounding box.
|
|
438
|
-
|
|
439
|
-
:return: the y coordinate of the bottom edge of the bounding box
|
|
440
|
-
"""
|
|
441
|
-
return self.bbox.y2
|
|
442
|
-
|
|
443
|
-
@y2.setter
|
|
444
|
-
def y2(self, value: float):
|
|
445
|
-
"""Set the y coordinate of the bottom edge of the bounding box.
|
|
446
|
-
|
|
447
|
-
:param value: the new y coordinate of the bottom edge of the bounding box
|
|
448
|
-
"""
|
|
449
|
-
self.y += value - self.y2
|
|
450
|
-
|
|
451
|
-
@property
|
|
452
|
-
def cy(self) -> float:
|
|
453
|
-
"""The y coordinate of the center of the bounding box.
|
|
454
|
-
|
|
455
|
-
:return: the y coordinate of the center of the bounding box
|
|
456
|
-
"""
|
|
457
|
-
return self.bbox.cy
|
|
458
|
-
|
|
459
|
-
@cy.setter
|
|
460
|
-
def cy(self, value: float):
|
|
461
|
-
"""Set the y coordinate of the center of the bounding box.
|
|
462
|
-
|
|
463
|
-
:param value: the new y coordinate of the center of the bounding box
|
|
464
|
-
"""
|
|
465
|
-
self.y += value - self.cy
|
|
466
|
-
|
|
467
|
-
@property
|
|
468
|
-
def width(self) -> float:
|
|
469
|
-
"""The width of the bounding box.
|
|
470
|
-
|
|
471
|
-
:return: the width of the bounding box
|
|
472
|
-
"""
|
|
473
|
-
return self.bbox.width
|
|
474
|
-
|
|
475
|
-
@width.setter
|
|
476
|
-
def width(self, value: float):
|
|
477
|
-
"""Set the width of the bounding box.
|
|
478
|
-
|
|
479
|
-
:param value: the new width of the bounding box
|
|
480
|
-
"""
|
|
481
|
-
current_x = self.x
|
|
482
|
-
current_y = self.y
|
|
483
|
-
self.scale *= value / self.width
|
|
484
|
-
self.x = current_x
|
|
485
|
-
self.y = current_y
|
|
486
|
-
|
|
487
|
-
@property
|
|
488
|
-
def height(self) -> float:
|
|
489
|
-
"""The height of the bounding box.
|
|
490
|
-
|
|
491
|
-
:return: the height of the bounding box
|
|
492
|
-
"""
|
|
493
|
-
return self.bbox.height
|
|
494
|
-
|
|
495
|
-
@height.setter
|
|
496
|
-
def height(self, value: float):
|
|
497
|
-
"""Set the height of the bounding box.
|
|
498
|
-
|
|
499
|
-
:param value: the new height of the bounding box
|
|
500
|
-
"""
|
|
501
|
-
self.width *= value / self.height
|