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
scadpy/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ from .color import * # noqa: F403
2
+ from .core import * # noqa: F403
3
+ from .d2 import * # noqa: F403
4
+ from .d3 import * # noqa: F403
5
+ from .utils import * # noqa: F403
@@ -0,0 +1,3 @@
1
+ from .constants import *
2
+ from .type import *
3
+ from .utils import *
@@ -0,0 +1,3 @@
1
+ from scadpy.color.constants.DEFAULT_OPACITY import DEFAULT_OPACITY
2
+
3
+ BEIGE = [0.96, 0.96, 0.86, DEFAULT_OPACITY]
@@ -0,0 +1,3 @@
1
+ from scadpy.color.constants.DEFAULT_OPACITY import DEFAULT_OPACITY
2
+
3
+ BLACK = [0.05, 0.05, 0.05, DEFAULT_OPACITY]
@@ -0,0 +1,3 @@
1
+ from scadpy.color.constants.DEFAULT_OPACITY import DEFAULT_OPACITY
2
+
3
+ BLUE = [0.1, 0.3, 0.9, DEFAULT_OPACITY]
@@ -0,0 +1,3 @@
1
+ from scadpy.color.constants.DEFAULT_OPACITY import DEFAULT_OPACITY
2
+
3
+ BROWN = [0.65, 0.32, 0.17, DEFAULT_OPACITY]
@@ -0,0 +1,3 @@
1
+ from scadpy.color.constants.DEFAULT_OPACITY import DEFAULT_OPACITY
2
+
3
+ DARK_GRAY = [0.25, 0.25, 0.25, DEFAULT_OPACITY]
@@ -0,0 +1,3 @@
1
+ from scadpy.color.constants.GRAY import GRAY
2
+
3
+ DEFAULT_COLOR = GRAY
@@ -0,0 +1 @@
1
+ DEFAULT_OPACITY = 1
@@ -0,0 +1,3 @@
1
+ from scadpy.color.constants.DEFAULT_OPACITY import DEFAULT_OPACITY
2
+
3
+ GRAY = [0.5, 0.5, 0.5, DEFAULT_OPACITY]
@@ -0,0 +1,3 @@
1
+ from scadpy.color.constants.DEFAULT_OPACITY import DEFAULT_OPACITY
2
+
3
+ GREEN = [0.1, 0.9, 0.1, DEFAULT_OPACITY]
@@ -0,0 +1,3 @@
1
+ from scadpy.color.constants.DEFAULT_OPACITY import DEFAULT_OPACITY
2
+
3
+ ORANGE = [1.0, 0.55, 0.0, DEFAULT_OPACITY]
@@ -0,0 +1,3 @@
1
+ from scadpy.color.constants.DEFAULT_OPACITY import DEFAULT_OPACITY
2
+
3
+ RED = [0.9, 0.1, 0.1, DEFAULT_OPACITY]
@@ -0,0 +1,3 @@
1
+ from scadpy.color.constants.DEFAULT_OPACITY import DEFAULT_OPACITY
2
+
3
+ WHITE = [0.95, 0.95, 0.95, DEFAULT_OPACITY]
@@ -0,0 +1,3 @@
1
+ from scadpy.color.constants.DEFAULT_OPACITY import DEFAULT_OPACITY
2
+
3
+ YELLOW = [0.95, 0.95, 0.1, DEFAULT_OPACITY]
@@ -0,0 +1,29 @@
1
+ __all__ = [
2
+ "BEIGE",
3
+ "BLACK",
4
+ "BLUE",
5
+ "BROWN",
6
+ "DEFAULT_COLOR",
7
+ "DEFAULT_OPACITY",
8
+ "DARK_GRAY",
9
+ "GRAY",
10
+ "GREEN",
11
+ "ORANGE",
12
+ "RED",
13
+ "WHITE",
14
+ "YELLOW",
15
+ ]
16
+
17
+ from .BEIGE import BEIGE
18
+ from .BLACK import BLACK
19
+ from .BLUE import BLUE
20
+ from .BROWN import BROWN
21
+ from .DEFAULT_COLOR import DEFAULT_COLOR
22
+ from .DARK_GRAY import DARK_GRAY
23
+ from .DEFAULT_OPACITY import DEFAULT_OPACITY
24
+ from .GRAY import GRAY
25
+ from .GREEN import GREEN
26
+ from .ORANGE import ORANGE
27
+ from .RED import RED
28
+ from .WHITE import WHITE
29
+ from .YELLOW import YELLOW
@@ -0,0 +1,3 @@
1
+ __all__ = ["Color"]
2
+
3
+ from .color import Color
@@ -0,0 +1,3 @@
1
+ from collections.abc import Sequence
2
+
3
+ Color = Sequence[float]
@@ -0,0 +1,3 @@
1
+ __all__ = ["get_random_color"]
2
+
3
+ from .get_random_color import get_random_color
@@ -0,0 +1,36 @@
1
+ from __future__ import annotations
2
+
3
+ import random
4
+ from typing import TYPE_CHECKING, cast
5
+
6
+ from typeguard import typechecked
7
+
8
+ if TYPE_CHECKING:
9
+ from scadpy.color import Color
10
+
11
+
12
+ @typechecked
13
+ def get_random_color() -> Color:
14
+ from scadpy import color
15
+
16
+ color_names: list[str] = getattr(color.constants, "__all__", [])
17
+ color_names = [
18
+ c for c in color_names if c != "DEFAULT_OPACITY" and c != "DEFAULT_COLOR"
19
+ ]
20
+
21
+ if not color_names:
22
+ raise ValueError("No color constant found.")
23
+
24
+ name = random.choice(color_names)
25
+ color_value = cast(color.Color, getattr(color.constants, name))
26
+
27
+ if not isinstance(color_value, (list, tuple)):
28
+ raise TypeError(f"{name} is not a list or tuple.")
29
+
30
+ if len(color_value) != 4:
31
+ raise TypeError(f"{name} must be RGBA (4 values), got {len(color_value)}.")
32
+
33
+ if not all(0.0 <= c <= 1.0 for c in color_value):
34
+ raise ValueError(f"{name} has values outside [0.0, 1.0] range.")
35
+
36
+ return color_value
@@ -0,0 +1,3 @@
1
+ from .assembly import * # noqa: F403
2
+ from .component import * # noqa: F403
3
+ from .part import * # noqa: F403
@@ -0,0 +1,5 @@
1
+ from .combinations import * # noqa: F403
2
+ from .topologies import * # noqa: F403
3
+ from .transformations import * # noqa: F403
4
+ from .types import * # noqa: F403
5
+ from .utils import * # noqa: F403
@@ -0,0 +1,13 @@
1
+ __all__ = [
2
+ "concat_assemblies",
3
+ "exclude_assemblies",
4
+ "intersect_assemblies",
5
+ "subtract_assemblies",
6
+ "unify_assemblies",
7
+ ]
8
+
9
+ from .concat_assemblies import concat_assemblies
10
+ from .exclude_assemblies import exclude_assemblies
11
+ from .intersect_assemblies import intersect_assemblies
12
+ from .subtract_assemblies import subtract_assemblies
13
+ from .unify_assemblies import unify_assemblies
@@ -0,0 +1,50 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Iterable, Sequence
4
+
5
+ from typeguard import typechecked
6
+
7
+
8
+ @typechecked
9
+ def concat_assemblies[A, P](
10
+ assemblies: Iterable[A],
11
+ get_assembly_parts: Callable[[A], Iterable[P]],
12
+ concat_parts: Callable[[Sequence[P]], A],
13
+ ) -> A:
14
+ """
15
+ Combine multiple assemblies into a single assembly by concatenating all their parts.
16
+
17
+ This function uses dependency injection to remain type-agnostic, allowing it
18
+ to work with any assembly/part domain model by providing appropriate accessor
19
+ and concatenation functions.
20
+
21
+ Parameters
22
+ ----------
23
+ assemblies : Iterable[A]
24
+ Iterable of assembly objects to be combined.
25
+ get_assembly_parts : Callable[[A], Iterable[P]]
26
+ Function that extracts parts from an assembly.
27
+ concat_parts : Callable[[Sequence[P]], A]
28
+ Function that combines a sequence of parts into a new assembly.
29
+
30
+ Returns
31
+ -------
32
+ A
33
+ The assembly object created by combining all parts from all input assemblies.
34
+
35
+ Examples
36
+ --------
37
+ >>> from scadpy import concat_assemblies
38
+ >>> assemblies = [
39
+ ... ['a', 'b'],
40
+ ... ['c'],
41
+ ... []
42
+ ... ]
43
+ >>> concat_assemblies(
44
+ ... assemblies,
45
+ ... get_assembly_parts=lambda a: a,
46
+ ... concat_parts=lambda parts: ''.join(parts)
47
+ ... )
48
+ 'abc'
49
+ """
50
+ return concat_parts([p for a in assemblies for p in get_assembly_parts(a)])
@@ -0,0 +1,135 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Iterable, Sequence
4
+
5
+ import numpy as np
6
+ from numpy.typing import NDArray
7
+ from typeguard import typechecked
8
+
9
+
10
+ @typechecked
11
+ def exclude_assemblies[A, P](
12
+ assemblies: Sequence[A],
13
+ get_assembly_parts: Callable[[A], Iterable[P]],
14
+ get_part_bounds: Callable[[P], NDArray[np.float64]],
15
+ are_parts_intersecting: Callable[[P, P], bool],
16
+ subtract_parts: Callable[[P, P], A],
17
+ intersect_parts: Callable[[Sequence[P]], A],
18
+ unify_parts: Callable[[Sequence[P]], A],
19
+ concat_parts: Callable[[Sequence[P]], A],
20
+ ) -> A:
21
+ """
22
+ Compute the symmetric difference (exclusive-or) of multiple assemblies.
23
+
24
+ This function is fully generic and uses dependency injection for all domain-specific
25
+ operations, making it suitable for a wide range of applications (2D, 3D, CAD, etc.).
26
+ The result contains all parts that are present in exactly one assembly, i.e., the union minus all intersections.
27
+
28
+ Parameters
29
+ ----------
30
+ assemblies : Sequence[A]
31
+ Sequence of assembly objects to process.
32
+ get_assembly_parts : Callable[[A], Iterable[P]]
33
+ Function that extracts parts from an assembly.
34
+ get_part_bounds : Callable[[P], NDArray[np.float64]]
35
+ Function to extract the bounding box of a part.
36
+ are_parts_intersecting : Callable[[P, P], bool]
37
+ Function to determine if two parts intersect.
38
+ subtract_parts : Callable[[P, P], A]
39
+ Function to subtract one part from another, returning an assembly.
40
+ intersect_parts : Callable[[Sequence[P]], A]
41
+ Function to compute the intersection of a group of parts.
42
+ unify_parts : Callable[[Sequence[P]], A]
43
+ Function to unify (union) overlapping parts within an assembly.
44
+ concat_parts : Callable[[Sequence[P]], A]
45
+ Function to concatenate a sequence of parts into a new assembly.
46
+
47
+ Returns
48
+ -------
49
+ A
50
+ The assembly object containing the symmetric difference of all input assemblies.
51
+
52
+ Examples
53
+ --------
54
+ >>> from scadpy import exclude_assemblies
55
+
56
+ >>> assemblies = [
57
+ ... [{'bounds': [0, 0, 2, 2]}],
58
+ ... [{'bounds': [1, 1, 3, 3]}, {'bounds': [5, 5, 6, 6]}]
59
+ ... ]
60
+ ...
61
+ >>> def are_intersecting(p1, p2):
62
+ ... b1, b2 = p1['bounds'], p2['bounds']
63
+ ... return not (b1[2] <= b2[0] or b2[2] <= b1[0] or
64
+ ... b1[3] <= b2[1] or b2[3] <= b1[1])
65
+ ...
66
+ >>> def subtract_geometries(g1, g2):
67
+ ... if (g2[0] > g1[0] and g2[2] < g1[2]
68
+ ... and g2[1] > g1[1] and g2[3] < g1[3]):
69
+ ... return [
70
+ ... [g1[0], g1[1], g2[0], g1[3]], # left
71
+ ... [g2[2], g1[1], g1[2], g1[3]], # right
72
+ ... [g2[0], g1[1], g2[2], g2[1]], # bottom
73
+ ... [g2[0], g2[3], g2[2], g1[3]], # top
74
+ ... ]
75
+ ... return [g1]
76
+ ...
77
+ >>> def subtract_parts(p1, p2):
78
+ ... geometries = subtract_geometries(
79
+ ... p1['bounds'], p2['bounds']
80
+ ... )
81
+ ... return [{'bounds': g} for g in geometries]
82
+ ...
83
+ >>> result = exclude_assemblies(
84
+ ... assemblies,
85
+ ... get_assembly_parts=lambda a: a,
86
+ ... get_part_bounds=lambda p: p['bounds'],
87
+ ... are_parts_intersecting=are_intersecting,
88
+ ... subtract_parts=subtract_parts,
89
+ ... intersect_parts= lambda parts: parts,
90
+ ... unify_parts= lambda parts: parts,
91
+ ... concat_parts= lambda parts: parts
92
+ ... )
93
+ >>> result == [
94
+ ... {'bounds': [0, 0, 2, 2]},
95
+ ... {'bounds': [1, 1, 3, 3]},
96
+ ... {'bounds': [5, 5, 6, 6]}
97
+ ... ]
98
+ True
99
+ """
100
+ from scadpy.core.assembly import subtract_assemblies, unify_assemblies
101
+
102
+ if len(assemblies) == 0:
103
+ return concat_parts([])
104
+
105
+ result = assemblies[0]
106
+ for assembly in assemblies[1:]:
107
+ result = unify_assemblies(
108
+ assemblies=[
109
+ subtract_assemblies(
110
+ to_be_subtracted=result,
111
+ to_subtract=assembly,
112
+ get_assembly_parts=get_assembly_parts,
113
+ get_part_bounds=get_part_bounds,
114
+ are_parts_intersecting=are_parts_intersecting,
115
+ subtract_parts=subtract_parts,
116
+ intersect_parts=intersect_parts,
117
+ unify_parts=unify_parts,
118
+ concat_parts=concat_parts,
119
+ ),
120
+ subtract_assemblies(
121
+ to_be_subtracted=assembly,
122
+ to_subtract=result,
123
+ get_assembly_parts=get_assembly_parts,
124
+ get_part_bounds=get_part_bounds,
125
+ are_parts_intersecting=are_parts_intersecting,
126
+ subtract_parts=subtract_parts,
127
+ intersect_parts=intersect_parts,
128
+ unify_parts=unify_parts,
129
+ concat_parts=concat_parts,
130
+ ),
131
+ ],
132
+ get_assembly_parts=get_assembly_parts,
133
+ unify_parts=unify_parts,
134
+ )
135
+ return result
@@ -0,0 +1,128 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Iterable, Sequence
4
+
5
+ import numpy as np
6
+ from numpy.typing import NDArray
7
+ from typeguard import typechecked
8
+
9
+
10
+ @typechecked
11
+ def intersect_assemblies[A, P](
12
+ assemblies: Sequence[A],
13
+ get_assembly_parts: Callable[[A], Iterable[P]],
14
+ get_part_bounds: Callable[[P], NDArray[np.float64]],
15
+ are_parts_intersecting: Callable[[P, P], bool],
16
+ intersect_parts: Callable[[Sequence[P]], A],
17
+ unify_parts: Callable[[Sequence[P]], A],
18
+ concat_parts: Callable[[Sequence[P]], A],
19
+ ) -> A:
20
+ """
21
+ Compute the intersection of multiple assemblies, keeping only intersections that involve at least one part from each assembly.
22
+
23
+ This function is fully generic and uses dependency injection for all domain-specific
24
+ operations, making it suitable for a wide range of applications (2D, 3D, CAD, etc.).
25
+ Only groups of parts that include at least one part from each input assembly are intersected and included in the result.
26
+
27
+ Parameters
28
+ ----------
29
+ assemblies : Sequence[A]
30
+ Sequence of assembly objects to intersect.
31
+ get_assembly_parts : Callable[[A], Iterable[P]]
32
+ Function that extracts parts from an assembly.
33
+ get_part_bounds : Callable[[P], NDArray[np.float64]]
34
+ Function to extract the bounding box of a part.
35
+ are_parts_intersecting : Callable[[P, P], bool]
36
+ Function to determine if two parts intersect.
37
+ intersect_parts : Callable[[Sequence[P]], A]
38
+ Function to compute the intersection of a group of parts.
39
+ unify_parts : Callable[[Sequence[P]], A]
40
+ Function to unify (union) overlapping parts within an assembly.
41
+ concat_parts : Callable[[Sequence[P]], A]
42
+ Function to concatenate a sequence of parts into a new assembly.
43
+
44
+ Returns
45
+ -------
46
+ A
47
+ The assembly object containing all intersections involving at least one part from each input assembly.
48
+
49
+ Examples
50
+ --------
51
+ >>> from scadpy import intersect_assemblies
52
+
53
+ >>> assemblies = [
54
+ ... [{'bounds': [0, 0, 2, 2]}],
55
+ ... [{'bounds': [1, 1, 3, 3]}, {'bounds': [5, 5, 6, 6]}]
56
+ ... ]
57
+ ...
58
+ >>> def are_intersecting(p1, p2):
59
+ ... b1, b2 = p1['bounds'], p2['bounds']
60
+ ... return not (b1[2] <= b2[0] or b2[2] <= b1[0] or
61
+ ... b1[3] <= b2[1] or b2[3] <= b1[1])
62
+ ...
63
+ >>> def intersect_geometries(geometries):
64
+ ... if len(geometries) == 1:
65
+ ... return [geometries[0]]
66
+ ... minx = max(g[0] for g in geometries)
67
+ ... miny = max(g[1] for g in geometries)
68
+ ... maxx = min(g[2] for g in geometries)
69
+ ... maxy = min(g[3] for g in geometries)
70
+ ... if minx < maxx and miny < maxy:
71
+ ... return [[minx, miny, maxx, maxy]]
72
+ ... return []
73
+ ...
74
+ >>> def intersect_parts(parts):
75
+ ... geometries = [p['bounds'] for p in parts]
76
+ ... for g in intersect_geometries(geometries):
77
+ ... return [{'bounds': g}]
78
+ ...
79
+ >>> intersect_assemblies(
80
+ ... assemblies,
81
+ ... get_assembly_parts=lambda a: a,
82
+ ... get_part_bounds=lambda p: p['bounds'],
83
+ ... are_parts_intersecting=are_intersecting,
84
+ ... intersect_parts=intersect_parts,
85
+ ... unify_parts=lambda p: p,
86
+ ... concat_parts=lambda p: p
87
+ ... )
88
+ [{'bounds': [1, 1, 2, 2]}]
89
+ """
90
+ from scadpy.core.assembly import concat_assemblies, unify_assemblies
91
+ from scadpy.core.component import get_intersecting_component_index_groups
92
+
93
+ unified_assemblies = [
94
+ unify_assemblies(
95
+ [a],
96
+ get_assembly_parts=get_assembly_parts,
97
+ unify_parts=unify_parts,
98
+ )
99
+ for a in assemblies
100
+ ]
101
+ part_with_assembly_index: list[tuple[P, int]] = [
102
+ (p, i) for i, a in enumerate(unified_assemblies) for p in get_assembly_parts(a)
103
+ ]
104
+ parts = [p for p, _ in part_with_assembly_index]
105
+ assembly_indices = [i for _, i in part_with_assembly_index]
106
+
107
+ intersecting_part_index_groups: list[list[int]] = (
108
+ get_intersecting_component_index_groups(
109
+ parts,
110
+ get_component_bounds=get_part_bounds,
111
+ are_components_intersecting=are_parts_intersecting,
112
+ )
113
+ )
114
+
115
+ # keep only groups that have at least one part from each assembly
116
+ num_assemblies = len(assemblies)
117
+ intersected_assemblies: list[A] = []
118
+ for group in intersecting_part_index_groups:
119
+ assembly_ids_in_group = {assembly_indices[idx] for idx in group}
120
+ if len(assembly_ids_in_group) == num_assemblies:
121
+ group_parts = [parts[idx] for idx in group]
122
+ intersected_assemblies.append(intersect_parts(group_parts))
123
+
124
+ return concat_assemblies(
125
+ assemblies=intersected_assemblies,
126
+ get_assembly_parts=get_assembly_parts,
127
+ concat_parts=concat_parts,
128
+ )
@@ -0,0 +1,151 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Iterable, Sequence
4
+
5
+ import numpy as np
6
+ from numpy.typing import NDArray
7
+ from typeguard import typechecked
8
+
9
+
10
+ @typechecked
11
+ def subtract_assemblies[A, P](
12
+ to_be_subtracted: A,
13
+ to_subtract: A,
14
+ get_assembly_parts: Callable[[A], Iterable[P]],
15
+ get_part_bounds: Callable[[P], NDArray[np.float64]],
16
+ are_parts_intersecting: Callable[[P, P], bool],
17
+ subtract_parts: Callable[[P, P], A],
18
+ intersect_parts: Callable[[Sequence[P]], A],
19
+ unify_parts: Callable[[Sequence[P]], A],
20
+ concat_parts: Callable[[Sequence[P]], A],
21
+ ) -> A:
22
+ """
23
+ Subtract the geometry of all parts in one assembly from all parts in another assembly.
24
+
25
+ This function is fully generic and uses dependency injection for all domain-specific
26
+ operations, making it suitable for a wide range of applications (2D, 3D, CAD, etc.).
27
+ For each part in the first assembly, all intersecting parts from the second assembly are subtracted.
28
+
29
+ Parameters
30
+ ----------
31
+ to_be_subtracted : A
32
+ The assembly whose parts will be subtracted from.
33
+ to_subtract : A
34
+ The assembly whose parts will be subtracted.
35
+ get_assembly_parts : Callable[[A], Iterable[P]]
36
+ Function that extracts parts from an assembly.
37
+ get_part_bounds : Callable[[P], NDArray[np.float64]]
38
+ Function to extract the bounding box of a part.
39
+ are_parts_intersecting : Callable[[P, P], bool]
40
+ Function to determine if two parts intersect.
41
+ subtract_parts : Callable[[P, P], A]
42
+ Function to subtract one part from another, returning an assembly.
43
+ intersect_parts : Callable[[Sequence[P]], A]
44
+ Function to compute the intersection of a group of parts.
45
+ unify_parts : Callable[[Sequence[P]], A]
46
+ Function to unify (union) overlapping parts within an assembly.
47
+ concat_parts : Callable[[Sequence[P]], A]
48
+ Function to concatenate a sequence of parts into a new assembly.
49
+
50
+ Returns
51
+ -------
52
+ A
53
+ The assembly object containing all subtracted parts.
54
+
55
+ Examples
56
+ --------
57
+ >>> from scadpy import subtract_assemblies
58
+
59
+ >>> assembly1 = [{'bounds': [0, 0, 3, 3]}]
60
+ >>> assembly2 = [{'bounds': [1, 1, 2, 2]}]
61
+ ...
62
+ >>> def are_intersecting(p1, p2):
63
+ ... b1, b2 = p1['bounds'], p2['bounds']
64
+ ... return not (b1[2] <= b2[0] or b2[2] <= b1[0] or
65
+ ... b1[3] <= b2[1] or b2[3] <= b1[1])
66
+ ...
67
+ >>> def subtract_geometries(g1, g2):
68
+ ... if (g2[0] > g1[0] and g2[2] < g1[2]
69
+ ... and g2[1] > g1[1] and g2[3] < g1[3]):
70
+ ... return [
71
+ ... [g1[0], g1[1], g2[0], g1[3]], # left
72
+ ... [g2[2], g1[1], g1[2], g1[3]], # right
73
+ ... [g2[0], g1[1], g2[2], g2[1]], # bottom
74
+ ... [g2[0], g2[3], g2[2], g1[3]], # top
75
+ ... ]
76
+ ... return [g1]
77
+ ...
78
+ >>> def subtract_parts(p1, p2):
79
+ ... geometries = subtract_geometries(
80
+ ... p1['bounds'], p2['bounds']
81
+ ... )
82
+ ... return [{'bounds': g} for g in geometries]
83
+ ...
84
+ >>> result = subtract_assemblies(
85
+ ... assembly1, assembly2,
86
+ ... get_assembly_parts=lambda a: a,
87
+ ... get_part_bounds=lambda p: p['bounds'],
88
+ ... are_parts_intersecting=are_intersecting,
89
+ ... subtract_parts=subtract_parts,
90
+ ... intersect_parts=lambda p: p,
91
+ ... unify_parts=lambda p: p,
92
+ ... concat_parts=lambda p: p
93
+ ... )
94
+ >>> result == [
95
+ ... {'bounds': [0, 0, 1, 3]},
96
+ ... {'bounds': [2, 0, 3, 3]},
97
+ ... {'bounds': [1, 0, 2, 1]},
98
+ ... {'bounds': [1, 2, 2, 3]}
99
+ ... ]
100
+ True
101
+ """
102
+
103
+ from scadpy.core.assembly import (
104
+ concat_assemblies,
105
+ intersect_assemblies,
106
+ unify_assemblies,
107
+ )
108
+
109
+ unified_to_subtract = unify_assemblies(
110
+ [to_subtract],
111
+ get_assembly_parts=get_assembly_parts,
112
+ unify_parts=unify_parts,
113
+ )
114
+
115
+ parts_to_be_subtracted = get_assembly_parts(to_be_subtracted)
116
+ parts_to_subtract = get_assembly_parts(unified_to_subtract)
117
+
118
+ subtracted_assemblies: list[A] = []
119
+ for part_to_be_subtracted in parts_to_be_subtracted:
120
+ intersecting_parts_to_subtract = [
121
+ part
122
+ for part in parts_to_subtract
123
+ if are_parts_intersecting(part_to_be_subtracted, part)
124
+ ]
125
+ if not intersecting_parts_to_subtract:
126
+ subtracted_assemblies.append(concat_parts([part_to_be_subtracted]))
127
+ continue
128
+
129
+ partially_subtracted_assemblies: list[A] = []
130
+ for part_to_subtract in intersecting_parts_to_subtract:
131
+ partially_subtracted_assemblies.append(
132
+ subtract_parts(part_to_be_subtracted, part_to_subtract)
133
+ )
134
+
135
+ subtracted_assemblies += [
136
+ intersect_assemblies(
137
+ assemblies=partially_subtracted_assemblies,
138
+ get_assembly_parts=get_assembly_parts,
139
+ get_part_bounds=get_part_bounds,
140
+ are_parts_intersecting=are_parts_intersecting,
141
+ intersect_parts=intersect_parts,
142
+ unify_parts=unify_parts,
143
+ concat_parts=concat_parts,
144
+ )
145
+ ]
146
+
147
+ return concat_assemblies(
148
+ assemblies=subtracted_assemblies,
149
+ get_assembly_parts=get_assembly_parts,
150
+ concat_parts=concat_parts,
151
+ )