drawlib 0.2.3.dev2__py3-none-any.whl → 0.2.3.dev4__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 drawlib might be problematic. Click here for more details.
- drawlib/__init__.py +1 -1
- drawlib/v0_2/apis.py +5 -0
- drawlib/v0_2/private/core/model.py +19 -1
- drawlib/v0_2/private/core/util.py +6 -0
- drawlib/v0_2/private/core_canvas/base.py +53 -1
- drawlib/v0_2/private/core_canvas/canvas.py +8 -1
- drawlib/v0_2/private/core_canvas/line.py +192 -1
- drawlib/v0_2/private/core_canvas/original_arrow.py +477 -1
- drawlib/v0_2/private/core_canvas/patches.py +19 -69
- drawlib/v0_2/private/smartarts/boxlist.py +178 -0
- drawlib/v0_2/private/smartarts/bubblespeech.py +16 -16
- drawlib/v0_2/private/smartarts/bulletpoints.py +188 -0
- drawlib/v0_2/private/smartarts/dsart.py +7 -0
- drawlib/v0_2/private/smartarts/gridlayout.py +47 -26
- drawlib/v0_2/private/smartarts/pyramid.py +229 -17
- drawlib/v0_2/private/smartarts/table.py +683 -0
- drawlib/v0_2/private/smartarts/tree.py +10 -5
- drawlib/v0_2/private/util.py +116 -1
- drawlib/v0_2/private/validators/args.py +26 -11
- {drawlib-0.2.3.dev2.dist-info → drawlib-0.2.3.dev4.dist-info}/METADATA +1 -1
- {drawlib-0.2.3.dev2.dist-info → drawlib-0.2.3.dev4.dist-info}/RECORD +24 -21
- {drawlib-0.2.3.dev2.dist-info → drawlib-0.2.3.dev4.dist-info}/LICENSE.txt +0 -0
- {drawlib-0.2.3.dev2.dist-info → drawlib-0.2.3.dev4.dist-info}/WHEEL +0 -0
- {drawlib-0.2.3.dev2.dist-info → drawlib-0.2.3.dev4.dist-info}/entry_points.txt +0 -0
drawlib/__init__.py
CHANGED
drawlib/v0_2/apis.py
CHANGED
|
@@ -54,6 +54,10 @@ from drawlib.v0_2.private.core_canvas.canvas import (
|
|
|
54
54
|
arc,
|
|
55
55
|
# shape original
|
|
56
56
|
arrow,
|
|
57
|
+
arrow_arc,
|
|
58
|
+
arrow_l,
|
|
59
|
+
arrow_polyline,
|
|
60
|
+
arrow_u,
|
|
57
61
|
chevron,
|
|
58
62
|
circle,
|
|
59
63
|
# base
|
|
@@ -66,6 +70,7 @@ from drawlib.v0_2.private.core_canvas.canvas import (
|
|
|
66
70
|
image,
|
|
67
71
|
# line
|
|
68
72
|
line,
|
|
73
|
+
line_arc,
|
|
69
74
|
line_bezier1,
|
|
70
75
|
line_bezier2,
|
|
71
76
|
line_curved,
|
|
@@ -883,6 +883,7 @@ class ShapeTextStyle:
|
|
|
883
883
|
angle: Optional[float] = None
|
|
884
884
|
flip: Optional[bool] = None
|
|
885
885
|
xy_shift: Optional[Tuple[float, float]] = None
|
|
886
|
+
xy_abs_shift: Optional[Tuple[float, float]] = None
|
|
886
887
|
|
|
887
888
|
@error_handler
|
|
888
889
|
def copy(self) -> ShapeTextStyle:
|
|
@@ -1107,7 +1108,7 @@ class ShapeTextStyle:
|
|
|
1107
1108
|
|
|
1108
1109
|
@xy_shift.setter
|
|
1109
1110
|
@error_handler
|
|
1110
|
-
def xy_shift(self, value: Optional[float]) -> None:
|
|
1111
|
+
def xy_shift(self, value: Optional[Tuple[float, float]]) -> None:
|
|
1111
1112
|
"""Setter of xy_shift"""
|
|
1112
1113
|
if isinstance(value, property) or value is None:
|
|
1113
1114
|
self._xy_shift = None
|
|
@@ -1116,6 +1117,23 @@ class ShapeTextStyle:
|
|
|
1116
1117
|
coordinate_validator.validate_xy("ShapeTextStyle.xy_shift", value)
|
|
1117
1118
|
self._xy_shift = value
|
|
1118
1119
|
|
|
1120
|
+
@property # type: ignore[no-redef]
|
|
1121
|
+
@error_handler
|
|
1122
|
+
def xy_abs_shift(self) -> Optional[Tuple[float, float]]:
|
|
1123
|
+
"""Getter of xy_abs_shift"""
|
|
1124
|
+
return self._xy_abs_shift
|
|
1125
|
+
|
|
1126
|
+
@xy_abs_shift.setter
|
|
1127
|
+
@error_handler
|
|
1128
|
+
def xy_abs_shift(self, value: Optional[Tuple[float, float]]) -> None:
|
|
1129
|
+
"""Setter of xy_abs_shift"""
|
|
1130
|
+
if isinstance(value, property) or value is None:
|
|
1131
|
+
self._xy_abs_shift = None
|
|
1132
|
+
return
|
|
1133
|
+
|
|
1134
|
+
coordinate_validator.validate_xy("ShapeTextStyle.xy_abs_shift", value)
|
|
1135
|
+
self._xy_abs_shift = value
|
|
1136
|
+
|
|
1119
1137
|
|
|
1120
1138
|
@dataclasses.dataclass
|
|
1121
1139
|
class TextStyle:
|
|
@@ -498,6 +498,7 @@ class ShapeUtil:
|
|
|
498
498
|
text_angle = (text_angle + 180) % 360
|
|
499
499
|
|
|
500
500
|
x, y = xy
|
|
501
|
+
# shift
|
|
501
502
|
if style.xy_shift is not None:
|
|
502
503
|
x_shift, y_shift = style.xy_shift
|
|
503
504
|
if shape_angle == 0:
|
|
@@ -510,6 +511,11 @@ class ShapeUtil:
|
|
|
510
511
|
x += rotated_x_shift
|
|
511
512
|
y += rotated_y_shift
|
|
512
513
|
|
|
514
|
+
# absolute shift
|
|
515
|
+
if style.xy_abs_shift is not None:
|
|
516
|
+
x += style.xy_abs_shift[0]
|
|
517
|
+
y += style.xy_abs_shift[1]
|
|
518
|
+
|
|
513
519
|
# only check color. ignore alignment
|
|
514
520
|
options = TextUtil.get_text_options(style)
|
|
515
521
|
if "horizontalalignment" in options:
|
|
@@ -18,7 +18,10 @@ import matplotlib.lines
|
|
|
18
18
|
import matplotlib.text
|
|
19
19
|
import PIL.Image
|
|
20
20
|
from matplotlib import pyplot
|
|
21
|
-
from matplotlib.patches import
|
|
21
|
+
from matplotlib.patches import (
|
|
22
|
+
PathPatch,
|
|
23
|
+
Polygon,
|
|
24
|
+
)
|
|
22
25
|
from matplotlib.path import Path
|
|
23
26
|
|
|
24
27
|
import drawlib.v0_2.private.validators.args as validator
|
|
@@ -235,6 +238,55 @@ class CanvasBase:
|
|
|
235
238
|
# Shape
|
|
236
239
|
#
|
|
237
240
|
|
|
241
|
+
@error_handler
|
|
242
|
+
def polygon(
|
|
243
|
+
self,
|
|
244
|
+
xys: List[Tuple[float, float]],
|
|
245
|
+
style: Union[ShapeStyle, str, None] = None,
|
|
246
|
+
text: str = "",
|
|
247
|
+
textsize: Optional[float] = None,
|
|
248
|
+
textstyle: Union[ShapeTextStyle, str, None] = None,
|
|
249
|
+
) -> None:
|
|
250
|
+
"""Draw a polygon on the canvas.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
xys: List of vertices [(x1, y1), ...(x_n, y_n)].
|
|
254
|
+
style (optional): Style of the polygon.
|
|
255
|
+
text (optional): Text shown at the center of the polygon.
|
|
256
|
+
textsize (optional): Font size of the text.
|
|
257
|
+
textstyle (optional): Style of the text.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
None
|
|
261
|
+
"""
|
|
262
|
+
style, textstyle = ShapeUtil.format_styles(
|
|
263
|
+
style,
|
|
264
|
+
textstyle,
|
|
265
|
+
dtheme.polygonstyles.get,
|
|
266
|
+
dtheme.polygontextstyles.get,
|
|
267
|
+
)
|
|
268
|
+
validator.validate_shape_args(locals())
|
|
269
|
+
|
|
270
|
+
if textsize is not None:
|
|
271
|
+
textstyle.size = textsize
|
|
272
|
+
|
|
273
|
+
style.halign = None
|
|
274
|
+
style.valign = None
|
|
275
|
+
options = ShapeUtil.get_shape_options(style)
|
|
276
|
+
self._artists.append(Polygon(xy=xys, closed=True, **options))
|
|
277
|
+
|
|
278
|
+
if not text:
|
|
279
|
+
return
|
|
280
|
+
center, (_, _) = get_center_and_size(xys)
|
|
281
|
+
self._artists.append(
|
|
282
|
+
ShapeUtil.get_shape_text(
|
|
283
|
+
center,
|
|
284
|
+
text=text,
|
|
285
|
+
angle=0,
|
|
286
|
+
style=textstyle,
|
|
287
|
+
),
|
|
288
|
+
)
|
|
289
|
+
|
|
238
290
|
@error_handler
|
|
239
291
|
def shape( # noqa: C901
|
|
240
292
|
self,
|
|
@@ -266,6 +266,7 @@ line = canvas.line
|
|
|
266
266
|
line_curved = canvas.line_curved
|
|
267
267
|
line_bezier1 = canvas.line_bezier1
|
|
268
268
|
line_bezier2 = canvas.line_bezier2
|
|
269
|
+
line_arc = canvas.line_arc
|
|
269
270
|
lines = canvas.lines
|
|
270
271
|
lines_curved = canvas.lines_curved
|
|
271
272
|
lines_bezier = canvas.lines_bezier
|
|
@@ -275,10 +276,16 @@ rhombus = canvas.rhombus
|
|
|
275
276
|
trapezoid = canvas.trapezoid
|
|
276
277
|
parallelogram = canvas.parallelogram
|
|
277
278
|
triangle = canvas.triangle
|
|
278
|
-
arrow = canvas.arrow
|
|
279
279
|
star = canvas.star
|
|
280
280
|
chevron = canvas.chevron
|
|
281
281
|
|
|
282
|
+
# original arrow
|
|
283
|
+
arrow = canvas.arrow
|
|
284
|
+
arrow_polyline = canvas.arrow_polyline
|
|
285
|
+
arrow_arc = canvas.arrow_arc
|
|
286
|
+
arrow_l = canvas.arrow_l
|
|
287
|
+
arrow_u = canvas.arrow_u
|
|
288
|
+
|
|
282
289
|
# patches
|
|
283
290
|
arc = canvas.arc
|
|
284
291
|
circle = canvas.circle
|
|
@@ -20,7 +20,7 @@ import drawlib.v0_2.private.validators.args as validator
|
|
|
20
20
|
from drawlib.v0_2.private.core.model import LineStyle
|
|
21
21
|
from drawlib.v0_2.private.core.util import LineUtil
|
|
22
22
|
from drawlib.v0_2.private.core_canvas.base import CanvasBase
|
|
23
|
-
from drawlib.v0_2.private.util import error_handler
|
|
23
|
+
from drawlib.v0_2.private.util import error_handler, get_rotated_path_points
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class CanvasLineFeature(CanvasBase):
|
|
@@ -191,6 +191,62 @@ class CanvasLineFeature(CanvasBase):
|
|
|
191
191
|
style=style,
|
|
192
192
|
)
|
|
193
193
|
|
|
194
|
+
@error_handler
|
|
195
|
+
def line_arc(
|
|
196
|
+
self,
|
|
197
|
+
xy: Tuple[float, float],
|
|
198
|
+
width: float,
|
|
199
|
+
height: float,
|
|
200
|
+
angle_start: float = 0,
|
|
201
|
+
angle_end: float = 180,
|
|
202
|
+
angle: float = 0,
|
|
203
|
+
arrowhead: Literal["", "->", "<-", "<->"] = "",
|
|
204
|
+
lwidth: Optional[float] = None,
|
|
205
|
+
style: Union[LineStyle, str, None] = None,
|
|
206
|
+
) -> None:
|
|
207
|
+
"""Draw arc line on ellipse.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
xy: Tuple[float, float]: The center point of the ellipse from which the arc is drawn.
|
|
211
|
+
width: float: The width of the ellipse.
|
|
212
|
+
height: float: The height of the ellipse
|
|
213
|
+
angle_start: float: The starting angle of the arc in degrees (default is 0).
|
|
214
|
+
angle_end: float: The ending angle of the arc in degrees (default is 180).
|
|
215
|
+
angle (float): The angle of ellipse.
|
|
216
|
+
arrowhead: Literal["", "->", "<-", "<->"]: Optional arrowhead style ("", "->", "<-", "<->").
|
|
217
|
+
lwidth: Optional[float]: Optional width of the line.
|
|
218
|
+
style: Union[LineStyle, str, None]: Optional line style.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
None
|
|
222
|
+
"""
|
|
223
|
+
style = LineUtil.format_style(style)
|
|
224
|
+
# validator.validate_line_args(locals())
|
|
225
|
+
|
|
226
|
+
if angle_start > angle_end:
|
|
227
|
+
angle_start = -1 * (360 - angle_start)
|
|
228
|
+
path_points = LineArcHelper.get_ellipse_path_points(
|
|
229
|
+
xy,
|
|
230
|
+
width,
|
|
231
|
+
height,
|
|
232
|
+
angle_start,
|
|
233
|
+
angle_end,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
if angle != 0:
|
|
237
|
+
path_points = get_rotated_path_points(path_points, xy, angle) # type: ignore
|
|
238
|
+
|
|
239
|
+
start_point = path_points[0]
|
|
240
|
+
path_points = path_points[1:]
|
|
241
|
+
|
|
242
|
+
self.lines_bezier(
|
|
243
|
+
start_point, # type: ignore
|
|
244
|
+
path_points=path_points, # type: ignore
|
|
245
|
+
width=lwidth,
|
|
246
|
+
arrowhead=arrowhead,
|
|
247
|
+
style=style,
|
|
248
|
+
)
|
|
249
|
+
|
|
194
250
|
@error_handler
|
|
195
251
|
def lines(
|
|
196
252
|
self,
|
|
@@ -332,6 +388,141 @@ class CanvasLineFeature(CanvasBase):
|
|
|
332
388
|
self._artists.append(FancyArrowPatch(path=path, **options))
|
|
333
389
|
|
|
334
390
|
|
|
391
|
+
class LineArcHelper:
|
|
392
|
+
"""Internal class"""
|
|
393
|
+
|
|
394
|
+
@classmethod
|
|
395
|
+
def get_point_on_ellipse(
|
|
396
|
+
cls,
|
|
397
|
+
xy: Tuple[float, float],
|
|
398
|
+
width: float,
|
|
399
|
+
height: float,
|
|
400
|
+
angle: float,
|
|
401
|
+
) -> Tuple[float, float]:
|
|
402
|
+
"""Internal function"""
|
|
403
|
+
x, y = xy
|
|
404
|
+
# Convert angle from degrees to radians
|
|
405
|
+
angle = math.radians(angle)
|
|
406
|
+
|
|
407
|
+
# Calculate the coordinates of the point
|
|
408
|
+
point_x = x + width / 2 * math.cos(angle)
|
|
409
|
+
point_y = y + height / 2 * math.sin(angle)
|
|
410
|
+
|
|
411
|
+
return point_x, point_y
|
|
412
|
+
|
|
413
|
+
@classmethod
|
|
414
|
+
def get_ellipse_path_points(
|
|
415
|
+
cls,
|
|
416
|
+
xy: Tuple[float, float],
|
|
417
|
+
width: float,
|
|
418
|
+
height: float,
|
|
419
|
+
angle_start: float,
|
|
420
|
+
angle_end: float,
|
|
421
|
+
) -> List[
|
|
422
|
+
Union[
|
|
423
|
+
Tuple[float, float],
|
|
424
|
+
Tuple[Tuple[float, float], Tuple[float, float], Tuple[float, float]],
|
|
425
|
+
]
|
|
426
|
+
]:
|
|
427
|
+
"""Internal function"""
|
|
428
|
+
diff = angle_end - angle_start
|
|
429
|
+
if abs(diff) > 270:
|
|
430
|
+
# having +2 for avoiding situation next_mid_angle == angle_end
|
|
431
|
+
step = int(diff / 3)
|
|
432
|
+
elif abs(diff) > 135:
|
|
433
|
+
step = int(diff / 2)
|
|
434
|
+
else:
|
|
435
|
+
p1, p2, p3, p4 = cls.bezier_ellipse_arc_approximation(
|
|
436
|
+
xy,
|
|
437
|
+
width,
|
|
438
|
+
height,
|
|
439
|
+
angle_start,
|
|
440
|
+
angle_end,
|
|
441
|
+
)
|
|
442
|
+
return [p1, (p2, p3, p4)]
|
|
443
|
+
if diff >= 0:
|
|
444
|
+
diff += 2
|
|
445
|
+
else:
|
|
446
|
+
diff -= 2
|
|
447
|
+
|
|
448
|
+
i = 0
|
|
449
|
+
start = None
|
|
450
|
+
path_points = []
|
|
451
|
+
while True:
|
|
452
|
+
last_mid_angle = angle_start + step * i
|
|
453
|
+
next_mid_angle = angle_start + step * (i + 1)
|
|
454
|
+
|
|
455
|
+
if angle_start < angle_end:
|
|
456
|
+
# anti clock wise
|
|
457
|
+
is_last = next_mid_angle > angle_end
|
|
458
|
+
else:
|
|
459
|
+
# clock wise
|
|
460
|
+
is_last = angle_end > next_mid_angle
|
|
461
|
+
|
|
462
|
+
if is_last:
|
|
463
|
+
p1, p2, p3, p4 = cls.bezier_ellipse_arc_approximation(
|
|
464
|
+
xy,
|
|
465
|
+
width,
|
|
466
|
+
height,
|
|
467
|
+
last_mid_angle,
|
|
468
|
+
angle_end,
|
|
469
|
+
)
|
|
470
|
+
if start is None:
|
|
471
|
+
start = p1
|
|
472
|
+
path_points.append((p2, p3, p4))
|
|
473
|
+
break
|
|
474
|
+
|
|
475
|
+
else:
|
|
476
|
+
p1, p2, p3, p4 = cls.bezier_ellipse_arc_approximation(
|
|
477
|
+
xy,
|
|
478
|
+
width,
|
|
479
|
+
height,
|
|
480
|
+
last_mid_angle,
|
|
481
|
+
next_mid_angle,
|
|
482
|
+
)
|
|
483
|
+
if start is None:
|
|
484
|
+
start = p1
|
|
485
|
+
path_points.append((p2, p3, p4))
|
|
486
|
+
|
|
487
|
+
i += 1
|
|
488
|
+
|
|
489
|
+
path_points.insert(0, start)
|
|
490
|
+
return path_points
|
|
491
|
+
|
|
492
|
+
@classmethod
|
|
493
|
+
def bezier_ellipse_arc_approximation(
|
|
494
|
+
cls,
|
|
495
|
+
xy: Tuple[float, float],
|
|
496
|
+
width: float,
|
|
497
|
+
height: float,
|
|
498
|
+
start_angle: float,
|
|
499
|
+
end_angle: float,
|
|
500
|
+
) -> Tuple[Tuple[float, float], Tuple[float, float], Tuple[float, float], Tuple[float, float]]:
|
|
501
|
+
"""Internal function"""
|
|
502
|
+
x, y = xy
|
|
503
|
+
|
|
504
|
+
# Convert angles from degrees to radians
|
|
505
|
+
start_angle = math.radians(start_angle)
|
|
506
|
+
end_angle = math.radians(end_angle)
|
|
507
|
+
|
|
508
|
+
# Calculate the points on the ellipse at the start and end angles
|
|
509
|
+
start_point = (x + width / 2 * math.cos(start_angle), y + height / 2 * math.sin(start_angle))
|
|
510
|
+
end_point = (x + width / 2 * math.cos(end_angle), y + height / 2 * math.sin(end_angle))
|
|
511
|
+
|
|
512
|
+
# Calculate the control points for the Bezier curve
|
|
513
|
+
t = (4 / 3) * math.tan((end_angle - start_angle) / 4)
|
|
514
|
+
control_point1 = (
|
|
515
|
+
start_point[0] - t * width / 2 * math.sin(start_angle),
|
|
516
|
+
start_point[1] + t * height / 2 * math.cos(start_angle),
|
|
517
|
+
)
|
|
518
|
+
control_point2 = (
|
|
519
|
+
end_point[0] + t * width / 2 * math.sin(end_angle),
|
|
520
|
+
end_point[1] - t * height / 2 * math.cos(end_angle),
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
return start_point, control_point1, control_point2, end_point
|
|
524
|
+
|
|
525
|
+
|
|
335
526
|
def _get_mid_points(
|
|
336
527
|
a: Tuple[float, float],
|
|
337
528
|
b: Tuple[float, float],
|