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,5 @@
1
+ __all__ = ["shapely_base_geometry_to_shapely_polygons"]
2
+
3
+ from .shapely_base_geometry_to_shapely_polygons import (
4
+ shapely_base_geometry_to_shapely_polygons,
5
+ )
@@ -0,0 +1,25 @@
1
+ from shapely.geometry import MultiPolygon, Polygon
2
+ from shapely.geometry.base import BaseGeometry
3
+ from shapely.geometry.polygon import orient
4
+ from typeguard import typechecked
5
+
6
+
7
+ @typechecked
8
+ def shapely_base_geometry_to_shapely_polygons(
9
+ base_geometry: BaseGeometry,
10
+ ) -> list[Polygon]:
11
+ if base_geometry.is_empty:
12
+ return []
13
+
14
+ if isinstance(base_geometry, Polygon):
15
+ # normalize orientation: exterior CCW, interiors CW (shapely convention)
16
+ return [orient(base_geometry, sign=1.0)]
17
+
18
+ if isinstance(base_geometry, MultiPolygon):
19
+ return [
20
+ polygon
21
+ for geom in base_geometry.geoms
22
+ for polygon in shapely_base_geometry_to_shapely_polygons(geom)
23
+ ]
24
+
25
+ return []
@@ -0,0 +1,5 @@
1
+ __all__ = ["shapely_base_geometry_to_shapely_polygons"]
2
+
3
+ from .shapely_base_geometry_to_shapely_polygons import (
4
+ shapely_base_geometry_to_shapely_polygons,
5
+ )
@@ -0,0 +1,55 @@
1
+ from shapely.geometry import MultiPolygon, Polygon
2
+ from shapely.geometry.base import BaseGeometry
3
+ from typeguard import typechecked
4
+
5
+
6
+ @typechecked
7
+ def shapely_base_geometry_to_shapely_polygons(
8
+ base_geometry: BaseGeometry,
9
+ ) -> list[Polygon]:
10
+ """Recursively extract all polygons from a Shapely geometry.
11
+
12
+ Handles :class:`~shapely.geometry.Polygon` and
13
+ :class:`~shapely.geometry.MultiPolygon`; returns an empty list for empty
14
+ or unsupported geometry types.
15
+
16
+ Parameters
17
+ ----------
18
+ base_geometry : BaseGeometry
19
+ Any Shapely geometry.
20
+
21
+ Returns
22
+ -------
23
+ list[Polygon]
24
+ Flat list of all non-empty polygons found in *base_geometry*.
25
+
26
+ Examples
27
+ --------
28
+ >>> from shapely.geometry import MultiPolygon, Polygon
29
+ >>> from scadpy.d2.shape.utils import (
30
+ ... shapely_base_geometry_to_shapely_polygons,
31
+ ... )
32
+
33
+ >>> polygons = shapely_base_geometry_to_shapely_polygons(
34
+ ... MultiPolygon([
35
+ ... Polygon([(0, 0), (1, 0), (1, 1)]),
36
+ ... Polygon([(2, 0), (3, 0), (3, 1)]),
37
+ ... ])
38
+ ... )
39
+ >>> len(polygons)
40
+ 2
41
+ """
42
+ if base_geometry.is_empty:
43
+ return []
44
+
45
+ if isinstance(base_geometry, Polygon):
46
+ return [base_geometry]
47
+
48
+ if isinstance(base_geometry, MultiPolygon):
49
+ return [
50
+ polygon
51
+ for geom in base_geometry.geoms
52
+ for polygon in shapely_base_geometry_to_shapely_polygons(geom)
53
+ ]
54
+
55
+ return []
@@ -0,0 +1,3 @@
1
+ __all__ = ["resolve_vector_2d"]
2
+
3
+ from .resolve_vector_2d import resolve_vector_2d
@@ -0,0 +1,50 @@
1
+ from collections.abc import Iterable
2
+
3
+ import numpy as np
4
+ from numpy.typing import NDArray
5
+ from typeguard import typechecked
6
+
7
+
8
+ @typechecked
9
+ def resolve_vector_2d(
10
+ values: float | Iterable[float],
11
+ default_value: float,
12
+ ) -> NDArray[np.float64]:
13
+ """
14
+ Resolves input into a 2D vector (NumPy array of length 2).
15
+
16
+ Parameters
17
+ ----------
18
+ values : float or Iterable[float]
19
+ The input values to resolve into a 2D vector. It can be:
20
+ - A single numeric value, repeated to fill the vector.
21
+ - An iterable of numeric values, extended or truncated to length 2.
22
+ default_value : float
23
+ The value used to pad the vector if `values` has fewer than 2 elements.
24
+
25
+ Returns
26
+ -------
27
+ NDArray[np.float64]
28
+ A NumPy array of shape (2,) containing the resolved 2D vector.
29
+
30
+ Notes
31
+ -----
32
+ - If `values` is a single number, the result will be `[values, values]`.
33
+ - If `values` has one element, the result will be `[values[0], default_value]`.
34
+ - If `values` has more than 2 elements, only the first two will be kept.
35
+
36
+ Examples
37
+ --------
38
+ >>> from scadpy import resolve_vector_2d
39
+ >>> resolve_vector_2d(5, 0)
40
+ array([5., 5.])
41
+
42
+ >>> resolve_vector_2d([1], 0)
43
+ array([1., 0.])
44
+
45
+ >>> resolve_vector_2d([3, 4, 5], 0)
46
+ array([3., 4.])
47
+ """
48
+ from scadpy.utils.resolve_vector import resolve_vector
49
+
50
+ return resolve_vector(values, default_value, 2)
scadpy/d3/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ from .solid import * # noqa: F403
2
+ from .utils import * # noqa: F403
@@ -0,0 +1,8 @@
1
+ from .combinations import * # noqa: F403
2
+ from .exporters import * # noqa: F403
3
+ from .features import * # noqa: F403
4
+ from .topologies import * # noqa: F403
5
+ from .importers import * # noqa: F403
6
+ from .primitives import * # noqa: F403
7
+ from .transformations import * # noqa: F403
8
+ from .types import * # noqa: F403
@@ -0,0 +1,21 @@
1
+ __all__ = [
2
+ "are_solid_parts_intersecting",
3
+ "concat_solid",
4
+ "exclude_solid",
5
+ "intersect_solid",
6
+ "intersect_solid_parts",
7
+ "subtract_solid",
8
+ "subtract_solid_parts",
9
+ "unify_solid",
10
+ "unify_solid_parts",
11
+ ]
12
+
13
+ from .are_solid_parts_intersecting import are_solid_parts_intersecting
14
+ from .concat_solid import concat_solid
15
+ from .exclude_solid import exclude_solid
16
+ from .intersect_solid import intersect_solid
17
+ from .intersect_solid_parts import intersect_solid_parts
18
+ from .subtract_solid import subtract_solid
19
+ from .subtract_solid_parts import subtract_solid_parts
20
+ from .unify_solid import unify_solid
21
+ from .unify_solid_parts import unify_solid_parts
@@ -0,0 +1,51 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from trimesh import Trimesh
6
+ from trimesh.boolean import boolean_manifold # pyright: ignore[reportUnknownVariableType]
7
+ from typeguard import typechecked
8
+
9
+ if TYPE_CHECKING:
10
+ from scadpy.core.part import Part
11
+
12
+
13
+ @typechecked
14
+ def are_solid_parts_intersecting(part1: Part[Trimesh], part2: Part[Trimesh]) -> bool:
15
+ """Return whether two solid parts intersect geometrically.
16
+
17
+ Parameters
18
+ ----------
19
+ part1 : Part[Trimesh]
20
+ The first solid part.
21
+ part2 : Part[Trimesh]
22
+ The second solid part.
23
+
24
+ Returns
25
+ -------
26
+ bool
27
+ True if the two parts intersect (shared volume > 0), False otherwise.
28
+
29
+ Examples
30
+ --------
31
+ >>> from scadpy import cuboid, are_solid_parts_intersecting
32
+
33
+ >>> are_solid_parts_intersecting(
34
+ ... part1=cuboid(2)._parts[0],
35
+ ... part2=cuboid(2)._parts[0],
36
+ ... )
37
+ True
38
+ >>> are_solid_parts_intersecting(
39
+ ... part1=cuboid(1)._parts[0],
40
+ ... part2=cuboid(1).translate(10)._parts[0],
41
+ ... )
42
+ False
43
+ """
44
+ return bool(
45
+ boolean_manifold( # pyright: ignore[reportAny]
46
+ [part1.geometry, part2.geometry],
47
+ operation="intersection",
48
+ check_volume=False,
49
+ ).volume
50
+ != 0
51
+ )
@@ -0,0 +1,48 @@
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 Solid
10
+
11
+
12
+ @typechecked
13
+ def concat_solid(solids: Sequence[Solid]) -> Solid:
14
+ """Concatenate a sequence of solids into a single solid without any boolean operation.
15
+
16
+ All parts from all input solids are merged into a single solid. Parts that
17
+ overlap are not merged geometrically — use :func:`unify_solid` for that.
18
+
19
+ Parameters
20
+ ----------
21
+ solids : Sequence[Solid]
22
+ The solids to concatenate.
23
+
24
+ Returns
25
+ -------
26
+ Solid
27
+ A new solid containing all parts from all input solids.
28
+
29
+ Examples
30
+ --------
31
+ >>> from scadpy import cuboid, sphere, concat_solid
32
+
33
+ >>> concat_solid( # doctest: +SKIP
34
+ ... solids=[cuboid(4), sphere(radius=2).translate([3, 2, 0])]
35
+ ... )
36
+
37
+ .. render-example::
38
+ :name: concat_solid
39
+ :example: concat_solid(solids=[cuboid(4), sphere(radius=2).translate([3, 2, 0])])
40
+ """
41
+ from scadpy import Solid
42
+ from scadpy.core.assembly import concat_assemblies
43
+
44
+ return concat_assemblies(
45
+ assemblies=solids,
46
+ get_assembly_parts=lambda assembly: assembly._parts,
47
+ concat_parts=Solid.from_parts,
48
+ )
@@ -0,0 +1,71 @@
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 Solid
10
+
11
+
12
+ @typechecked
13
+ def exclude_solid(solids: Sequence[Solid]) -> Solid:
14
+ """Compute the symmetric difference (XOR) of a sequence of solids.
15
+
16
+ Keeps only the regions that belong to exactly one of the input solids.
17
+ Regions shared by two or more solids are removed.
18
+
19
+ Parameters
20
+ ----------
21
+ solids : Sequence[Solid]
22
+ The solids to compute the symmetric difference of.
23
+
24
+ Returns
25
+ -------
26
+ Solid
27
+ A new solid containing only the non-overlapping regions of the input solids.
28
+
29
+ Examples
30
+ --------
31
+ >>> from scadpy import cuboid, exclude_solid, x
32
+
33
+ >>> exclude_solid( # doctest: +SKIP
34
+ ... solids=[cuboid(4), cuboid(4).translate(x(2))]
35
+ ... )
36
+
37
+ .. render-example::
38
+ :name: exclude_solid
39
+ :example: exclude_solid(solids=[cuboid(4), cuboid(4).translate(x(2))])
40
+ :ghost: concat_solid(solids=[cuboid(4), cuboid(4).translate(x(2))])
41
+ """
42
+ from scadpy import (
43
+ Solid,
44
+ are_solid_parts_intersecting,
45
+ get_solid_part_bounds,
46
+ intersect_solid_parts,
47
+ subtract_solid_parts,
48
+ unify_solid_parts,
49
+ )
50
+ from scadpy.core.assembly import exclude_assemblies
51
+
52
+ return exclude_assemblies(
53
+ assemblies=solids,
54
+ get_assembly_parts=lambda assembly: assembly._parts,
55
+ get_part_bounds=get_solid_part_bounds,
56
+ are_parts_intersecting=are_solid_parts_intersecting,
57
+ subtract_parts=lambda part_base, part_cutter: subtract_solid_parts(
58
+ to_be_subtracted=part_base,
59
+ to_subtract=part_cutter,
60
+ make_assembly_from_parts=Solid.from_parts,
61
+ ),
62
+ intersect_parts=lambda parts: intersect_solid_parts(
63
+ parts=parts,
64
+ make_assembly_from_parts=Solid.from_parts,
65
+ ),
66
+ unify_parts=lambda parts: unify_solid_parts(
67
+ parts=parts,
68
+ make_assembly_from_parts=Solid.from_parts,
69
+ ),
70
+ concat_parts=Solid.from_parts,
71
+ )
@@ -0,0 +1,64 @@
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 Solid
10
+
11
+
12
+ @typechecked
13
+ def intersect_solid(solids: Sequence[Solid]) -> Solid:
14
+ """Compute the intersection of a sequence of solids.
15
+
16
+ Only the regions shared by all input solids are kept.
17
+
18
+ Parameters
19
+ ----------
20
+ solids : Sequence[Solid]
21
+ The solids to intersect.
22
+
23
+ Returns
24
+ -------
25
+ Solid
26
+ A new solid containing only the regions present in all input solids.
27
+
28
+ Examples
29
+ --------
30
+ >>> from scadpy import cuboid, sphere, intersect_solid
31
+
32
+ >>> intersect_solid( # doctest: +SKIP
33
+ ... solids=[cuboid(4), sphere(radius=2).translate(1)]
34
+ ... )
35
+
36
+ .. render-example::
37
+ :name: intersect_solid
38
+ :example: intersect_solid(solids=[cuboid(4), sphere(radius=2).translate(1)])
39
+ :ghost: cuboid(4) + sphere(radius=2).translate(1)
40
+ """
41
+ from scadpy import (
42
+ Solid,
43
+ are_solid_parts_intersecting,
44
+ get_solid_part_bounds,
45
+ intersect_solid_parts,
46
+ unify_solid_parts,
47
+ )
48
+ from scadpy.core.assembly import intersect_assemblies
49
+
50
+ return intersect_assemblies(
51
+ assemblies=solids,
52
+ get_assembly_parts=lambda assembly: assembly._parts,
53
+ get_part_bounds=get_solid_part_bounds,
54
+ are_parts_intersecting=are_solid_parts_intersecting,
55
+ intersect_parts=lambda parts: intersect_solid_parts(
56
+ parts=parts,
57
+ make_assembly_from_parts=Solid.from_parts,
58
+ ),
59
+ unify_parts=lambda parts: unify_solid_parts(
60
+ parts=parts,
61
+ make_assembly_from_parts=Solid.from_parts,
62
+ ),
63
+ concat_parts=Solid.from_parts,
64
+ )
@@ -0,0 +1,73 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Sequence
4
+ from typing import TYPE_CHECKING
5
+
6
+ from trimesh import Trimesh
7
+ from trimesh.boolean import boolean_manifold # pyright: ignore[reportUnknownVariableType]
8
+ from typeguard import typechecked
9
+
10
+ if TYPE_CHECKING:
11
+ from scadpy.core.part import Part
12
+ from scadpy.d3.solid import Solid
13
+
14
+
15
+ @typechecked
16
+ def intersect_solid_parts(
17
+ parts: Sequence[Part[Trimesh]],
18
+ make_assembly_from_parts: Callable[[Sequence[Part[Trimesh]]], Solid],
19
+ ) -> Solid:
20
+ """Intersect a sequence of solid parts and return the resulting solid.
21
+
22
+ Shortcut for :func:`intersect_parts`.
23
+ See :func:`intersect_parts` for full documentation.
24
+
25
+ Parameters
26
+ ----------
27
+ parts : Sequence[Part[Trimesh]]
28
+ The solid parts to intersect.
29
+ make_assembly_from_parts : Callable[[Sequence[Part[Trimesh]]], Solid]
30
+ Factory function to build the resulting Solid from a sequence of parts.
31
+
32
+ Returns
33
+ -------
34
+ Solid
35
+ A new solid containing the geometric intersection of the input parts.
36
+
37
+ Examples
38
+ --------
39
+ >>> from scadpy import cuboid, sphere, intersect_solid_parts, Solid
40
+
41
+ >>> intersect_solid_parts( # doctest: +SKIP
42
+ ... parts=(
43
+ ... list(cuboid(4)._parts)
44
+ ... + list(sphere(radius=2).translate([2, 2, 2])._parts)
45
+ ... ),
46
+ ... make_assembly_from_parts=Solid.from_parts,
47
+ ... )
48
+
49
+ .. render-example::
50
+ :name: intersect_solid_parts_example
51
+ :example: intersect_solid_parts(parts=list(cuboid(4)._parts) + list(sphere(radius=2).translate([2, 2, 2])._parts), make_assembly_from_parts=Solid.from_parts)
52
+ :ghost: concat_solid(solids=[cuboid(4), sphere(radius=2).translate([2, 2, 2])])
53
+ """
54
+ from scadpy import (
55
+ Part,
56
+ are_solid_parts_intersecting,
57
+ get_solid_part_bounds,
58
+ )
59
+ from scadpy.core.part import intersect_parts
60
+
61
+ return intersect_parts(
62
+ parts=parts,
63
+ get_part_color=lambda p: p.color,
64
+ get_part_magnitude=lambda p: p.geometry.volume, # pyright: ignore[reportAny]
65
+ get_part_bounds=get_solid_part_bounds,
66
+ are_parts_intersecting=are_solid_parts_intersecting,
67
+ get_part_geometry=lambda p: p.geometry,
68
+ intersect_geometries=lambda g: boolean_manifold(
69
+ g, operation="intersection", check_volume=False
70
+ ).split(), # pyright: ignore[reportUnknownMemberType]
71
+ make_part_from_geometry=Part[Trimesh].from_geometry,
72
+ make_assembly_from_parts=make_assembly_from_parts,
73
+ )
@@ -0,0 +1,72 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from typeguard import typechecked
6
+
7
+ if TYPE_CHECKING:
8
+ from scadpy import Solid
9
+
10
+
11
+ @typechecked
12
+ def subtract_solid(to_be_subtracted: Solid, to_subtract: Solid) -> Solid:
13
+ """Subtract one solid from another using boolean difference.
14
+
15
+ The geometry of ``to_subtract`` is removed from ``to_be_subtracted``.
16
+
17
+ Parameters
18
+ ----------
19
+ to_be_subtracted : Solid
20
+ The solid to subtract from.
21
+ to_subtract : Solid
22
+ The solid to subtract.
23
+
24
+ Returns
25
+ -------
26
+ Solid
27
+ A new solid with the geometry of ``to_subtract`` removed from ``to_be_subtracted``.
28
+
29
+ Examples
30
+ --------
31
+ >>> from scadpy import cuboid, sphere, subtract_solid
32
+
33
+ >>> subtract_solid( # doctest: +SKIP
34
+ ... to_be_subtracted=cuboid(4), to_subtract=sphere(radius=2)
35
+ ... )
36
+
37
+ .. render-example::
38
+ :name: subtract_solid
39
+ :example: subtract_solid(to_be_subtracted=cuboid(4), to_subtract=sphere(radius=2))
40
+ :ghost: cuboid(4)
41
+ """
42
+ from scadpy import (
43
+ Solid,
44
+ are_solid_parts_intersecting,
45
+ get_solid_part_bounds,
46
+ intersect_solid_parts,
47
+ subtract_solid_parts,
48
+ unify_solid_parts,
49
+ )
50
+ from scadpy.core.assembly import subtract_assemblies
51
+
52
+ return subtract_assemblies(
53
+ to_be_subtracted=to_be_subtracted,
54
+ to_subtract=to_subtract,
55
+ get_assembly_parts=lambda assembly: assembly._parts,
56
+ get_part_bounds=get_solid_part_bounds,
57
+ are_parts_intersecting=are_solid_parts_intersecting,
58
+ subtract_parts=lambda part_base, part_cutter: subtract_solid_parts(
59
+ to_be_subtracted=part_base,
60
+ to_subtract=part_cutter,
61
+ make_assembly_from_parts=Solid.from_parts,
62
+ ),
63
+ intersect_parts=lambda parts: intersect_solid_parts(
64
+ parts=parts,
65
+ make_assembly_from_parts=Solid.from_parts,
66
+ ),
67
+ unify_parts=lambda parts: unify_solid_parts(
68
+ parts=parts,
69
+ make_assembly_from_parts=Solid.from_parts,
70
+ ),
71
+ concat_parts=Solid.from_parts,
72
+ )
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Sequence
4
+ from typing import TYPE_CHECKING
5
+
6
+ from trimesh import Trimesh
7
+ from trimesh.boolean import boolean_manifold # pyright: ignore[reportUnknownVariableType]
8
+ from typeguard import typechecked
9
+
10
+ if TYPE_CHECKING:
11
+ from scadpy.core.part import Part
12
+ from scadpy.d3.solid import Solid
13
+
14
+
15
+ @typechecked
16
+ def subtract_solid_parts(
17
+ to_be_subtracted: Part[Trimesh],
18
+ to_subtract: Part[Trimesh],
19
+ make_assembly_from_parts: Callable[[Sequence[Part[Trimesh]]], Solid],
20
+ ) -> Solid:
21
+ """Subtract one solid part from another and return the resulting solid.
22
+
23
+ Shortcut for :func:`subtract_parts`.
24
+ See :func:`subtract_parts` for full documentation.
25
+
26
+ Parameters
27
+ ----------
28
+ to_be_subtracted : Part[Trimesh]
29
+ The part to subtract from.
30
+ to_subtract : Part[Trimesh]
31
+ The part to subtract.
32
+ make_assembly_from_parts : Callable[[list[Part[Trimesh]]], Solid]
33
+ Factory function to build the resulting Solid from a sequence of parts.
34
+
35
+ Returns
36
+ -------
37
+ Solid
38
+ A new solid with the geometry of ``to_subtract`` removed from ``to_be_subtracted``.
39
+
40
+ Examples
41
+ --------
42
+ >>> from scadpy import cuboid, sphere, subtract_solid_parts, Solid
43
+
44
+ >>> subtract_solid_parts( # doctest: +SKIP
45
+ ... to_be_subtracted=cuboid(4)._parts[0],
46
+ ... to_subtract=sphere(radius=2)._parts[0],
47
+ ... make_assembly_from_parts=Solid.from_parts,
48
+ ... )
49
+
50
+ .. render-example::
51
+ :name: subtract_solid_parts_example
52
+ :example: subtract_solid_parts(to_be_subtracted=cuboid(4)._parts[0], to_subtract=sphere(radius=2)._parts[0], make_assembly_from_parts=Solid.from_parts)
53
+ :ghost: concat_solid(solids=[cuboid(4), sphere(radius=2)])
54
+ """
55
+ from scadpy import Part
56
+ from scadpy.core.part import subtract_parts
57
+
58
+ return subtract_parts(
59
+ to_be_subtracted=to_be_subtracted,
60
+ to_subtract=to_subtract,
61
+ get_part_color=lambda p: p.color,
62
+ get_part_geometry=lambda p: p.geometry,
63
+ subtract_geometries=lambda g1, g2: boolean_manifold(
64
+ [g1, g2], operation="difference", check_volume=False
65
+ ).split(), # pyright: ignore[reportUnknownMemberType]
66
+ make_part_from_geometry=Part[Trimesh].from_geometry,
67
+ make_assembly_from_parts=make_assembly_from_parts,
68
+ )
@@ -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 Solid
10
+
11
+
12
+ @typechecked
13
+ def unify_solid(solids: Sequence[Solid]) -> Solid:
14
+ """Unite a sequence of solids into a single solid using boolean union.
15
+
16
+ All overlapping parts across the input solids are merged geometrically.
17
+ Use :func:`concat_solid` if you want to combine solids without merging overlaps.
18
+
19
+ Parameters
20
+ ----------
21
+ solids : Sequence[Solid]
22
+ The solids to unite.
23
+
24
+ Returns
25
+ -------
26
+ Solid
27
+ A new solid containing the geometric union of all input solids.
28
+
29
+ Examples
30
+ --------
31
+ >>> from scadpy import cuboid, sphere, unify_solid, x
32
+
33
+ >>> unify_solid( # doctest: +SKIP
34
+ ... solids=[cuboid(4), sphere(radius=2).translate(x(2))]
35
+ ... )
36
+
37
+ .. render-example::
38
+ :name: unify_solid
39
+ :example: unify_solid(solids=[cuboid(4), sphere(radius=2).translate(x(2))])
40
+ """
41
+ from scadpy import Solid, unify_solid_parts
42
+ from scadpy.core.assembly import unify_assemblies
43
+
44
+ return unify_assemblies(
45
+ assemblies=solids,
46
+ get_assembly_parts=lambda assembly: assembly._parts,
47
+ unify_parts=lambda parts: unify_solid_parts(
48
+ parts=parts,
49
+ make_assembly_from_parts=Solid.from_parts,
50
+ ),
51
+ )