ScadPy 0.1.0__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.
Files changed (236) hide show
  1. scadpy/__init__.py +5 -0
  2. scadpy/color/__init__.py +3 -0
  3. scadpy/color/constants/BEIGE.py +3 -0
  4. scadpy/color/constants/BLACK.py +3 -0
  5. scadpy/color/constants/BLUE.py +3 -0
  6. scadpy/color/constants/BROWN.py +3 -0
  7. scadpy/color/constants/DARK_GRAY.py +3 -0
  8. scadpy/color/constants/DEFAULT_COLOR.py +3 -0
  9. scadpy/color/constants/DEFAULT_OPACITY.py +1 -0
  10. scadpy/color/constants/GRAY.py +3 -0
  11. scadpy/color/constants/GREEN.py +3 -0
  12. scadpy/color/constants/ORANGE.py +3 -0
  13. scadpy/color/constants/RED.py +3 -0
  14. scadpy/color/constants/WHITE.py +3 -0
  15. scadpy/color/constants/YELLOW.py +3 -0
  16. scadpy/color/constants/__init__.py +29 -0
  17. scadpy/color/type/__init__.py +3 -0
  18. scadpy/color/type/color.py +3 -0
  19. scadpy/color/utils/__init__.py +3 -0
  20. scadpy/color/utils/get_random_color.py +36 -0
  21. scadpy/core/__init__.py +3 -0
  22. scadpy/core/assembly/__init__.py +5 -0
  23. scadpy/core/assembly/combinations/__init__.py +13 -0
  24. scadpy/core/assembly/combinations/concat_assemblies.py +50 -0
  25. scadpy/core/assembly/combinations/exclude_assemblies.py +135 -0
  26. scadpy/core/assembly/combinations/intersect_assemblies.py +128 -0
  27. scadpy/core/assembly/combinations/subtract_assemblies.py +151 -0
  28. scadpy/core/assembly/combinations/unify_assemblies.py +59 -0
  29. scadpy/core/assembly/topologies/__init__.py +41 -0
  30. scadpy/core/assembly/topologies/directed_edge/__init__.py +9 -0
  31. scadpy/core/assembly/topologies/directed_edge/get_assembly_directed_edge_directions.py +70 -0
  32. scadpy/core/assembly/topologies/directed_edge/get_assembly_directed_edge_to_edge.py +49 -0
  33. scadpy/core/assembly/topologies/directed_edge/get_assembly_directed_edge_to_vertex.py +54 -0
  34. scadpy/core/assembly/topologies/edge/__init__.py +9 -0
  35. scadpy/core/assembly/topologies/edge/get_assembly_edge_lengths.py +46 -0
  36. scadpy/core/assembly/topologies/edge/get_assembly_edge_midpoints.py +51 -0
  37. scadpy/core/assembly/topologies/edge/get_assembly_edge_normals.py +67 -0
  38. scadpy/core/assembly/topologies/face_corner/__init__.py +13 -0
  39. scadpy/core/assembly/topologies/face_corner/get_assembly_face_corner_angles.py +72 -0
  40. scadpy/core/assembly/topologies/face_corner/get_assembly_face_corner_normals.py +103 -0
  41. scadpy/core/assembly/topologies/face_corner/get_assembly_face_corner_to_incoming_directed_edge.py +65 -0
  42. scadpy/core/assembly/topologies/face_corner/get_assembly_face_corner_to_outgoing_directed_edge.py +65 -0
  43. scadpy/core/assembly/topologies/face_corner/get_assembly_face_directed_edge_to_corner.py +79 -0
  44. scadpy/core/assembly/topologies/part/__init__.py +5 -0
  45. scadpy/core/assembly/topologies/part/get_assembly_part_colors.py +55 -0
  46. scadpy/core/assembly/topologies/vertex/__init__.py +7 -0
  47. scadpy/core/assembly/topologies/vertex/get_assembly_vertex_coordinates.py +70 -0
  48. scadpy/core/assembly/topologies/vertex/get_assembly_vertex_to_part.py +62 -0
  49. scadpy/core/assembly/transformations/__init__.py +19 -0
  50. scadpy/core/assembly/transformations/color_assembly.py +24 -0
  51. scadpy/core/assembly/transformations/mirror_vertex_coordinates.py +68 -0
  52. scadpy/core/assembly/transformations/pull_vertex_coordinates.py +64 -0
  53. scadpy/core/assembly/transformations/push_vertex_coordinates.py +64 -0
  54. scadpy/core/assembly/transformations/resize_vertex_coordinates.py +121 -0
  55. scadpy/core/assembly/transformations/rotate_vertex_coordinates.py +73 -0
  56. scadpy/core/assembly/transformations/scale_vertex_coordinates.py +76 -0
  57. scadpy/core/assembly/transformations/translate_vertex_coordinates.py +70 -0
  58. scadpy/core/assembly/types/__init__.py +7 -0
  59. scadpy/core/assembly/types/assembly.py +14 -0
  60. scadpy/core/assembly/types/topology_filter.py +6 -0
  61. scadpy/core/assembly/utils/__init__.py +9 -0
  62. scadpy/core/assembly/utils/lookup_pairs.py +56 -0
  63. scadpy/core/assembly/utils/resolve_topology_filter.py +84 -0
  64. scadpy/core/assembly/utils/transform_filtered_parts.py +55 -0
  65. scadpy/core/component/__init__.py +3 -0
  66. scadpy/core/component/exporters/__init__.py +7 -0
  67. scadpy/core/component/exporters/map_component_to_html_file.py +47 -0
  68. scadpy/core/component/exporters/map_component_to_screen.py +63 -0
  69. scadpy/core/component/features/__init__.py +5 -0
  70. scadpy/core/component/features/get_component_bounds.py +38 -0
  71. scadpy/core/component/utils/__init__.py +9 -0
  72. scadpy/core/component/utils/blend_component_colors.py +77 -0
  73. scadpy/core/component/utils/get_intersecting_component_index_groups.py +108 -0
  74. scadpy/core/part/__init__.py +3 -0
  75. scadpy/core/part/combinations/__init__.py +11 -0
  76. scadpy/core/part/combinations/concat_parts.py +48 -0
  77. scadpy/core/part/combinations/intersect_parts.py +147 -0
  78. scadpy/core/part/combinations/subtract_parts.py +94 -0
  79. scadpy/core/part/combinations/unify_parts.py +143 -0
  80. scadpy/core/part/types/__init__.py +5 -0
  81. scadpy/core/part/types/part.py +34 -0
  82. scadpy/core/part/utils/__init__.py +5 -0
  83. scadpy/core/part/utils/blend_part_colors.py +32 -0
  84. scadpy/d2/__init__.py +2 -0
  85. scadpy/d2/shape/__init__.py +9 -0
  86. scadpy/d2/shape/combinations/__init__.py +21 -0
  87. scadpy/d2/shape/combinations/are_shape_parts_intersecting.py +49 -0
  88. scadpy/d2/shape/combinations/concat_shape.py +48 -0
  89. scadpy/d2/shape/combinations/exclude_shape.py +71 -0
  90. scadpy/d2/shape/combinations/intersect_shape.py +64 -0
  91. scadpy/d2/shape/combinations/intersect_shape_parts.py +71 -0
  92. scadpy/d2/shape/combinations/subtract_shape.py +72 -0
  93. scadpy/d2/shape/combinations/subtract_shape_parts.py +66 -0
  94. scadpy/d2/shape/combinations/unify_shape.py +51 -0
  95. scadpy/d2/shape/combinations/unify_shape_parts.py +74 -0
  96. scadpy/d2/shape/exporters/__init__.py +17 -0
  97. scadpy/d2/shape/exporters/map_shape_to_dxf.py +43 -0
  98. scadpy/d2/shape/exporters/map_shape_to_dxf_file.py +38 -0
  99. scadpy/d2/shape/exporters/map_shape_to_html.py +117 -0
  100. scadpy/d2/shape/exporters/map_shape_to_html_file.py +58 -0
  101. scadpy/d2/shape/exporters/map_shape_to_screen.py +51 -0
  102. scadpy/d2/shape/exporters/map_shape_to_svg.py +40 -0
  103. scadpy/d2/shape/exporters/map_shape_to_svg_file.py +38 -0
  104. scadpy/d2/shape/features/__init__.py +9 -0
  105. scadpy/d2/shape/features/get_shape_bounds.py +40 -0
  106. scadpy/d2/shape/features/get_shape_part_bounds.py +37 -0
  107. scadpy/d2/shape/features/is_shape_empty.py +38 -0
  108. scadpy/d2/shape/importers/__init__.py +13 -0
  109. scadpy/d2/shape/importers/map_dxf_to_shape.py +55 -0
  110. scadpy/d2/shape/importers/map_geometries_to_shape.py +45 -0
  111. scadpy/d2/shape/importers/map_geometry_to_shape.py +43 -0
  112. scadpy/d2/shape/importers/map_parts_to_shape.py +62 -0
  113. scadpy/d2/shape/importers/map_svg_to_shape.py +55 -0
  114. scadpy/d2/shape/primitives/__init__.py +6 -0
  115. scadpy/d2/shape/primitives/circle.py +68 -0
  116. scadpy/d2/shape/primitives/polygon.py +86 -0
  117. scadpy/d2/shape/primitives/rectangle.py +85 -0
  118. scadpy/d2/shape/primitives/square.py +57 -0
  119. scadpy/d2/shape/topologies/__init__.py +53 -0
  120. scadpy/d2/shape/topologies/corner/__init__.py +15 -0
  121. scadpy/d2/shape/topologies/corner/are_shape_corners_convex.py +75 -0
  122. scadpy/d2/shape/topologies/corner/get_shape_corner_angles.py +58 -0
  123. scadpy/d2/shape/topologies/corner/get_shape_corner_normals.py +82 -0
  124. scadpy/d2/shape/topologies/corner/get_shape_corner_to_incoming_directed_edge.py +39 -0
  125. scadpy/d2/shape/topologies/corner/get_shape_corner_to_outgoing_directed_edge.py +39 -0
  126. scadpy/d2/shape/topologies/corner/get_shape_corner_to_vertex.py +65 -0
  127. scadpy/d2/shape/topologies/directed_edge/__init__.py +11 -0
  128. scadpy/d2/shape/topologies/directed_edge/get_shape_directed_edge_directions.py +44 -0
  129. scadpy/d2/shape/topologies/directed_edge/get_shape_directed_edge_to_corner.py +41 -0
  130. scadpy/d2/shape/topologies/directed_edge/get_shape_directed_edge_to_edge.py +51 -0
  131. scadpy/d2/shape/topologies/directed_edge/get_shape_directed_edge_to_vertex.py +63 -0
  132. scadpy/d2/shape/topologies/edge/__init__.py +11 -0
  133. scadpy/d2/shape/topologies/edge/get_shape_edge_lengths.py +43 -0
  134. scadpy/d2/shape/topologies/edge/get_shape_edge_midpoints.py +46 -0
  135. scadpy/d2/shape/topologies/edge/get_shape_edge_normals.py +40 -0
  136. scadpy/d2/shape/topologies/edge/get_shape_edge_to_vertex.py +71 -0
  137. scadpy/d2/shape/topologies/ring/__init__.py +7 -0
  138. scadpy/d2/shape/topologies/ring/get_shape_ring_to_part.py +46 -0
  139. scadpy/d2/shape/topologies/ring/get_shape_ring_types.py +46 -0
  140. scadpy/d2/shape/topologies/vertex/__init__.py +11 -0
  141. scadpy/d2/shape/topologies/vertex/get_shape_part_vertex_coordinates.py +62 -0
  142. scadpy/d2/shape/topologies/vertex/get_shape_vertex_coordinates.py +44 -0
  143. scadpy/d2/shape/topologies/vertex/get_shape_vertex_to_part.py +42 -0
  144. scadpy/d2/shape/topologies/vertex/get_shape_vertex_to_ring.py +63 -0
  145. scadpy/d2/shape/transformations/__init__.py +43 -0
  146. scadpy/d2/shape/transformations/chamfer_shape.py +259 -0
  147. scadpy/d2/shape/transformations/color_shape.py +46 -0
  148. scadpy/d2/shape/transformations/convexify_shape.py +79 -0
  149. scadpy/d2/shape/transformations/fill_shape.py +68 -0
  150. scadpy/d2/shape/transformations/fillet_shape.py +289 -0
  151. scadpy/d2/shape/transformations/grow_shape.py +82 -0
  152. scadpy/d2/shape/transformations/linear_cut_shape.py +116 -0
  153. scadpy/d2/shape/transformations/linear_extrude_shape.py +60 -0
  154. scadpy/d2/shape/transformations/linear_slice_shape.py +144 -0
  155. scadpy/d2/shape/transformations/mirror_shape.py +53 -0
  156. scadpy/d2/shape/transformations/pull_shape.py +67 -0
  157. scadpy/d2/shape/transformations/push_shape.py +67 -0
  158. scadpy/d2/shape/transformations/radial_extrude_shape.py +285 -0
  159. scadpy/d2/shape/transformations/radial_slice_shape.py +132 -0
  160. scadpy/d2/shape/transformations/recoordinate_shape.py +82 -0
  161. scadpy/d2/shape/transformations/resize_shape.py +91 -0
  162. scadpy/d2/shape/transformations/rotate_shape.py +63 -0
  163. scadpy/d2/shape/transformations/scale_shape.py +58 -0
  164. scadpy/d2/shape/transformations/shrink_shape.py +69 -0
  165. scadpy/d2/shape/transformations/translate_shape.py +54 -0
  166. scadpy/d2/shape/types/__init__.py +3 -0
  167. scadpy/d2/shape/types/shape.py +792 -0
  168. scadpy/d2/shape/types/utils/__init__.py +5 -0
  169. scadpy/d2/shape/types/utils/shapely_base_geometry_to_shapely_polygons.py +25 -0
  170. scadpy/d2/shape/utils/__init__.py +5 -0
  171. scadpy/d2/shape/utils/shapely_base_geometry_to_shapely_polygons.py +55 -0
  172. scadpy/d2/utils/__init__.py +3 -0
  173. scadpy/d2/utils/resolve_vector_2d.py +50 -0
  174. scadpy/d3/__init__.py +2 -0
  175. scadpy/d3/solid/__init__.py +8 -0
  176. scadpy/d3/solid/combinations/__init__.py +21 -0
  177. scadpy/d3/solid/combinations/are_solid_parts_intersecting.py +51 -0
  178. scadpy/d3/solid/combinations/concat_solid.py +48 -0
  179. scadpy/d3/solid/combinations/exclude_solid.py +71 -0
  180. scadpy/d3/solid/combinations/intersect_solid.py +64 -0
  181. scadpy/d3/solid/combinations/intersect_solid_parts.py +73 -0
  182. scadpy/d3/solid/combinations/subtract_solid.py +72 -0
  183. scadpy/d3/solid/combinations/subtract_solid_parts.py +68 -0
  184. scadpy/d3/solid/combinations/unify_solid.py +51 -0
  185. scadpy/d3/solid/combinations/unify_solid_parts.py +73 -0
  186. scadpy/d3/solid/exporters/__init__.py +11 -0
  187. scadpy/d3/solid/exporters/map_solid_to_html.py +318 -0
  188. scadpy/d3/solid/exporters/map_solid_to_html_file.py +58 -0
  189. scadpy/d3/solid/exporters/map_solid_to_screen.py +51 -0
  190. scadpy/d3/solid/exporters/map_solid_to_stl_file.py +48 -0
  191. scadpy/d3/solid/features/__init__.py +11 -0
  192. scadpy/d3/solid/features/get_solid_bounds.py +37 -0
  193. scadpy/d3/solid/features/get_solid_part_bounds.py +37 -0
  194. scadpy/d3/solid/features/get_solid_part_colors.py +39 -0
  195. scadpy/d3/solid/features/is_solid_empty.py +36 -0
  196. scadpy/d3/solid/importers/__init__.py +11 -0
  197. scadpy/d3/solid/importers/map_geometries_to_solid.py +42 -0
  198. scadpy/d3/solid/importers/map_geometry_to_solid.py +42 -0
  199. scadpy/d3/solid/importers/map_parts_to_solid.py +66 -0
  200. scadpy/d3/solid/importers/map_stl_to_solid.py +37 -0
  201. scadpy/d3/solid/primitives/__init__.py +7 -0
  202. scadpy/d3/solid/primitives/cone.py +70 -0
  203. scadpy/d3/solid/primitives/cuboid.py +75 -0
  204. scadpy/d3/solid/primitives/cylinder.py +73 -0
  205. scadpy/d3/solid/primitives/polyhedron.py +60 -0
  206. scadpy/d3/solid/primitives/sphere.py +58 -0
  207. scadpy/d3/solid/topologies/__init__.py +8 -0
  208. scadpy/d3/solid/topologies/triangle/__init__.py +5 -0
  209. scadpy/d3/solid/topologies/triangle/get_solid_triangle_to_vertex.py +49 -0
  210. scadpy/d3/solid/topologies/vertex/__init__.py +7 -0
  211. scadpy/d3/solid/topologies/vertex/get_solid_vertex_coordinates.py +39 -0
  212. scadpy/d3/solid/topologies/vertex/get_solid_vertex_to_part.py +37 -0
  213. scadpy/d3/solid/transformations/__init__.py +23 -0
  214. scadpy/d3/solid/transformations/color_solid.py +46 -0
  215. scadpy/d3/solid/transformations/convexify_solid.py +64 -0
  216. scadpy/d3/solid/transformations/mirror_solid.py +53 -0
  217. scadpy/d3/solid/transformations/pull_solid.py +67 -0
  218. scadpy/d3/solid/transformations/push_solid.py +67 -0
  219. scadpy/d3/solid/transformations/recoordinate_solid.py +68 -0
  220. scadpy/d3/solid/transformations/resize_solid.py +92 -0
  221. scadpy/d3/solid/transformations/rotate_solid.py +93 -0
  222. scadpy/d3/solid/transformations/scale_solid.py +58 -0
  223. scadpy/d3/solid/transformations/translate_solid.py +54 -0
  224. scadpy/d3/solid/types/__init__.py +3 -0
  225. scadpy/d3/solid/types/solid.py +448 -0
  226. scadpy/d3/utils/__init__.py +3 -0
  227. scadpy/d3/utils/resolve_vector_3d.py +50 -0
  228. scadpy/utils/__init__.py +6 -0
  229. scadpy/utils/resolve_vector.py +64 -0
  230. scadpy/utils/x.py +38 -0
  231. scadpy/utils/y.py +38 -0
  232. scadpy/utils/z.py +38 -0
  233. scadpy-0.1.0.dist-info/METADATA +282 -0
  234. scadpy-0.1.0.dist-info/RECORD +236 -0
  235. scadpy-0.1.0.dist-info/WHEEL +4 -0
  236. scadpy-0.1.0.dist-info/licenses/LICENSE.md +43 -0
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable
4
+ from typing import TYPE_CHECKING
5
+
6
+ from shapely.geometry.polygon import Polygon
7
+ from typeguard import typechecked
8
+
9
+ if TYPE_CHECKING:
10
+ from scadpy.core.part import Part
11
+ from scadpy.d2.shape import Shape
12
+
13
+
14
+ @typechecked
15
+ def subtract_shape_parts(
16
+ to_be_subtracted: Part[Polygon],
17
+ to_subtract: Part[Polygon],
18
+ make_assembly_from_parts: Callable[[list[Part[Polygon]]], Shape],
19
+ ) -> Shape:
20
+ """Subtract one shape part from another and return the resulting shape.
21
+
22
+ Shortcut for :func:`subtract_parts`.
23
+ See :func:`subtract_parts` for full documentation.
24
+
25
+ Parameters
26
+ ----------
27
+ to_be_subtracted : Part[Polygon]
28
+ The part to subtract from.
29
+ to_subtract : Part[Polygon]
30
+ The part to subtract.
31
+ make_assembly_from_parts : Callable[[list[Part[Polygon]]], Shape]
32
+ Factory function to build the resulting Shape from a sequence of parts.
33
+
34
+ Returns
35
+ -------
36
+ Shape
37
+ A new shape with the geometry of ``to_subtract`` removed from ``to_be_subtracted``.
38
+
39
+ Examples
40
+ --------
41
+ >>> from scadpy import square, circle, subtract_shape_parts, Shape
42
+
43
+ >>> subtract_shape_parts(
44
+ ... to_be_subtracted=square(4)._parts[0],
45
+ ... to_subtract=circle(radius=1)._parts[0],
46
+ ... make_assembly_from_parts=Shape.from_parts,
47
+ ... ) # doctest: +SKIP
48
+
49
+ .. render-example::
50
+ :name: subtract_shape_parts_example
51
+ :example: subtract_shape_parts(to_be_subtracted=square(4)._parts[0], to_subtract=circle(radius=1)._parts[0], make_assembly_from_parts=Shape.from_parts)
52
+ :ghost: square(4)
53
+ """
54
+ from scadpy import Part, shapely_base_geometry_to_shapely_polygons, subtract_parts
55
+
56
+ return subtract_parts(
57
+ to_be_subtracted=to_be_subtracted,
58
+ to_subtract=to_subtract,
59
+ get_part_color=lambda p: p.color,
60
+ get_part_geometry=lambda p: p.geometry,
61
+ subtract_geometries=lambda p_1, p_2: (
62
+ shapely_base_geometry_to_shapely_polygons(p_1 - p_2)
63
+ ),
64
+ make_part_from_geometry=Part[Polygon].from_geometry,
65
+ make_assembly_from_parts=make_assembly_from_parts,
66
+ )
@@ -0,0 +1,51 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Sequence
4
+ from typing import TYPE_CHECKING
5
+
6
+ from typeguard import typechecked
7
+
8
+ if TYPE_CHECKING:
9
+ from scadpy import Shape
10
+
11
+
12
+ @typechecked
13
+ def unify_shape(shapes: Sequence[Shape]) -> Shape:
14
+ """Unite a sequence of shapes into a single shape using boolean union.
15
+
16
+ All overlapping parts across the input shapes are merged geometrically.
17
+ Use :func:`concat_shape` if you want to combine shapes without merging overlaps.
18
+
19
+ Parameters
20
+ ----------
21
+ shapes : Sequence[Shape]
22
+ The shapes to unite.
23
+
24
+ Returns
25
+ -------
26
+ Shape
27
+ A new shape containing the geometric union of all input shapes.
28
+
29
+ Examples
30
+ --------
31
+ >>> from scadpy import square, circle, unify_shape
32
+
33
+ >>> unify_shape( # doctest: +SKIP
34
+ ... shapes=[square(4), circle(radius=2).translate([2, 0])]
35
+ ... )
36
+
37
+ .. render-example::
38
+ :name: unify_shape
39
+ :example: unify_shape(shapes=[square(4), circle(radius=2).translate([2, 0])])
40
+ """
41
+ from scadpy import Shape, unify_shape_parts
42
+ from scadpy.core.assembly import unify_assemblies
43
+
44
+ return unify_assemblies(
45
+ assemblies=shapes,
46
+ get_assembly_parts=lambda assembly: assembly._parts,
47
+ unify_parts=lambda parts: unify_shape_parts(
48
+ parts=parts,
49
+ make_assembly_from_parts=Shape.from_parts,
50
+ ),
51
+ )
@@ -0,0 +1,74 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Sequence
4
+ from typing import TYPE_CHECKING
5
+
6
+ from shapely.geometry.polygon import Polygon
7
+ from shapely.ops import unary_union
8
+ from typeguard import typechecked
9
+
10
+ if TYPE_CHECKING:
11
+ from scadpy.core.part import Part
12
+ from scadpy.d2.shape import Shape
13
+
14
+
15
+ @typechecked
16
+ def unify_shape_parts(
17
+ parts: Sequence[Part[Polygon]],
18
+ make_assembly_from_parts: Callable[[Sequence[Part[Polygon]]], Shape],
19
+ ) -> Shape:
20
+ """Unite a sequence of shape parts and return the resulting shape.
21
+
22
+ Shortcut for :func:`unify_parts`.
23
+ See :func:`unify_parts` for full documentation.
24
+
25
+ Parameters
26
+ ----------
27
+ parts : Sequence[Part[Polygon]]
28
+ The shape parts to unite.
29
+ make_assembly_from_parts : Callable[[Sequence[Part[Polygon]]], Shape]
30
+ Factory function to build the resulting Shape from a sequence of parts.
31
+
32
+ Returns
33
+ -------
34
+ Shape
35
+ A new shape containing the geometric union of the input parts.
36
+
37
+ Examples
38
+ --------
39
+ >>> from scadpy import square, circle, unify_shape_parts, Shape
40
+
41
+ >>> unify_shape_parts( # doctest: +SKIP
42
+ ... parts=(
43
+ ... list(square(3)._parts)
44
+ ... + list(circle(radius=1.5).translate([2, 2])._parts)
45
+ ... ),
46
+ ... make_assembly_from_parts=Shape.from_parts,
47
+ ... )
48
+
49
+ .. render-example::
50
+ :name: unify_shape_parts_example
51
+ :example: unify_shape_parts(parts=list(square(3)._parts) + list(circle(radius=1.5).translate([2, 2])._parts), make_assembly_from_parts=Shape.from_parts)
52
+ :ghost: concat_shape(shapes=[square(3), circle(radius=1.5).translate([2, 2])])
53
+ """
54
+ from scadpy import (
55
+ Part,
56
+ are_shape_parts_intersecting,
57
+ get_shape_part_bounds,
58
+ shapely_base_geometry_to_shapely_polygons,
59
+ unify_parts,
60
+ )
61
+
62
+ return unify_parts(
63
+ parts=parts,
64
+ get_part_color=lambda p: p.color,
65
+ get_part_magnitude=lambda p: p.geometry.area,
66
+ get_part_bounds=get_shape_part_bounds,
67
+ are_parts_intersecting=are_shape_parts_intersecting,
68
+ get_part_geometry=lambda p: p.geometry,
69
+ unify_geometries=lambda g: shapely_base_geometry_to_shapely_polygons(
70
+ unary_union(g)
71
+ ),
72
+ make_part_from_geometry=Part[Polygon].from_geometry,
73
+ make_assembly_from_parts=make_assembly_from_parts,
74
+ )
@@ -0,0 +1,17 @@
1
+ __all__ = [
2
+ "map_shape_to_dxf",
3
+ "map_shape_to_dxf_file",
4
+ "map_shape_to_html",
5
+ "map_shape_to_html_file",
6
+ "map_shape_to_screen",
7
+ "map_shape_to_svg",
8
+ "map_shape_to_svg_file",
9
+ ]
10
+
11
+ from .map_shape_to_dxf import map_shape_to_dxf
12
+ from .map_shape_to_dxf_file import map_shape_to_dxf_file
13
+ from .map_shape_to_html import map_shape_to_html
14
+ from .map_shape_to_html_file import map_shape_to_html_file
15
+ from .map_shape_to_screen import map_shape_to_screen
16
+ from .map_shape_to_svg import map_shape_to_svg
17
+ from .map_shape_to_svg_file import map_shape_to_svg_file
@@ -0,0 +1,43 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import trimesh
6
+ from shapely.geometry import MultiPolygon
7
+ from trimesh.path.exchange.dxf import export_dxf
8
+ from typeguard import typechecked
9
+
10
+ if TYPE_CHECKING:
11
+ from scadpy import Shape
12
+
13
+
14
+ @typechecked
15
+ def map_shape_to_dxf(shape: Shape) -> str:
16
+ """Export a shape to a DXF string.
17
+
18
+ Each part of the shape is exported as a closed polyline. Polygon
19
+ holes are exported as separate polylines.
20
+
21
+ Parameters
22
+ ----------
23
+ shape : Shape
24
+ The shape to export.
25
+
26
+ Returns
27
+ -------
28
+ str
29
+ A DXF document as a string.
30
+
31
+ Examples
32
+ --------
33
+ >>> from scadpy import square, circle, map_shape_to_dxf
34
+
35
+ >>> dxf = map_shape_to_dxf(square(4) - circle(1))
36
+ >>> dxf.startswith("999")
37
+ True
38
+ """
39
+ geometries = [part.geometry for part in shape._parts]
40
+ if not geometries:
41
+ return ""
42
+
43
+ return export_dxf(trimesh.load_path(MultiPolygon(geometries)))
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING
5
+
6
+ from typeguard import typechecked
7
+
8
+ if TYPE_CHECKING:
9
+ from scadpy import Shape
10
+
11
+
12
+ @typechecked
13
+ def map_shape_to_dxf_file(shape: Shape, path: str | Path) -> int:
14
+ """Save a shape as a DXF file.
15
+
16
+ Parameters
17
+ ----------
18
+ shape : Shape
19
+ The shape to export.
20
+ path : str or Path
21
+ Destination file path.
22
+
23
+ Returns
24
+ -------
25
+ int
26
+ Number of characters written.
27
+
28
+ Examples
29
+ --------
30
+ >>> from scadpy import square, circle, map_shape_to_dxf_file
31
+
32
+ >>> map_shape_to_dxf_file( # doctest: +SKIP
33
+ ... shape=square(4) - circle(1), path="output.dxf"
34
+ ... )
35
+ """
36
+ from scadpy import map_shape_to_dxf
37
+
38
+ return Path(path).write_text(map_shape_to_dxf(shape), encoding="utf-8")
@@ -0,0 +1,117 @@
1
+ from __future__ import annotations
2
+
3
+ from io import StringIO
4
+ from typing import TYPE_CHECKING
5
+
6
+ import matplotlib.pyplot as plt
7
+ from IPython.core.display import HTML
8
+ from shapely.plotting import plot_polygon
9
+ from typeguard import typechecked
10
+
11
+ from scadpy.color.constants import BLACK, WHITE
12
+
13
+ if TYPE_CHECKING:
14
+ from scadpy import Color, Shape
15
+
16
+
17
+ @typechecked
18
+ def map_shape_to_html(
19
+ shape: Shape,
20
+ background_color: Color = WHITE,
21
+ foreground_color: Color = BLACK,
22
+ ) -> HTML:
23
+ """
24
+ Render a shape assembly as an SVG HTML object using matplotlib and shapely.
25
+
26
+ This function extracts all parts from the assembly, converts each to a Shapely
27
+ polygon, and plots them with their associated colors. The resulting figure is
28
+ exported as SVG and wrapped in an IPython HTML object for display in notebooks
29
+ or web interfaces.
30
+
31
+ Parameters
32
+ ----------
33
+ shape : Shape
34
+ The shape to render.
35
+ background_color : Color, default=WHITE
36
+ The background color of the rendered output.
37
+ foreground_color : Color, default=BLACK
38
+ The foreground color (axes, grid) of the rendered output.
39
+
40
+ Returns
41
+ -------
42
+ HTML
43
+ An IPython HTML object containing the SVG rendering of the shape.
44
+
45
+ Examples
46
+ --------
47
+ >>> from IPython.core.display import HTML
48
+ >>> from scadpy import square, map_shape_to_html
49
+
50
+ >>> html = map_shape_to_html(shape=square(4))
51
+ >>> isinstance(html, HTML)
52
+ True
53
+ """
54
+ foreground_color_hex = "#{:02X}{:02X}{:02X}".format(
55
+ *(int(x * 255) for x in foreground_color[:-1])
56
+ )
57
+ background_color_hex = "#{:02X}{:02X}{:02X}".format(
58
+ *(int(x * 255) for x in background_color[:-1])
59
+ )
60
+ x_min, y_min, x_max, y_max = shape.bounds
61
+
62
+ width = x_max - x_min
63
+ height = y_max - y_min
64
+
65
+ if width > height:
66
+ difference = width - height
67
+ y_max = y_max + difference / 2
68
+ y_min = y_min - difference / 2
69
+ height = width
70
+ else:
71
+ difference = height - width
72
+ x_max = x_max + difference / 2
73
+ x_min = x_min - difference / 2
74
+ width = height
75
+
76
+ if width == 0 or height == 0:
77
+ width = max(width, 1.0)
78
+ height = max(height, 1.0)
79
+
80
+ aspect_ratio = height / width
81
+ fig_width = 5
82
+ fig_height = fig_width * aspect_ratio
83
+ fig, ax = plt.subplots(figsize=(fig_width, fig_height))
84
+
85
+ fig.patch.set_facecolor(background_color_hex)
86
+ ax.set_facecolor(background_color_hex)
87
+ ax.tick_params(axis="both", colors=foreground_color_hex)
88
+
89
+ for spine in ax.spines.values():
90
+ spine.set_edgecolor(foreground_color_hex)
91
+
92
+ for part in shape._parts:
93
+ color = part.color
94
+ edge_color = tuple((c + b) / 2 for c, b in zip(color[:3], background_color[:3]))
95
+ plot_polygon(
96
+ part.geometry,
97
+ ax=ax,
98
+ add_points=False,
99
+ facecolor=(color[0], color[1], color[2], color[3]),
100
+ edgecolor=(*edge_color, 1.0),
101
+ linewidth=2,
102
+ )
103
+
104
+ padding = 0.1 * width
105
+ ax.set_xlim(x_min - padding, x_max + padding)
106
+ ax.set_ylim(y_min - padding, y_max + padding)
107
+
108
+ ax.set_aspect("equal")
109
+ ax.grid(True, alpha=0.3, linewidth=0.5)
110
+ ax.axhline(y=0, color=foreground_color_hex, linewidth=1, alpha=0.3)
111
+ ax.axvline(x=0, color=foreground_color_hex, linewidth=1, alpha=0.3)
112
+
113
+ svg_output = StringIO()
114
+ plt.savefig(svg_output, format="svg", bbox_inches="tight", pad_inches=0)
115
+ plt.close()
116
+
117
+ return HTML(svg_output.getvalue())
@@ -0,0 +1,58 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from scadpy.color.constants import BLACK, WHITE
6
+ from typeguard import typechecked
7
+
8
+ if TYPE_CHECKING:
9
+ from scadpy import Color, Shape
10
+
11
+
12
+ @typechecked
13
+ def map_shape_to_html_file(
14
+ shape: Shape,
15
+ path: str,
16
+ background_color: Color = WHITE,
17
+ foreground_color: Color = BLACK,
18
+ ) -> int:
19
+ """Save a shape as an HTML file.
20
+
21
+ Shortcut for :func:`map_component_to_html_file`.
22
+ See :func:`map_component_to_html_file` for full documentation.
23
+
24
+ Parameters
25
+ ----------
26
+ shape : Shape
27
+ The shape to save.
28
+ path : str
29
+ The file path where the HTML will be written.
30
+ background_color : Color, default=WHITE
31
+ The background color of the rendered output.
32
+ foreground_color : Color, default=BLACK
33
+ The foreground color (axes, grid) of the rendered output.
34
+
35
+ Returns
36
+ -------
37
+ int
38
+ The number of characters written to the file.
39
+
40
+ Examples
41
+ --------
42
+ >>> from scadpy import square, map_shape_to_html_file
43
+
44
+ >>> map_shape_to_html_file( # doctest: +SKIP
45
+ ... shape=square(4), path="output.html"
46
+ ... )
47
+ """
48
+ from scadpy import map_component_to_html_file, map_shape_to_html
49
+
50
+ return map_component_to_html_file(
51
+ component=shape,
52
+ path=path,
53
+ to_html=lambda component: map_shape_to_html(
54
+ shape=component,
55
+ background_color=background_color,
56
+ foreground_color=foreground_color,
57
+ ),
58
+ )
@@ -0,0 +1,51 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from scadpy.color.constants import BLACK, WHITE
6
+ from typeguard import typechecked
7
+
8
+ if TYPE_CHECKING:
9
+ from scadpy import Color, Shape
10
+
11
+
12
+ @typechecked
13
+ def map_shape_to_screen(
14
+ shape: Shape,
15
+ background_color: Color = WHITE,
16
+ foreground_color: Color = BLACK,
17
+ ) -> None:
18
+ """Display a shape in a Qt-based window.
19
+
20
+ Shortcut for :func:`map_component_to_screen`.
21
+ See :func:`map_component_to_screen` for full documentation.
22
+
23
+ Parameters
24
+ ----------
25
+ shape : Shape
26
+ The shape to display.
27
+ background_color : Color, default=WHITE
28
+ The background color of the rendered window.
29
+ foreground_color : Color, default=BLACK
30
+ The foreground color (axes, grid) of the rendered window.
31
+
32
+ Returns
33
+ -------
34
+ None
35
+
36
+ Examples
37
+ --------
38
+ >>> from scadpy import square, map_shape_to_screen
39
+
40
+ >>> map_shape_to_screen(shape=square(4)) # doctest: +SKIP
41
+ """
42
+ from scadpy import map_component_to_screen, map_shape_to_html
43
+
44
+ map_component_to_screen(
45
+ component=shape,
46
+ to_html=lambda component: map_shape_to_html(
47
+ shape=component,
48
+ background_color=background_color,
49
+ foreground_color=foreground_color,
50
+ ),
51
+ )
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import trimesh
6
+ from shapely.geometry import MultiPolygon
7
+ from trimesh.path.exchange.svg_io import export_svg
8
+ from typeguard import typechecked
9
+
10
+ if TYPE_CHECKING:
11
+ from scadpy import Shape
12
+
13
+
14
+ @typechecked
15
+ def map_shape_to_svg(shape: Shape) -> str:
16
+ """Export a shape to an SVG string.
17
+
18
+ Parameters
19
+ ----------
20
+ shape : Shape
21
+ The shape to export.
22
+
23
+ Returns
24
+ -------
25
+ str
26
+ A self-contained SVG document as a string.
27
+
28
+ Examples
29
+ --------
30
+ >>> from scadpy import square, circle, map_shape_to_svg
31
+
32
+ >>> svg = map_shape_to_svg(square(4) - circle(1))
33
+ >>> svg.startswith("<svg")
34
+ True
35
+ """
36
+ geometries = [part.geometry for part in shape._parts]
37
+ if not geometries:
38
+ return '<svg xmlns="http://www.w3.org/2000/svg"/>'
39
+
40
+ return export_svg(trimesh.load_path(MultiPolygon(geometries)))
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING
5
+
6
+ from typeguard import typechecked
7
+
8
+ if TYPE_CHECKING:
9
+ from scadpy import Shape
10
+
11
+
12
+ @typechecked
13
+ def map_shape_to_svg_file(shape: Shape, path: str | Path) -> int:
14
+ """Save a shape as an SVG file.
15
+
16
+ Parameters
17
+ ----------
18
+ shape : Shape
19
+ The shape to export.
20
+ path : str or Path
21
+ Destination file path.
22
+
23
+ Returns
24
+ -------
25
+ int
26
+ Number of characters written.
27
+
28
+ Examples
29
+ --------
30
+ >>> from scadpy import square, circle, map_shape_to_svg_file
31
+
32
+ >>> map_shape_to_svg_file( # doctest: +SKIP
33
+ ... shape=square(4) - circle(1), path="output.svg"
34
+ ... )
35
+ """
36
+ from scadpy import map_shape_to_svg
37
+
38
+ return Path(path).write_text(map_shape_to_svg(shape), encoding="utf-8")
@@ -0,0 +1,9 @@
1
+ __all__ = [
2
+ "get_shape_bounds",
3
+ "get_shape_part_bounds",
4
+ "is_shape_empty",
5
+ ]
6
+
7
+ from .get_shape_bounds import get_shape_bounds
8
+ from .get_shape_part_bounds import get_shape_part_bounds
9
+ from .is_shape_empty import is_shape_empty
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import numpy as np
6
+ from numpy.typing import NDArray
7
+ from typeguard import typechecked
8
+
9
+ if TYPE_CHECKING:
10
+ from scadpy import Shape
11
+
12
+
13
+ @typechecked
14
+ def get_shape_bounds(shape: Shape) -> NDArray[np.float64]:
15
+ """
16
+ Return the axis-aligned bounding box of the shape.
17
+
18
+ Parameters
19
+ ----------
20
+ shape : Shape
21
+ The shape to compute bounds for.
22
+
23
+ Returns
24
+ -------
25
+ NDArray[np.float64]
26
+ 1D array ``[min_x, min_y, max_x, max_y]``.
27
+ Returns zeros if the shape is empty.
28
+
29
+ Examples
30
+ --------
31
+ >>> from shapely.geometry import Polygon
32
+ >>> from scadpy import Shape, get_shape_bounds
33
+
34
+ >>> polygon = Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])
35
+ >>> get_shape_bounds(Shape.from_geometry(polygon))
36
+ array([0., 0., 2., 2.])
37
+ """
38
+ from scadpy import get_component_bounds
39
+
40
+ return get_component_bounds(shape.vertex_coordinates)
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import numpy as np
6
+ from numpy.typing import NDArray
7
+ from shapely.geometry.polygon import Polygon
8
+ from typeguard import typechecked
9
+
10
+ if TYPE_CHECKING:
11
+ from scadpy.core.part import Part
12
+
13
+
14
+ @typechecked
15
+ def get_shape_part_bounds(part: Part[Polygon]) -> NDArray[np.float64]:
16
+ """Return the 2D bounding box of a shape part as [minx, miny, maxx, maxy].
17
+
18
+ Parameters
19
+ ----------
20
+ part : Part[Polygon]
21
+ The shape part to compute the bounding box of.
22
+
23
+ Returns
24
+ -------
25
+ NDArray[np.float64]
26
+ Array of shape (4,) containing [minx, miny, maxx, maxy].
27
+
28
+ Examples
29
+ --------
30
+ >>> from scadpy import square, get_shape_part_bounds
31
+ >>> bounds = get_shape_part_bounds(part=square(2)._parts[0])
32
+ >>> bounds.shape
33
+ (4,)
34
+ """
35
+ from scadpy import get_component_bounds, get_shape_part_vertex_coordinates
36
+
37
+ return get_component_bounds(get_shape_part_vertex_coordinates(part))