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,39 @@
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 Solid
11
+
12
+
13
+ @typechecked
14
+ def get_solid_vertex_coordinates(solid: Solid) -> NDArray[np.float64]:
15
+ """For each vertex in the solid, return its coordinates.
16
+
17
+ Parameters
18
+ ----------
19
+ solid : Solid
20
+ The solid to extract vertex coordinates from.
21
+
22
+ Returns
23
+ -------
24
+ NDArray[np.float64]
25
+ 2D array of shape (n_vertices, 3), one row per vertex.
26
+
27
+ Examples
28
+ --------
29
+ >>> from scadpy import cuboid, get_solid_vertex_coordinates
30
+
31
+ >>> vertex_coordinates = get_solid_vertex_coordinates(cuboid(2))
32
+ >>> vertex_coordinates.shape
33
+ (8, 3)
34
+ """
35
+ from scadpy import get_assembly_vertex_coordinates
36
+
37
+ return get_assembly_vertex_coordinates(
38
+ solid._parts, lambda p: p.geometry.vertices, 3
39
+ )
@@ -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 typeguard import typechecked
8
+
9
+ if TYPE_CHECKING:
10
+ from scadpy import Solid
11
+
12
+
13
+ @typechecked
14
+ def get_solid_vertex_to_part(solid: Solid) -> NDArray[np.int64]:
15
+ """For each vertex in the solid, return its part index.
16
+
17
+ Parameters
18
+ ----------
19
+ solid : Solid
20
+ The solid to extract part indices from.
21
+
22
+ Returns
23
+ -------
24
+ NDArray[np.int64]
25
+ 1D array of shape (n_vertices,), one element per vertex.
26
+
27
+ Examples
28
+ --------
29
+ >>> from scadpy import cuboid, get_solid_vertex_to_part
30
+
31
+ >>> solid = cuboid(2) + cuboid(2).translate(5)
32
+ >>> get_solid_vertex_to_part(solid)
33
+ array([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1])
34
+ """
35
+ from scadpy import get_assembly_vertex_to_part
36
+
37
+ return get_assembly_vertex_to_part(solid._parts, lambda p: p.geometry.vertices)
@@ -0,0 +1,23 @@
1
+ __all__ = [
2
+ "color_solid",
3
+ "convexify_solid",
4
+ "mirror_solid",
5
+ "pull_solid",
6
+ "push_solid",
7
+ "recoordinate_solid",
8
+ "resize_solid",
9
+ "rotate_solid",
10
+ "scale_solid",
11
+ "translate_solid",
12
+ ]
13
+
14
+ from .color_solid import color_solid
15
+ from .convexify_solid import convexify_solid
16
+ from .mirror_solid import mirror_solid
17
+ from .pull_solid import pull_solid
18
+ from .push_solid import push_solid
19
+ from .recoordinate_solid import recoordinate_solid
20
+ from .resize_solid import resize_solid
21
+ from .rotate_solid import rotate_solid
22
+ from .scale_solid import scale_solid
23
+ from .translate_solid import translate_solid
@@ -0,0 +1,46 @@
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 Color, Solid
9
+
10
+
11
+ @typechecked
12
+ def color_solid(solid: Solid, color: Color) -> Solid:
13
+ """Set the color of all parts in a solid.
14
+
15
+ Parameters
16
+ ----------
17
+ solid : Solid
18
+ The solid whose parts will be recolored.
19
+ color : Color
20
+ The RGBA color to apply to all parts.
21
+
22
+ Returns
23
+ -------
24
+ Solid
25
+ A new solid with all parts set to the given color.
26
+
27
+ Examples
28
+ --------
29
+ >>> from scadpy import cuboid, color_solid
30
+ >>> from scadpy.color.constants import RED
31
+
32
+ >>> color_solid(solid=cuboid(4), color=RED) # doctest: +SKIP
33
+
34
+ .. render-example::
35
+ :name: color_solid
36
+ :example: color_solid(solid=cuboid(4), color=RED)
37
+ :keep-color:
38
+ """
39
+ from scadpy import Solid, color_assembly
40
+
41
+ return color_assembly(
42
+ assembly=solid,
43
+ color=color,
44
+ get_assembly_parts=lambda assembly: assembly._parts,
45
+ concat_parts=Solid.from_parts,
46
+ )
@@ -0,0 +1,64 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import numpy as np
6
+ from trimesh import Trimesh
7
+ from typeguard import typechecked
8
+
9
+ if TYPE_CHECKING:
10
+ from scadpy import Solid, TopologyFilter
11
+
12
+
13
+ @typechecked
14
+ def convexify_solid(
15
+ solid: Solid, part_filter: TopologyFilter[Solid] | None = None
16
+ ) -> Solid:
17
+ """Create a new solid whose selected parts are replaced by their convex hull.
18
+
19
+ Parameters
20
+ ----------
21
+ solid : Solid
22
+ The input solid whose parts will be convexified.
23
+ part_filter : TopologyFilter[Solid] | None, optional
24
+ A boolean mask selecting which parts to convexify. Parts not selected are
25
+ left unchanged. If None, all parts are convexified together.
26
+
27
+ Returns
28
+ -------
29
+ Solid
30
+ A new solid consisting of the convex hull of the selected parts, plus the
31
+ unselected parts unchanged.
32
+
33
+ Examples
34
+ --------
35
+ >>> from scadpy import cuboid, sphere, convexify_solid
36
+
37
+ >>> convexify_solid( # doctest: +SKIP
38
+ ... cuboid(4) + sphere(radius=2).translate([3, 3, 3])
39
+ ... )
40
+
41
+ .. render-example::
42
+ :name: convexify_solid
43
+ :example: convexify_solid(cuboid(4) + sphere(radius=2).translate([3, 3, 3]))
44
+ :ghost: cuboid(4) + sphere(radius=2).translate([3, 3, 3])
45
+ """
46
+ from scadpy import Part, Solid, blend_part_colors, transform_filtered_parts
47
+
48
+ return transform_filtered_parts(
49
+ assembly=solid,
50
+ parts=solid._parts,
51
+ part_filter=part_filter,
52
+ transform=lambda parts: [
53
+ Part[Trimesh].from_geometry(
54
+ Trimesh(
55
+ vertices=np.vstack([p.geometry.vertices for p in parts])
56
+ ).convex_hull,
57
+ blend_part_colors(
58
+ parts=parts,
59
+ get_part_magnitude=lambda p: p.geometry.volume,
60
+ ),
61
+ )
62
+ ],
63
+ concat_parts=Solid.from_parts,
64
+ )
@@ -0,0 +1,53 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable
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 mirror_solid(
14
+ solid: Solid,
15
+ normal: float | Iterable[float],
16
+ pivot: float | Iterable[float] = 0,
17
+ ) -> Solid:
18
+ """Mirror a solid across a plane defined by a normal vector and a pivot point.
19
+
20
+ Parameters
21
+ ----------
22
+ solid : Solid
23
+ The solid to mirror.
24
+ normal : float | Iterable[float]
25
+ The normal vector of the mirror plane. Does not need to be normalized.
26
+ If a single float is provided, it is broadcast to all coordinate dimensions.
27
+ pivot : float | Iterable[float], default=0
28
+ The point through which the mirror plane passes. If a single float is
29
+ provided, it is broadcast to all coordinate dimensions. Defaults to the origin.
30
+
31
+ Returns
32
+ -------
33
+ Solid
34
+ A new solid with all vertices mirrored across the specified plane.
35
+
36
+ Examples
37
+ --------
38
+ >>> from scadpy import cuboid, mirror_solid
39
+
40
+ >>> mirror_solid( # doctest: +SKIP
41
+ ... solid=cuboid(4), normal=[1, 0, 0], pivot=[2, 0, 0]
42
+ ... )
43
+
44
+ .. render-example::
45
+ :name: mirror_solid
46
+ :example: mirror_solid(solid=cuboid(4), normal=[1, 0, 0], pivot=[2, 0, 0])
47
+ :ghost: cuboid(4)
48
+ """
49
+ from scadpy import mirror_vertex_coordinates
50
+
51
+ return solid.recoordinate(
52
+ mirror_vertex_coordinates(solid.vertex_coordinates, normal, pivot)
53
+ )
@@ -0,0 +1,67 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable
4
+ from typing import TYPE_CHECKING
5
+
6
+ from typeguard import typechecked
7
+
8
+
9
+ if TYPE_CHECKING:
10
+ from scadpy import Solid, TopologyFilter
11
+
12
+
13
+ @typechecked
14
+ def pull_solid(
15
+ solid: Solid,
16
+ distance: float,
17
+ pivot: float | Iterable[float] = 0,
18
+ vertex_filter: TopologyFilter[Solid] | None = None,
19
+ ) -> Solid:
20
+ """Move a subset of solid vertices toward a pivot point by a given distance.
21
+
22
+ Each selected vertex is translated toward the pivot by at most ``distance`` units.
23
+ Vertices already closer than ``distance`` to the pivot are moved to the pivot.
24
+
25
+ Parameters
26
+ ----------
27
+ solid : Solid
28
+ The solid whose vertices will be pulled.
29
+ distance : float
30
+ The maximum distance each vertex is moved toward the pivot.
31
+ pivot : float | Iterable[float], default=0
32
+ The attraction point. If a single float is provided, it is broadcast
33
+ to all coordinate dimensions. Defaults to the origin.
34
+ vertex_filter : TopologyFilter[Solid] | None, default=None
35
+ A boolean array or callable selecting which vertices are affected. If ``None``,
36
+ all vertices are moved.
37
+
38
+ Returns
39
+ -------
40
+ Solid
41
+ A new solid with the selected vertices moved toward the pivot.
42
+
43
+ See Also
44
+ --------
45
+ push_solid : Move solid vertices away from a pivot point.
46
+
47
+ Examples
48
+ --------
49
+ >>> from scadpy import cuboid, pull_solid
50
+ >>> import numpy as np
51
+
52
+ >>> pull_solid( # doctest: +SKIP
53
+ ... solid=cuboid(4), distance=1.0, pivot=[2, 2, 2],
54
+ ... vertex_filter=np.ones(8, dtype=bool),
55
+ ... )
56
+
57
+ .. render-example::
58
+ :name: pull_solid
59
+ :example: pull_solid(solid=cuboid(4), distance=1.0, pivot=[2, 2, 2], vertex_filter=cuboid(4).vertex_coordinates[:, 0] < 1)
60
+ :ghost: cuboid(4)
61
+ """
62
+ from scadpy import resolve_topology_filter, pull_vertex_coordinates
63
+
64
+ resolved_vertex_filter = resolve_topology_filter(solid, len(solid.vertex_coordinates), vertex_filter)
65
+ return solid.recoordinate(
66
+ pull_vertex_coordinates(solid.vertex_coordinates, distance, pivot, resolved_vertex_filter)
67
+ )
@@ -0,0 +1,67 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable
4
+ from typing import TYPE_CHECKING
5
+
6
+ from typeguard import typechecked
7
+
8
+
9
+ if TYPE_CHECKING:
10
+ from scadpy import Solid, TopologyFilter
11
+
12
+
13
+ @typechecked
14
+ def push_solid(
15
+ solid: Solid,
16
+ distance: float,
17
+ pivot: float | Iterable[float] = 0,
18
+ vertex_filter: TopologyFilter[Solid] | None = None,
19
+ ) -> Solid:
20
+ """Move a subset of solid vertices away from a pivot point by a given distance.
21
+
22
+ Each selected vertex is translated away from the pivot by exactly ``distance`` units.
23
+ Vertices located exactly at the pivot are not moved (undefined direction).
24
+
25
+ Parameters
26
+ ----------
27
+ solid : Solid
28
+ The solid whose vertices will be pushed.
29
+ distance : float
30
+ The distance each vertex is moved away from the pivot.
31
+ pivot : float | Iterable[float], default=0
32
+ The repulsion point. If a single float is provided, it is broadcast
33
+ to all coordinate dimensions. Defaults to the origin.
34
+ vertex_filter : TopologyFilter[Solid] | None, default=None
35
+ A boolean array or callable selecting which vertices are affected. If ``None``,
36
+ all vertices are moved.
37
+
38
+ Returns
39
+ -------
40
+ Solid
41
+ A new solid with the selected vertices moved away from the pivot.
42
+
43
+ See Also
44
+ --------
45
+ pull_solid : Move solid vertices toward a pivot point.
46
+
47
+ Examples
48
+ --------
49
+ >>> from scadpy import cuboid, push_solid
50
+ >>> import numpy as np
51
+
52
+ >>> push_solid( # doctest: +SKIP
53
+ ... solid=cuboid(4), distance=1.0, pivot=[2, 2, 2],
54
+ ... vertex_filter=np.ones(8, dtype=bool),
55
+ ... )
56
+
57
+ .. render-example::
58
+ :name: push_solid
59
+ :example: push_solid(solid=cuboid(4), distance=1.0, pivot=[2, 2, 2], vertex_filter=cuboid(4).vertex_coordinates[:, 0] < 1)
60
+ :ghost: cuboid(4)
61
+ """
62
+ from scadpy import resolve_topology_filter, push_vertex_coordinates
63
+
64
+ resolved_vertex_filter = resolve_topology_filter(solid, len(solid.vertex_coordinates), vertex_filter)
65
+ return solid.recoordinate(
66
+ push_vertex_coordinates(solid.vertex_coordinates, distance, pivot, resolved_vertex_filter)
67
+ )
@@ -0,0 +1,68 @@
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 trimesh import Trimesh
8
+ from typeguard import typechecked
9
+
10
+ if TYPE_CHECKING:
11
+ from scadpy.d3.solid import Solid
12
+
13
+
14
+ @typechecked
15
+ def recoordinate_solid(
16
+ solid: Solid, vertex_coordinates: NDArray[np.float64]
17
+ ) -> Solid:
18
+ """Rebuild a solid with new vertex coordinates, preserving topology and colors.
19
+
20
+ Parameters
21
+ ----------
22
+ solid : Solid
23
+ The source solid providing topology (part/face structure) and colors.
24
+ vertex_coordinates : NDArray[np.float64]
25
+ New vertex coordinates of shape ``(n_vertices, 3)``, in the same order
26
+ as :func:`get_assembly_vertex_coordinates`.
27
+
28
+ Returns
29
+ -------
30
+ Solid
31
+ A new solid with the same topology as *solid* but at the new positions.
32
+
33
+ Examples
34
+ --------
35
+ >>> from scadpy import cuboid, recoordinate_solid
36
+
37
+ >>> recoordinate_solid( # doctest: +SKIP
38
+ ... solid=cuboid(4),
39
+ ... vertex_coordinates=cuboid(4).vertex_coordinates + [2.0, 1.0, 0.0],
40
+ ... )
41
+
42
+ .. render-example::
43
+ :name: recoordinate_solid
44
+ :example: recoordinate_solid(solid=cuboid(4), vertex_coordinates=cuboid(4).vertex_coordinates + [2.0, 1.0, 0.0])
45
+ :ghost: cuboid(4)
46
+ """
47
+ from scadpy import Part, map_parts_to_solid
48
+
49
+ vertex_to_part = solid.vertex_to_part
50
+ part_colors = solid.part_colors
51
+
52
+ parts = []
53
+ for part_index in np.unique(vertex_to_part):
54
+ part_mask = vertex_to_part == part_index
55
+ part_vertex_coordinates = vertex_coordinates[part_mask]
56
+ color = list(part_colors[part_index])
57
+
58
+ parts.append(
59
+ Part[Trimesh].from_geometry(
60
+ Trimesh(
61
+ vertices=part_vertex_coordinates,
62
+ faces=solid._parts[part_index].geometry.faces,
63
+ ),
64
+ color,
65
+ )
66
+ )
67
+
68
+ return map_parts_to_solid(parts)
@@ -0,0 +1,92 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable
4
+ from typing import TYPE_CHECKING
5
+
6
+ from typeguard import typechecked
7
+
8
+ if TYPE_CHECKING:
9
+ from scadpy import Solid, TopologyFilter
10
+
11
+
12
+ @typechecked
13
+ def resize_solid(
14
+ solid: Solid,
15
+ size: Iterable[float | None],
16
+ auto: bool = False,
17
+ pivot: float | Iterable[float] | None = None,
18
+ vertex_filter: TopologyFilter[Solid] | None = None,
19
+ ) -> Solid:
20
+ """Resize a solid to fit target dimensions.
21
+
22
+ Scales the solid so that each non-``None`` axis matches the given target
23
+ size. The scaling pivot defaults to the center of the bounding box so the
24
+ solid stays in place.
25
+
26
+ Shortcut delegating to :func:`resize_vertex_coordinates`.
27
+
28
+ Parameters
29
+ ----------
30
+ solid : Solid
31
+ The solid to resize.
32
+ size : Iterable[float | None]
33
+ Target dimensions ``[width, height, depth]``. Pass ``None`` for an
34
+ axis to leave it unchanged (or scale it proportionally when
35
+ ``auto=True``).
36
+ auto : bool, default=False
37
+ If ``True``, axes with ``None`` are scaled proportionally to the
38
+ average ratio of the defined axes. If ``False``, ``None`` axes are
39
+ left unchanged.
40
+ pivot : float | Iterable[float] | None, default=None
41
+ The point relative to which scaling is performed. Defaults to the
42
+ center of the bounding box.
43
+ vertex_filter : TopologyFilter[Solid] | None, default=None
44
+ Boolean array or callable selecting which vertices are resized. If ``None``, all
45
+ vertices are resized.
46
+
47
+ Returns
48
+ -------
49
+ Solid
50
+ A new solid resized to the target dimensions.
51
+
52
+ Examples
53
+ --------
54
+ >>> from scadpy import cuboid, resize_solid
55
+
56
+ >>> # resize to an exact size on all axes:
57
+ >>> resize_solid(solid=cuboid([4, 2, 1]), size=[6, 6, 6]) # doctest: +SKIP
58
+
59
+ .. render-example::
60
+ :name: resize_solid_exact
61
+ :example: resize_solid(solid=cuboid([4, 2, 1]), size=[6, 6, 6])
62
+ :ghost: cuboid([4, 2, 1])
63
+
64
+ >>> # freeze two axes (``None``) and scale only the first:
65
+ >>> resize_solid(solid=cuboid([4, 2, 1]), size=[6, None, None]) # doctest: +SKIP
66
+
67
+ .. render-example::
68
+ :name: resize_solid_freeze
69
+ :example: resize_solid(solid=cuboid([4, 2, 1]), size=[6, None, None])
70
+ :ghost: cuboid([4, 2, 1])
71
+
72
+ >>> # scale frozen axes proportionally with ``auto=True``:
73
+ >>> resize_solid(solid=cuboid([4, 2, 1]), size=[6, None, None], auto=True) # doctest: +SKIP
74
+
75
+ .. render-example::
76
+ :name: resize_solid_auto
77
+ :example: resize_solid(solid=cuboid([4, 2, 1]), size=[6, None, None], auto=True)
78
+ :ghost: cuboid([4, 2, 1])
79
+ """
80
+ from scadpy import resolve_topology_filter, resize_vertex_coordinates
81
+
82
+ resolved_vertex_filter = resolve_topology_filter(solid, len(solid.vertex_coordinates), vertex_filter)
83
+ return solid.recoordinate(
84
+ resize_vertex_coordinates(
85
+ solid.vertex_coordinates,
86
+ size=size,
87
+ n_dims=3,
88
+ auto=auto,
89
+ pivot=pivot,
90
+ vertex_filter=resolved_vertex_filter,
91
+ )
92
+ )
@@ -0,0 +1,93 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable
4
+ from typing import TYPE_CHECKING
5
+
6
+ import numpy as np
7
+ from typeguard import typechecked
8
+
9
+ if TYPE_CHECKING:
10
+ from scadpy.d3.solid import Solid
11
+ from scadpy import TopologyFilter
12
+
13
+
14
+ @typechecked
15
+ def rotate_solid(
16
+ solid: Solid,
17
+ angle: float,
18
+ axis: float | Iterable[float],
19
+ pivot: float | Iterable[float] = 0,
20
+ vertex_filter: TopologyFilter[Solid] | None = None,
21
+ ) -> Solid:
22
+ """Rotate a solid by a given angle around an axis passing through a pivot point.
23
+
24
+ Parameters
25
+ ----------
26
+ solid : Solid
27
+ The solid to rotate.
28
+ angle : float
29
+ The rotation angle in degrees.
30
+ axis : float | Iterable[float]
31
+ The rotation axis vector. If a single float is provided, it is broadcast
32
+ to all coordinate dimensions.
33
+ pivot : float | Iterable[float], default=0
34
+ The point around which rotation is applied. If a single float is provided,
35
+ it is broadcast to all coordinate dimensions. Defaults to the origin.
36
+ vertex_filter : TopologyFilter[Solid] | None, default=None
37
+ Boolean array or callable selecting which vertices are rotated. If ``None``, all
38
+ vertices are rotated.
39
+
40
+ Returns
41
+ -------
42
+ Solid
43
+ A new solid with the selected vertices rotated around the axis and pivot.
44
+
45
+ Examples
46
+ --------
47
+ >>> from scadpy import cuboid, rotate_solid
48
+
49
+ >>> rotate_solid( # doctest: +SKIP
50
+ ... solid=cuboid(4), angle=45,
51
+ ... axis=[0, 0, 1], pivot=[2, 2, 2],
52
+ ... )
53
+
54
+ .. render-example::
55
+ :name: rotate_solid
56
+ :example: rotate_solid(solid=cuboid(4), angle=45, axis=[0, 0, 1], pivot=[2, 2, 2])
57
+ :ghost: cuboid(4)
58
+ """
59
+ from scadpy import resolve_topology_filter, rotate_vertex_coordinates, resolve_vector_3d
60
+
61
+ angle_rad = np.deg2rad(angle)
62
+ cosinus = np.cos(angle_rad)
63
+ sinus = np.sin(angle_rad)
64
+ one_minus_cosinus = 1 - cosinus
65
+
66
+ axis_array = resolve_vector_3d(axis, 0)
67
+ axis_array = axis_array / np.linalg.norm(axis_array)
68
+ x, y, z = axis_array
69
+
70
+ R = np.array(
71
+ [
72
+ [
73
+ cosinus + x * x * one_minus_cosinus,
74
+ x * y * one_minus_cosinus - z * sinus,
75
+ x * z * one_minus_cosinus + y * sinus,
76
+ ],
77
+ [
78
+ y * x * one_minus_cosinus + z * sinus,
79
+ cosinus + y * y * one_minus_cosinus,
80
+ y * z * one_minus_cosinus - x * sinus,
81
+ ],
82
+ [
83
+ z * x * one_minus_cosinus - y * sinus,
84
+ z * y * one_minus_cosinus + x * sinus,
85
+ cosinus + z * z * one_minus_cosinus,
86
+ ],
87
+ ]
88
+ )
89
+
90
+ resolved_vertex_filter = resolve_topology_filter(solid, len(solid.vertex_coordinates), vertex_filter)
91
+ return solid.recoordinate(
92
+ rotate_vertex_coordinates(solid.vertex_coordinates, R, pivot, resolved_vertex_filter)
93
+ )