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.
- scadpy/__init__.py +5 -0
- scadpy/color/__init__.py +3 -0
- scadpy/color/constants/BEIGE.py +3 -0
- scadpy/color/constants/BLACK.py +3 -0
- scadpy/color/constants/BLUE.py +3 -0
- scadpy/color/constants/BROWN.py +3 -0
- scadpy/color/constants/DARK_GRAY.py +3 -0
- scadpy/color/constants/DEFAULT_COLOR.py +3 -0
- scadpy/color/constants/DEFAULT_OPACITY.py +1 -0
- scadpy/color/constants/GRAY.py +3 -0
- scadpy/color/constants/GREEN.py +3 -0
- scadpy/color/constants/ORANGE.py +3 -0
- scadpy/color/constants/RED.py +3 -0
- scadpy/color/constants/WHITE.py +3 -0
- scadpy/color/constants/YELLOW.py +3 -0
- scadpy/color/constants/__init__.py +29 -0
- scadpy/color/type/__init__.py +3 -0
- scadpy/color/type/color.py +3 -0
- scadpy/color/utils/__init__.py +3 -0
- scadpy/color/utils/get_random_color.py +36 -0
- scadpy/core/__init__.py +3 -0
- scadpy/core/assembly/__init__.py +5 -0
- scadpy/core/assembly/combinations/__init__.py +13 -0
- scadpy/core/assembly/combinations/concat_assemblies.py +50 -0
- scadpy/core/assembly/combinations/exclude_assemblies.py +135 -0
- scadpy/core/assembly/combinations/intersect_assemblies.py +128 -0
- scadpy/core/assembly/combinations/subtract_assemblies.py +151 -0
- scadpy/core/assembly/combinations/unify_assemblies.py +59 -0
- scadpy/core/assembly/topologies/__init__.py +41 -0
- scadpy/core/assembly/topologies/directed_edge/__init__.py +9 -0
- scadpy/core/assembly/topologies/directed_edge/get_assembly_directed_edge_directions.py +70 -0
- scadpy/core/assembly/topologies/directed_edge/get_assembly_directed_edge_to_edge.py +49 -0
- scadpy/core/assembly/topologies/directed_edge/get_assembly_directed_edge_to_vertex.py +54 -0
- scadpy/core/assembly/topologies/edge/__init__.py +9 -0
- scadpy/core/assembly/topologies/edge/get_assembly_edge_lengths.py +46 -0
- scadpy/core/assembly/topologies/edge/get_assembly_edge_midpoints.py +51 -0
- scadpy/core/assembly/topologies/edge/get_assembly_edge_normals.py +67 -0
- scadpy/core/assembly/topologies/face_corner/__init__.py +13 -0
- scadpy/core/assembly/topologies/face_corner/get_assembly_face_corner_angles.py +72 -0
- scadpy/core/assembly/topologies/face_corner/get_assembly_face_corner_normals.py +103 -0
- scadpy/core/assembly/topologies/face_corner/get_assembly_face_corner_to_incoming_directed_edge.py +65 -0
- scadpy/core/assembly/topologies/face_corner/get_assembly_face_corner_to_outgoing_directed_edge.py +65 -0
- scadpy/core/assembly/topologies/face_corner/get_assembly_face_directed_edge_to_corner.py +79 -0
- scadpy/core/assembly/topologies/part/__init__.py +5 -0
- scadpy/core/assembly/topologies/part/get_assembly_part_colors.py +55 -0
- scadpy/core/assembly/topologies/vertex/__init__.py +7 -0
- scadpy/core/assembly/topologies/vertex/get_assembly_vertex_coordinates.py +70 -0
- scadpy/core/assembly/topologies/vertex/get_assembly_vertex_to_part.py +62 -0
- scadpy/core/assembly/transformations/__init__.py +19 -0
- scadpy/core/assembly/transformations/color_assembly.py +24 -0
- scadpy/core/assembly/transformations/mirror_vertex_coordinates.py +68 -0
- scadpy/core/assembly/transformations/pull_vertex_coordinates.py +64 -0
- scadpy/core/assembly/transformations/push_vertex_coordinates.py +64 -0
- scadpy/core/assembly/transformations/resize_vertex_coordinates.py +121 -0
- scadpy/core/assembly/transformations/rotate_vertex_coordinates.py +73 -0
- scadpy/core/assembly/transformations/scale_vertex_coordinates.py +76 -0
- scadpy/core/assembly/transformations/translate_vertex_coordinates.py +70 -0
- scadpy/core/assembly/types/__init__.py +7 -0
- scadpy/core/assembly/types/assembly.py +14 -0
- scadpy/core/assembly/types/topology_filter.py +6 -0
- scadpy/core/assembly/utils/__init__.py +9 -0
- scadpy/core/assembly/utils/lookup_pairs.py +56 -0
- scadpy/core/assembly/utils/resolve_topology_filter.py +84 -0
- scadpy/core/assembly/utils/transform_filtered_parts.py +55 -0
- scadpy/core/component/__init__.py +3 -0
- scadpy/core/component/exporters/__init__.py +7 -0
- scadpy/core/component/exporters/map_component_to_html_file.py +47 -0
- scadpy/core/component/exporters/map_component_to_screen.py +63 -0
- scadpy/core/component/features/__init__.py +5 -0
- scadpy/core/component/features/get_component_bounds.py +38 -0
- scadpy/core/component/utils/__init__.py +9 -0
- scadpy/core/component/utils/blend_component_colors.py +77 -0
- scadpy/core/component/utils/get_intersecting_component_index_groups.py +108 -0
- scadpy/core/part/__init__.py +3 -0
- scadpy/core/part/combinations/__init__.py +11 -0
- scadpy/core/part/combinations/concat_parts.py +48 -0
- scadpy/core/part/combinations/intersect_parts.py +147 -0
- scadpy/core/part/combinations/subtract_parts.py +94 -0
- scadpy/core/part/combinations/unify_parts.py +143 -0
- scadpy/core/part/types/__init__.py +5 -0
- scadpy/core/part/types/part.py +34 -0
- scadpy/core/part/utils/__init__.py +5 -0
- scadpy/core/part/utils/blend_part_colors.py +32 -0
- scadpy/d2/__init__.py +2 -0
- scadpy/d2/shape/__init__.py +9 -0
- scadpy/d2/shape/combinations/__init__.py +21 -0
- scadpy/d2/shape/combinations/are_shape_parts_intersecting.py +49 -0
- scadpy/d2/shape/combinations/concat_shape.py +48 -0
- scadpy/d2/shape/combinations/exclude_shape.py +71 -0
- scadpy/d2/shape/combinations/intersect_shape.py +64 -0
- scadpy/d2/shape/combinations/intersect_shape_parts.py +71 -0
- scadpy/d2/shape/combinations/subtract_shape.py +72 -0
- scadpy/d2/shape/combinations/subtract_shape_parts.py +66 -0
- scadpy/d2/shape/combinations/unify_shape.py +51 -0
- scadpy/d2/shape/combinations/unify_shape_parts.py +74 -0
- scadpy/d2/shape/exporters/__init__.py +17 -0
- scadpy/d2/shape/exporters/map_shape_to_dxf.py +43 -0
- scadpy/d2/shape/exporters/map_shape_to_dxf_file.py +38 -0
- scadpy/d2/shape/exporters/map_shape_to_html.py +117 -0
- scadpy/d2/shape/exporters/map_shape_to_html_file.py +58 -0
- scadpy/d2/shape/exporters/map_shape_to_screen.py +51 -0
- scadpy/d2/shape/exporters/map_shape_to_svg.py +40 -0
- scadpy/d2/shape/exporters/map_shape_to_svg_file.py +38 -0
- scadpy/d2/shape/features/__init__.py +9 -0
- scadpy/d2/shape/features/get_shape_bounds.py +40 -0
- scadpy/d2/shape/features/get_shape_part_bounds.py +37 -0
- scadpy/d2/shape/features/is_shape_empty.py +38 -0
- scadpy/d2/shape/importers/__init__.py +13 -0
- scadpy/d2/shape/importers/map_dxf_to_shape.py +55 -0
- scadpy/d2/shape/importers/map_geometries_to_shape.py +45 -0
- scadpy/d2/shape/importers/map_geometry_to_shape.py +43 -0
- scadpy/d2/shape/importers/map_parts_to_shape.py +62 -0
- scadpy/d2/shape/importers/map_svg_to_shape.py +55 -0
- scadpy/d2/shape/primitives/__init__.py +6 -0
- scadpy/d2/shape/primitives/circle.py +68 -0
- scadpy/d2/shape/primitives/polygon.py +86 -0
- scadpy/d2/shape/primitives/rectangle.py +85 -0
- scadpy/d2/shape/primitives/square.py +57 -0
- scadpy/d2/shape/topologies/__init__.py +53 -0
- scadpy/d2/shape/topologies/corner/__init__.py +15 -0
- scadpy/d2/shape/topologies/corner/are_shape_corners_convex.py +75 -0
- scadpy/d2/shape/topologies/corner/get_shape_corner_angles.py +58 -0
- scadpy/d2/shape/topologies/corner/get_shape_corner_normals.py +82 -0
- scadpy/d2/shape/topologies/corner/get_shape_corner_to_incoming_directed_edge.py +39 -0
- scadpy/d2/shape/topologies/corner/get_shape_corner_to_outgoing_directed_edge.py +39 -0
- scadpy/d2/shape/topologies/corner/get_shape_corner_to_vertex.py +65 -0
- scadpy/d2/shape/topologies/directed_edge/__init__.py +11 -0
- scadpy/d2/shape/topologies/directed_edge/get_shape_directed_edge_directions.py +44 -0
- scadpy/d2/shape/topologies/directed_edge/get_shape_directed_edge_to_corner.py +41 -0
- scadpy/d2/shape/topologies/directed_edge/get_shape_directed_edge_to_edge.py +51 -0
- scadpy/d2/shape/topologies/directed_edge/get_shape_directed_edge_to_vertex.py +63 -0
- scadpy/d2/shape/topologies/edge/__init__.py +11 -0
- scadpy/d2/shape/topologies/edge/get_shape_edge_lengths.py +43 -0
- scadpy/d2/shape/topologies/edge/get_shape_edge_midpoints.py +46 -0
- scadpy/d2/shape/topologies/edge/get_shape_edge_normals.py +40 -0
- scadpy/d2/shape/topologies/edge/get_shape_edge_to_vertex.py +71 -0
- scadpy/d2/shape/topologies/ring/__init__.py +7 -0
- scadpy/d2/shape/topologies/ring/get_shape_ring_to_part.py +46 -0
- scadpy/d2/shape/topologies/ring/get_shape_ring_types.py +46 -0
- scadpy/d2/shape/topologies/vertex/__init__.py +11 -0
- scadpy/d2/shape/topologies/vertex/get_shape_part_vertex_coordinates.py +62 -0
- scadpy/d2/shape/topologies/vertex/get_shape_vertex_coordinates.py +44 -0
- scadpy/d2/shape/topologies/vertex/get_shape_vertex_to_part.py +42 -0
- scadpy/d2/shape/topologies/vertex/get_shape_vertex_to_ring.py +63 -0
- scadpy/d2/shape/transformations/__init__.py +43 -0
- scadpy/d2/shape/transformations/chamfer_shape.py +259 -0
- scadpy/d2/shape/transformations/color_shape.py +46 -0
- scadpy/d2/shape/transformations/convexify_shape.py +79 -0
- scadpy/d2/shape/transformations/fill_shape.py +68 -0
- scadpy/d2/shape/transformations/fillet_shape.py +289 -0
- scadpy/d2/shape/transformations/grow_shape.py +82 -0
- scadpy/d2/shape/transformations/linear_cut_shape.py +116 -0
- scadpy/d2/shape/transformations/linear_extrude_shape.py +60 -0
- scadpy/d2/shape/transformations/linear_slice_shape.py +144 -0
- scadpy/d2/shape/transformations/mirror_shape.py +53 -0
- scadpy/d2/shape/transformations/pull_shape.py +67 -0
- scadpy/d2/shape/transformations/push_shape.py +67 -0
- scadpy/d2/shape/transformations/radial_extrude_shape.py +285 -0
- scadpy/d2/shape/transformations/radial_slice_shape.py +132 -0
- scadpy/d2/shape/transformations/recoordinate_shape.py +82 -0
- scadpy/d2/shape/transformations/resize_shape.py +91 -0
- scadpy/d2/shape/transformations/rotate_shape.py +63 -0
- scadpy/d2/shape/transformations/scale_shape.py +58 -0
- scadpy/d2/shape/transformations/shrink_shape.py +69 -0
- scadpy/d2/shape/transformations/translate_shape.py +54 -0
- scadpy/d2/shape/types/__init__.py +3 -0
- scadpy/d2/shape/types/shape.py +792 -0
- scadpy/d2/shape/types/utils/__init__.py +5 -0
- scadpy/d2/shape/types/utils/shapely_base_geometry_to_shapely_polygons.py +25 -0
- scadpy/d2/shape/utils/__init__.py +5 -0
- scadpy/d2/shape/utils/shapely_base_geometry_to_shapely_polygons.py +55 -0
- scadpy/d2/utils/__init__.py +3 -0
- scadpy/d2/utils/resolve_vector_2d.py +50 -0
- scadpy/d3/__init__.py +2 -0
- scadpy/d3/solid/__init__.py +8 -0
- scadpy/d3/solid/combinations/__init__.py +21 -0
- scadpy/d3/solid/combinations/are_solid_parts_intersecting.py +51 -0
- scadpy/d3/solid/combinations/concat_solid.py +48 -0
- scadpy/d3/solid/combinations/exclude_solid.py +71 -0
- scadpy/d3/solid/combinations/intersect_solid.py +64 -0
- scadpy/d3/solid/combinations/intersect_solid_parts.py +73 -0
- scadpy/d3/solid/combinations/subtract_solid.py +72 -0
- scadpy/d3/solid/combinations/subtract_solid_parts.py +68 -0
- scadpy/d3/solid/combinations/unify_solid.py +51 -0
- scadpy/d3/solid/combinations/unify_solid_parts.py +73 -0
- scadpy/d3/solid/exporters/__init__.py +11 -0
- scadpy/d3/solid/exporters/map_solid_to_html.py +318 -0
- scadpy/d3/solid/exporters/map_solid_to_html_file.py +58 -0
- scadpy/d3/solid/exporters/map_solid_to_screen.py +51 -0
- scadpy/d3/solid/exporters/map_solid_to_stl_file.py +48 -0
- scadpy/d3/solid/features/__init__.py +11 -0
- scadpy/d3/solid/features/get_solid_bounds.py +37 -0
- scadpy/d3/solid/features/get_solid_part_bounds.py +37 -0
- scadpy/d3/solid/features/get_solid_part_colors.py +39 -0
- scadpy/d3/solid/features/is_solid_empty.py +36 -0
- scadpy/d3/solid/importers/__init__.py +11 -0
- scadpy/d3/solid/importers/map_geometries_to_solid.py +42 -0
- scadpy/d3/solid/importers/map_geometry_to_solid.py +42 -0
- scadpy/d3/solid/importers/map_parts_to_solid.py +66 -0
- scadpy/d3/solid/importers/map_stl_to_solid.py +37 -0
- scadpy/d3/solid/primitives/__init__.py +7 -0
- scadpy/d3/solid/primitives/cone.py +70 -0
- scadpy/d3/solid/primitives/cuboid.py +75 -0
- scadpy/d3/solid/primitives/cylinder.py +73 -0
- scadpy/d3/solid/primitives/polyhedron.py +60 -0
- scadpy/d3/solid/primitives/sphere.py +58 -0
- scadpy/d3/solid/topologies/__init__.py +8 -0
- scadpy/d3/solid/topologies/triangle/__init__.py +5 -0
- scadpy/d3/solid/topologies/triangle/get_solid_triangle_to_vertex.py +49 -0
- scadpy/d3/solid/topologies/vertex/__init__.py +7 -0
- scadpy/d3/solid/topologies/vertex/get_solid_vertex_coordinates.py +39 -0
- scadpy/d3/solid/topologies/vertex/get_solid_vertex_to_part.py +37 -0
- scadpy/d3/solid/transformations/__init__.py +23 -0
- scadpy/d3/solid/transformations/color_solid.py +46 -0
- scadpy/d3/solid/transformations/convexify_solid.py +64 -0
- scadpy/d3/solid/transformations/mirror_solid.py +53 -0
- scadpy/d3/solid/transformations/pull_solid.py +67 -0
- scadpy/d3/solid/transformations/push_solid.py +67 -0
- scadpy/d3/solid/transformations/recoordinate_solid.py +68 -0
- scadpy/d3/solid/transformations/resize_solid.py +92 -0
- scadpy/d3/solid/transformations/rotate_solid.py +93 -0
- scadpy/d3/solid/transformations/scale_solid.py +58 -0
- scadpy/d3/solid/transformations/translate_solid.py +54 -0
- scadpy/d3/solid/types/__init__.py +3 -0
- scadpy/d3/solid/types/solid.py +448 -0
- scadpy/d3/utils/__init__.py +3 -0
- scadpy/d3/utils/resolve_vector_3d.py +50 -0
- scadpy/utils/__init__.py +6 -0
- scadpy/utils/resolve_vector.py +64 -0
- scadpy/utils/x.py +38 -0
- scadpy/utils/y.py +38 -0
- scadpy/utils/z.py +38 -0
- scadpy-0.1.0.dist-info/METADATA +282 -0
- scadpy-0.1.0.dist-info/RECORD +236 -0
- scadpy-0.1.0.dist-info/WHEEL +4 -0
- 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
|
+
)
|