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,36 @@
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 is_solid_empty(solid: Solid) -> bool:
13
+ """Return whether the solid has no vertices.
14
+
15
+ Parameters
16
+ ----------
17
+ solid : Solid
18
+ The solid to check.
19
+
20
+ Returns
21
+ -------
22
+ bool
23
+ True if the solid has no vertices, False otherwise.
24
+
25
+ Examples
26
+ --------
27
+ >>> from scadpy import Solid, is_solid_empty
28
+
29
+ >>> is_solid_empty(Solid.from_parts([]))
30
+ True
31
+
32
+ >>> from scadpy import cuboid
33
+ >>> is_solid_empty(cuboid(2))
34
+ False
35
+ """
36
+ return len(solid.vertex_coordinates) == 0
@@ -0,0 +1,11 @@
1
+ __all__ = [
2
+ "map_geometries_to_solid",
3
+ "map_geometry_to_solid",
4
+ "map_parts_to_solid",
5
+ "map_stl_to_solid",
6
+ ]
7
+
8
+ from .map_geometries_to_solid import map_geometries_to_solid
9
+ from .map_geometry_to_solid import map_geometry_to_solid
10
+ from .map_parts_to_solid import map_parts_to_solid
11
+ from .map_stl_to_solid import map_stl_to_solid
@@ -0,0 +1,42 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Sequence
4
+ from typing import TYPE_CHECKING
5
+
6
+ from trimesh import Trimesh
7
+ from typeguard import typechecked
8
+
9
+ if TYPE_CHECKING:
10
+ from scadpy.d3.solid import Solid
11
+
12
+
13
+ @typechecked
14
+ def map_geometries_to_solid(geometries: Sequence[Trimesh]) -> Solid:
15
+ """Map a sequence of Trimesh geometries to a solid.
16
+
17
+ Parameters
18
+ ----------
19
+ geometries : Sequence[Trimesh]
20
+ The geometries to map.
21
+
22
+ Returns
23
+ -------
24
+ Solid
25
+ A new solid containing all input geometries as parts.
26
+
27
+ Examples
28
+ --------
29
+ >>> from scadpy import cuboid, map_geometries_to_solid
30
+
31
+ >>> map_geometries_to_solid( # doctest: +SKIP
32
+ ... [cuboid(4)._parts[0].geometry]
33
+ ... )
34
+
35
+ .. render-example::
36
+ :name: map_geometries_to_solid
37
+ :example: map_geometries_to_solid([cuboid(4)._parts[0].geometry])
38
+ """
39
+ from scadpy.core.part import Part
40
+ from scadpy.d3.solid import Solid
41
+
42
+ return Solid.from_parts([Part[Trimesh].from_geometry(g) for g in geometries])
@@ -0,0 +1,42 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from trimesh import Trimesh
6
+ from typeguard import typechecked
7
+
8
+ if TYPE_CHECKING:
9
+ from scadpy.d3.solid import Solid
10
+
11
+
12
+ @typechecked
13
+ def map_geometry_to_solid(geometry: Trimesh) -> Solid:
14
+ """Map a single Trimesh geometry to a solid.
15
+
16
+ Shortcut for :func:`map_geometries_to_solid` with a single geometry.
17
+
18
+ Parameters
19
+ ----------
20
+ geometry : Trimesh
21
+ The geometry to map.
22
+
23
+ Returns
24
+ -------
25
+ Solid
26
+ A new solid containing the single geometry as a part.
27
+
28
+ Examples
29
+ --------
30
+ >>> from scadpy import cuboid, map_geometry_to_solid
31
+
32
+ >>> map_geometry_to_solid( # doctest: +SKIP
33
+ ... cuboid(4)._parts[0].geometry
34
+ ... )
35
+
36
+ .. render-example::
37
+ :name: map_geometry_to_solid
38
+ :example: map_geometry_to_solid(cuboid(4)._parts[0].geometry)
39
+ """
40
+ from scadpy.d3.solid.importers import map_geometries_to_solid
41
+
42
+ return map_geometries_to_solid([geometry])
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Sequence
4
+ from typing import TYPE_CHECKING
5
+
6
+ from trimesh import Trimesh
7
+ from typeguard import typechecked
8
+
9
+ if TYPE_CHECKING:
10
+ from scadpy import Part, Solid
11
+
12
+
13
+ @typechecked
14
+ def map_parts_to_solid(
15
+ parts: Sequence[Part[Trimesh]],
16
+ ) -> Solid:
17
+ """Map a sequence of parts to a solid, repairing geometry where needed.
18
+
19
+ For each part, two repairs are applied if needed:
20
+
21
+ - **Locally inconsistent winding**: if adjacent faces have inconsistent winding
22
+ order (e.g. after some boolean operations), :func:`trimesh.Trimesh.fix_normals`
23
+ is called to make them consistent.
24
+ - **Globally inverted normals**: if the mesh has negative volume (e.g. after
25
+ a mirror transform), the face winding order is reversed to restore
26
+ outward-pointing normals.
27
+
28
+ A mesh copy is only made when a repair is actually needed.
29
+
30
+ Parameters
31
+ ----------
32
+ parts : Sequence[Part[Trimesh]]
33
+ The parts to map. Each part holds a Trimesh mesh and a color.
34
+
35
+ Returns
36
+ -------
37
+ Solid
38
+ A new solid containing all parts with corrected geometry.
39
+
40
+ Examples
41
+ --------
42
+ >>> from scadpy import cuboid, map_parts_to_solid
43
+
44
+ >>> map_parts_to_solid( # doctest: +SKIP
45
+ ... cuboid(4)._parts
46
+ ... )
47
+
48
+ .. render-example::
49
+ :name: map_parts_to_solid
50
+ :example: map_parts_to_solid(cuboid(4)._parts)
51
+ """
52
+ from scadpy.core.part import Part
53
+ from scadpy.d3.solid.types.solid import Solid
54
+
55
+ fixed_parts: list[Part[Trimesh]] = []
56
+ for part in parts:
57
+ mesh = part.geometry
58
+ needs_fix = (not mesh.is_winding_consistent) or (mesh.volume < 0)
59
+ if needs_fix:
60
+ mesh = mesh.copy()
61
+ mesh.fix_normals()
62
+ fixed_parts.append(Part[Trimesh].from_geometry(mesh, part.color))
63
+
64
+ solid = Solid()
65
+ solid._parts = fixed_parts
66
+ return solid
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING
5
+
6
+ from trimesh import Trimesh, load
7
+ from typeguard import typechecked
8
+
9
+ if TYPE_CHECKING:
10
+ from scadpy.d3.solid import Solid
11
+
12
+
13
+ @typechecked
14
+ def map_stl_to_solid(source: str | Path) -> Solid:
15
+ """Load a solid from an STL file.
16
+
17
+ Parameters
18
+ ----------
19
+ source : str or Path
20
+ Path to the ``.stl`` file.
21
+
22
+ Returns
23
+ -------
24
+ Solid
25
+ A new solid loaded from the STL file.
26
+
27
+ Examples
28
+ --------
29
+ >>> from scadpy import map_stl_to_solid
30
+
31
+ >>> map_stl_to_solid("model.stl") # doctest: +SKIP
32
+ """
33
+ from typing import cast
34
+
35
+ from scadpy.d3.solid.importers import map_geometry_to_solid
36
+
37
+ return map_geometry_to_solid(cast(Trimesh, load(source, force="mesh")))
@@ -0,0 +1,7 @@
1
+ __all__ = ["cone", "cuboid", "cylinder", "polyhedron", "sphere"]
2
+
3
+ from .cone import cone
4
+ from .cuboid import cuboid
5
+ from .cylinder import cylinder
6
+ from .polyhedron import polyhedron
7
+ from .sphere import sphere
@@ -0,0 +1,70 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import numpy as np
6
+ from typeguard import typechecked
7
+
8
+ if TYPE_CHECKING:
9
+ from scadpy.d3.solid import Solid
10
+
11
+
12
+ @typechecked
13
+ def cone(radius: float, height: float, section_count: int = 32) -> Solid:
14
+ """Create a cone centered at the origin, apex pointing along +z.
15
+
16
+ Parameters
17
+ ----------
18
+ radius : float
19
+ The radius of the base circle.
20
+ height : float
21
+ The total height of the cone.
22
+ section_count : int, optional
23
+ The number of sides of the polygonal base approximation. Default is 32.
24
+
25
+ Returns
26
+ -------
27
+ Solid
28
+ A :class:`~scadpy.d3.solid.types.solid.Solid` object representing the
29
+ cone, centered at the origin.
30
+
31
+ Examples
32
+ --------
33
+ >>> from scadpy import cone
34
+
35
+ >>> cone(radius=2, height=4) # doctest: +SKIP
36
+
37
+ .. render-example::
38
+ :name: cone
39
+ :example: cone(radius=2, height=4)
40
+
41
+ >>> cone(radius=2, height=4, section_count=6) # doctest: +SKIP
42
+
43
+ .. render-example::
44
+ :name: hexagonal_cone
45
+ :example: cone(radius=2, height=4, section_count=6)
46
+ """
47
+ from scadpy.d3.solid.primitives.polyhedron import polyhedron
48
+
49
+ n = section_count
50
+ angles = np.linspace(0, 2 * np.pi, n, endpoint=False)
51
+ cos_a = np.cos(angles)
52
+ sin_a = np.sin(angles)
53
+
54
+ # Vertex layout:
55
+ # 0 .. n-1 base circle at z = -height/2
56
+ # n apex at z = +height/2
57
+ # n+1 base center at z = -height/2
58
+ base = np.column_stack([radius * cos_a, radius * sin_a, np.full(n, -height / 2)])
59
+ apex = np.array([[0.0, 0.0, height / 2]])
60
+ base_center = np.array([[0.0, 0.0, -height / 2]])
61
+ vertices = np.vstack([base, apex, base_center]).astype(np.float64)
62
+
63
+ i = np.arange(n, dtype=np.int64)
64
+ j = (i + 1) % n
65
+
66
+ side = np.column_stack([i, j, np.full(n, n, dtype=np.int64)]) # normal outward+up
67
+ base_cap = np.column_stack([np.full(n, n + 1, dtype=np.int64), j, i]) # normal -z
68
+
69
+ faces = np.vstack([side, base_cap]).astype(np.int64)
70
+ return polyhedron(vertices=vertices, faces=faces)
@@ -0,0 +1,75 @@
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
+
12
+
13
+ @typechecked
14
+ def cuboid(size: float | Iterable[float]) -> Solid:
15
+ """Create a box (rectangular cuboid) centered at the origin.
16
+
17
+ Parameters
18
+ ----------
19
+ size : float | Iterable[float]
20
+ The dimensions of the cuboid as ``[width, depth, height]``.
21
+ If a single float is provided, it is broadcast to all three dimensions,
22
+ producing a cube. Missing values default to 0.
23
+
24
+ Returns
25
+ -------
26
+ Solid
27
+ A :class:`~scadpy.d3.solid.types.solid.Solid` object representing the cuboid,
28
+ centered at the origin.
29
+
30
+ Examples
31
+ --------
32
+ >>> from scadpy import cuboid
33
+
34
+ >>> cuboid(4) # doctest: +SKIP
35
+
36
+ .. render-example::
37
+ :name: cube
38
+ :example: cuboid(4)
39
+
40
+ >>> cuboid([4, 2, 1]) # doctest: +SKIP
41
+
42
+ .. render-example::
43
+ :name: cuboid
44
+ :example: cuboid([4, 2, 1])
45
+ """
46
+ from scadpy.d3 import resolve_vector_3d
47
+ from scadpy.d3.solid.primitives.polyhedron import polyhedron
48
+
49
+ w, d, h = resolve_vector_3d(size, 0) / 2
50
+
51
+ vertices = np.array(
52
+ [
53
+ [-w, -d, -h], # 0
54
+ [+w, -d, -h], # 1
55
+ [+w, +d, -h], # 2
56
+ [-w, +d, -h], # 3
57
+ [-w, -d, +h], # 4
58
+ [+w, -d, +h], # 5
59
+ [+w, +d, +h], # 6
60
+ [-w, +d, +h], # 7
61
+ ],
62
+ dtype=np.float64,
63
+ )
64
+ faces = np.array(
65
+ [
66
+ [0, 3, 2], [0, 2, 1], # bottom (normal -z)
67
+ [4, 5, 6], [4, 6, 7], # top (normal +z)
68
+ [0, 1, 5], [0, 5, 4], # front (normal -y)
69
+ [3, 7, 6], [3, 6, 2], # back (normal +y)
70
+ [0, 4, 7], [0, 7, 3], # left (normal -x)
71
+ [1, 2, 6], [1, 6, 5], # right (normal +x)
72
+ ],
73
+ dtype=np.int64,
74
+ )
75
+ return polyhedron(vertices=vertices, faces=faces)
@@ -0,0 +1,73 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import numpy as np
6
+ from typeguard import typechecked
7
+
8
+ if TYPE_CHECKING:
9
+ from scadpy.d3.solid import Solid
10
+
11
+
12
+ @typechecked
13
+ def cylinder(radius: float, height: float, section_count: int = 32) -> Solid:
14
+ """Create a cylinder centered at the origin, aligned along the z-axis.
15
+
16
+ Parameters
17
+ ----------
18
+ radius : float
19
+ The radius of the cylinder.
20
+ height : float
21
+ The total height of the cylinder.
22
+ section_count : int, optional
23
+ The number of sides of the polygonal approximation. Default is 32.
24
+
25
+ Returns
26
+ -------
27
+ Solid
28
+ A :class:`~scadpy.d3.solid.types.solid.Solid` object representing the
29
+ cylinder, centered at the origin.
30
+
31
+ Examples
32
+ --------
33
+ >>> from scadpy import cylinder
34
+
35
+ >>> cylinder(radius=2, height=4) # doctest: +SKIP
36
+
37
+ .. render-example::
38
+ :name: cylinder
39
+ :example: cylinder(radius=2, height=4)
40
+
41
+ >>> cylinder(radius=2, height=4, section_count=6) # doctest: +SKIP
42
+
43
+ .. render-example::
44
+ :name: hexagonal_cylinder
45
+ :example: cylinder(radius=2, height=4, section_count=6)
46
+ """
47
+ from scadpy.d3.solid.primitives.polyhedron import polyhedron
48
+
49
+ n = section_count
50
+ angles = np.linspace(0, 2 * np.pi, n, endpoint=False)
51
+ cos_a = np.cos(angles)
52
+ sin_a = np.sin(angles)
53
+
54
+ # Vertex layout:
55
+ # 0 .. n-1 bottom circle
56
+ # n .. 2n-1 top circle
57
+ # 2n bottom center
58
+ # 2n+1 top center
59
+ bottom = np.column_stack([radius * cos_a, radius * sin_a, np.full(n, -height / 2)])
60
+ top = np.column_stack([radius * cos_a, radius * sin_a, np.full(n, height / 2)])
61
+ centers = np.array([[0.0, 0.0, -height / 2], [0.0, 0.0, height / 2]])
62
+ vertices = np.vstack([bottom, top, centers]).astype(np.float64)
63
+
64
+ i = np.arange(n, dtype=np.int64)
65
+ j = (i + 1) % n
66
+
67
+ side1 = np.column_stack([i, j, n + j]) # side triangle A
68
+ side2 = np.column_stack([i, n + j, n + i]) # side triangle B
69
+ bot_cap = np.column_stack([np.full(n, 2 * n, dtype=np.int64), j, i]) # normal -z
70
+ top_cap = np.column_stack([np.full(n, 2 * n + 1, dtype=np.int64), n + i, n + j]) # normal +z
71
+
72
+ faces = np.vstack([side1, side2, bot_cap, top_cap]).astype(np.int64)
73
+ return polyhedron(vertices=vertices, faces=faces)
@@ -0,0 +1,60 @@
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 polyhedron(
16
+ vertices: NDArray[np.float64],
17
+ faces: NDArray[np.int64],
18
+ ) -> Solid:
19
+ """Create a solid from raw vertex coordinates and triangular face indices.
20
+
21
+ This is the base primitive constructor. All other solid primitives ultimately
22
+ call this function with numpy-computed geometry.
23
+
24
+ Parameters
25
+ ----------
26
+ vertices : NDArray[np.float64]
27
+ Vertex coordinates of shape ``(n, 3)``.
28
+ faces : NDArray[np.int64]
29
+ Triangle face indices of shape ``(m, 3)``. Each row contains the indices
30
+ of three vertices forming a triangle. Winding order follows the
31
+ right-hand rule: outward-pointing normals require counter-clockwise
32
+ vertex ordering when viewed from outside.
33
+
34
+ Returns
35
+ -------
36
+ Solid
37
+ A new solid built from the given geometry.
38
+
39
+ Examples
40
+ --------
41
+ >>> import numpy as np
42
+ >>> from scadpy import polyhedron
43
+
44
+ >>> polyhedron( # doctest: +SKIP
45
+ ... vertices=np.array([
46
+ ... [ 1.0, 1.0, 1.0],
47
+ ... [ 1.0, -1.0, -1.0],
48
+ ... [-1.0, 1.0, -1.0],
49
+ ... [-1.0, -1.0, 1.0],
50
+ ... ]),
51
+ ... faces=np.array([[0, 1, 2], [0, 2, 3], [0, 3, 1], [1, 3, 2]], dtype=np.int64),
52
+ ... )
53
+
54
+ .. render-example::
55
+ :name: polyhedron
56
+ :example: polyhedron(vertices=np.array([[1.0,1.0,1.0],[1.0,-1.0,-1.0],[-1.0,1.0,-1.0],[-1.0,-1.0,1.0]]), faces=np.array([[0,1,2],[0,2,3],[0,3,1],[1,3,2]], dtype=np.int64))
57
+ """
58
+ from scadpy.d3.solid import Solid
59
+
60
+ return Solid.from_geometry(Trimesh(vertices=vertices, faces=faces))
@@ -0,0 +1,58 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import numpy as np
6
+ from trimesh.creation import icosphere
7
+ from typeguard import typechecked
8
+
9
+ if TYPE_CHECKING:
10
+ from scadpy.d3.solid import Solid
11
+
12
+
13
+ @typechecked
14
+ def sphere(radius: float, subdivision_count: int = 4) -> Solid:
15
+ """Create a sphere approximated by an icosphere mesh.
16
+
17
+ Parameters
18
+ ----------
19
+ radius : float
20
+ The radius of the sphere. Must be strictly positive.
21
+ subdivision_count : int, optional
22
+ The number of subdivision iterations applied to the base icosahedron.
23
+ Higher values produce a smoother approximation. Default is 4.
24
+
25
+ Returns
26
+ -------
27
+ Solid
28
+ A :class:`~scadpy.d3.solid.types.solid.Solid` object representing the
29
+ approximated sphere, centered at the origin.
30
+
31
+ Notes
32
+ -----
33
+ - The sphere is always centered at the origin.
34
+ - Subdivision count of 4 produces 2562 vertices.
35
+
36
+ Examples
37
+ --------
38
+ >>> from scadpy import sphere
39
+
40
+ >>> sphere(radius=2) # doctest: +SKIP
41
+
42
+ .. render-example::
43
+ :name: sphere
44
+ :example: sphere(radius=2)
45
+
46
+ >>> sphere(radius=2, subdivision_count=1) # doctest: +SKIP
47
+
48
+ .. render-example::
49
+ :name: low_resolution_sphere
50
+ :example: sphere(radius=2, subdivision_count=1)
51
+ """
52
+ from scadpy.d3.solid.primitives.polyhedron import polyhedron
53
+
54
+ mesh = icosphere(radius=radius, subdivisions=subdivision_count)
55
+ return polyhedron(
56
+ vertices=mesh.vertices.astype(np.float64),
57
+ faces=mesh.faces.astype(np.int64),
58
+ )
@@ -0,0 +1,8 @@
1
+ __all__ = [
2
+ "get_solid_triangle_to_vertex",
3
+ "get_solid_vertex_coordinates",
4
+ "get_solid_vertex_to_part",
5
+ ]
6
+
7
+ from .triangle import get_solid_triangle_to_vertex
8
+ from .vertex import get_solid_vertex_coordinates, get_solid_vertex_to_part
@@ -0,0 +1,5 @@
1
+ __all__ = [
2
+ "get_solid_triangle_to_vertex",
3
+ ]
4
+
5
+ from .get_solid_triangle_to_vertex import get_solid_triangle_to_vertex
@@ -0,0 +1,49 @@
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.d3.solid import Solid
11
+
12
+
13
+ @typechecked
14
+ def get_solid_triangle_to_vertex(
15
+ solid: Solid,
16
+ ) -> NDArray[np.int64]:
17
+ """For each triangle in the solid, return the indices of its three vertices.
18
+
19
+ Parameters
20
+ ----------
21
+ solid : Solid
22
+ The solid to extract triangle-to-vertex mapping from.
23
+
24
+ Returns
25
+ -------
26
+ NDArray[np.int64]
27
+ 2D array of shape (n_triangles, 3), one row per triangle containing
28
+ the global vertex indices of its three corners.
29
+
30
+ Examples
31
+ --------
32
+ >>> from scadpy import cuboid, get_solid_triangle_to_vertex
33
+
34
+ >>> triangle_to_vertex = get_solid_triangle_to_vertex(cuboid(2))
35
+ >>> triangle_to_vertex.shape[1]
36
+ 3
37
+ """
38
+ if not solid._parts:
39
+ return np.empty((0, 3), dtype=np.int64)
40
+
41
+ part_vertex_counts = [len(part.geometry.vertices) for part in solid._parts]
42
+ offsets = np.concatenate([[0], np.cumsum(part_vertex_counts[:-1])])
43
+
44
+ return np.concatenate(
45
+ [
46
+ (part.geometry.faces + offset).astype(np.int64)
47
+ for part, offset in zip(solid._parts, offsets)
48
+ ]
49
+ )
@@ -0,0 +1,7 @@
1
+ __all__ = [
2
+ "get_solid_vertex_coordinates",
3
+ "get_solid_vertex_to_part",
4
+ ]
5
+
6
+ from .get_solid_vertex_coordinates import get_solid_vertex_coordinates
7
+ from .get_solid_vertex_to_part import get_solid_vertex_to_part