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,75 @@
|
|
|
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.d2.shape import Shape
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@typechecked
|
|
14
|
+
def are_shape_corners_convex(
|
|
15
|
+
shape: Shape,
|
|
16
|
+
) -> NDArray[np.bool_]:
|
|
17
|
+
"""
|
|
18
|
+
For each corner in the shape, return whether it is convex.
|
|
19
|
+
|
|
20
|
+
A corner is convex if the shape turns left at that vertex on an exterior
|
|
21
|
+
ring (counter-clockwise winding). Concave corners turn right. Interior
|
|
22
|
+
rings have their orientation inverted accordingly.
|
|
23
|
+
|
|
24
|
+
Use :func:`get_shape_corner_angles` to get the magnitude of the turning
|
|
25
|
+
angle independently of convexity.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
shape : Shape
|
|
30
|
+
The shape to extract corner convexity from.
|
|
31
|
+
|
|
32
|
+
Returns
|
|
33
|
+
-------
|
|
34
|
+
NDArray[np.bool_]
|
|
35
|
+
1D boolean array of shape (n_corners,). True if convex, False if concave.
|
|
36
|
+
|
|
37
|
+
Examples
|
|
38
|
+
--------
|
|
39
|
+
>>> from scadpy import are_shape_corners_convex, polygon
|
|
40
|
+
|
|
41
|
+
>>> # arrow: 5 convex corners (tip and sides)
|
|
42
|
+
>>> # + 2 concave corners (the tail notch)
|
|
43
|
+
>>> arrow = polygon(
|
|
44
|
+
... [(0, 1), (3, 0), (5, 2), (3, 4),
|
|
45
|
+
... (0, 3), (1, 2.5), (1, 1.5)]
|
|
46
|
+
... )
|
|
47
|
+
>>> are_shape_corners_convex(arrow).tolist()
|
|
48
|
+
[True, True, True, True, True, False, False]
|
|
49
|
+
"""
|
|
50
|
+
corner_to_vertex = shape.corner_to_vertex
|
|
51
|
+
coords = shape.vertex_coordinates
|
|
52
|
+
|
|
53
|
+
if len(corner_to_vertex) == 0:
|
|
54
|
+
return np.empty(0, dtype=np.bool_)
|
|
55
|
+
|
|
56
|
+
prev_coords = coords[corner_to_vertex[:, 0]]
|
|
57
|
+
curr_coords = coords[corner_to_vertex[:, 1]]
|
|
58
|
+
next_coords = coords[corner_to_vertex[:, 2]]
|
|
59
|
+
|
|
60
|
+
v_in = curr_coords - prev_coords
|
|
61
|
+
v_out = next_coords - curr_coords
|
|
62
|
+
|
|
63
|
+
# 2D cross product: positive = left turn (convex on CCW ring)
|
|
64
|
+
cross = v_in[:, 0] * v_out[:, 1] - v_in[:, 1] * v_out[:, 0]
|
|
65
|
+
|
|
66
|
+
is_convex = cross > 0
|
|
67
|
+
|
|
68
|
+
# interior rings (CW in shapely) have inverted orientation — flip convexity
|
|
69
|
+
ring_types = shape.ring_types
|
|
70
|
+
vertex_to_ring = shape.vertex_to_ring
|
|
71
|
+
corner_ring_indices = vertex_to_ring[corner_to_vertex[:, 1]]
|
|
72
|
+
is_interior = ring_types[corner_ring_indices] == "interior"
|
|
73
|
+
is_convex = np.where(is_interior, ~is_convex, is_convex)
|
|
74
|
+
|
|
75
|
+
return is_convex
|
|
@@ -0,0 +1,58 @@
|
|
|
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.d2.shape import Shape
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@typechecked
|
|
14
|
+
def get_shape_corner_angles(
|
|
15
|
+
shape: Shape,
|
|
16
|
+
) -> NDArray[np.float64]:
|
|
17
|
+
"""
|
|
18
|
+
For each corner in the shape, return its interior angle in degrees.
|
|
19
|
+
|
|
20
|
+
The angle is always positive, in the range (0°, 180°). It represents
|
|
21
|
+
the turning angle at the corner, regardless of whether the corner is
|
|
22
|
+
convex or concave. Use :func:`are_shape_corners_convex` to distinguish
|
|
23
|
+
convex corners from concave ones.
|
|
24
|
+
|
|
25
|
+
The angle is computed as the absolute value of the signed angle from the
|
|
26
|
+
incoming edge to the outgoing edge at each corner, using the 2D cross
|
|
27
|
+
product to determine orientation.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
shape : Shape
|
|
32
|
+
The shape to extract corner angles from.
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
NDArray[np.float64]
|
|
37
|
+
1D array of shape (n_corners,), one angle per corner, in degrees.
|
|
38
|
+
All values are in the range (0°, 180°).
|
|
39
|
+
|
|
40
|
+
Examples
|
|
41
|
+
--------
|
|
42
|
+
>>> from scadpy import get_shape_corner_angles, polygon
|
|
43
|
+
|
|
44
|
+
>>> # arrow: 5 convex corners + 2 concave corners,
|
|
45
|
+
>>> # all with different angles
|
|
46
|
+
>>> arrow = polygon(
|
|
47
|
+
... [(0, 1), (3, 0), (5, 2), (3, 4),
|
|
48
|
+
... (0, 3), (1, 2.5), (1, 1.5)]
|
|
49
|
+
... )
|
|
50
|
+
>>> get_shape_corner_angles(arrow).round(2).tolist()
|
|
51
|
+
[135.0, 63.43, 90.0, 63.43, 135.0, 63.43, 63.43]
|
|
52
|
+
"""
|
|
53
|
+
from scadpy.core.assembly import get_assembly_face_corner_angles
|
|
54
|
+
|
|
55
|
+
return get_assembly_face_corner_angles(
|
|
56
|
+
corner_to_vertex=shape.corner_to_vertex,
|
|
57
|
+
vertex_coordinates=shape.vertex_coordinates,
|
|
58
|
+
)
|
|
@@ -0,0 +1,82 @@
|
|
|
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.d2.shape import Shape
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@typechecked
|
|
14
|
+
def get_shape_corner_normals(
|
|
15
|
+
shape: Shape,
|
|
16
|
+
epsilon: float = 1e-10,
|
|
17
|
+
) -> NDArray[np.float64]:
|
|
18
|
+
"""
|
|
19
|
+
For each corner in the shape, return its outward unit normal.
|
|
20
|
+
|
|
21
|
+
The normal is the bisector of the outward edge normals at the corner,
|
|
22
|
+
oriented to point away from the filled material. It points outward for
|
|
23
|
+
convex corners and inward for concave ones, consistently with
|
|
24
|
+
:func:`are_shape_corners_convex`.
|
|
25
|
+
|
|
26
|
+
Each edge normal is the 90° CW rotation of the edge direction, which
|
|
27
|
+
points outward for CCW (exterior) rings. The bisector is then the
|
|
28
|
+
normalized average of the two adjacent edge normals. Its sign is
|
|
29
|
+
corrected using :func:`are_shape_corners_convex`, which already accounts
|
|
30
|
+
for ring orientation (interior vs exterior), so no separate handling
|
|
31
|
+
is needed here.
|
|
32
|
+
|
|
33
|
+
For degenerate 180° corners (straight edges) where the bisector
|
|
34
|
+
vanishes, the normal falls back to the outward edge normal of the
|
|
35
|
+
incoming edge (90° CW rotation).
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
shape : Shape
|
|
40
|
+
The shape to extract corner normals from.
|
|
41
|
+
epsilon : float, optional
|
|
42
|
+
Threshold below which the bisector norm is considered degenerate
|
|
43
|
+
(straight 180° corner). Defaults to ``1e-10``.
|
|
44
|
+
|
|
45
|
+
Returns
|
|
46
|
+
-------
|
|
47
|
+
NDArray[np.float64]
|
|
48
|
+
2D array of shape (n_corners, 2). Each row is a unit vector ``[nx, ny]``.
|
|
49
|
+
|
|
50
|
+
Examples
|
|
51
|
+
--------
|
|
52
|
+
>>> from scadpy import (
|
|
53
|
+
... get_shape_corner_normals,
|
|
54
|
+
... are_shape_corners_convex,
|
|
55
|
+
... polygon,
|
|
56
|
+
... )
|
|
57
|
+
|
|
58
|
+
>>> # arrow: 5 convex corners (normals point outward)
|
|
59
|
+
>>> # + 2 concave corners (normals point inward,
|
|
60
|
+
>>> # into the tail notch — x component is positive)
|
|
61
|
+
>>> arrow = polygon(
|
|
62
|
+
... [(0, 1), (3, 0), (5, 2), (3, 4),
|
|
63
|
+
... (0, 3), (1, 2.5), (1, 1.5)]
|
|
64
|
+
... )
|
|
65
|
+
>>> normals = get_shape_corner_normals(arrow)
|
|
66
|
+
>>> normals.round(4) # doctest: +NORMALIZE_WHITESPACE
|
|
67
|
+
array([[-0.9975, -0.0709],
|
|
68
|
+
[ 0.2298, -0.9732],
|
|
69
|
+
[ 1. , 0. ],
|
|
70
|
+
[ 0.2298, 0.9732],
|
|
71
|
+
[-0.9975, 0.0709],
|
|
72
|
+
[ 0.8507, 0.5257],
|
|
73
|
+
[ 0.8507, -0.5257]])
|
|
74
|
+
"""
|
|
75
|
+
from scadpy.core.assembly import get_assembly_face_corner_normals
|
|
76
|
+
|
|
77
|
+
return get_assembly_face_corner_normals(
|
|
78
|
+
corner_to_vertex=shape.corner_to_vertex,
|
|
79
|
+
vertex_coordinates=shape.vertex_coordinates,
|
|
80
|
+
are_corners_convex=shape.are_corners_convex,
|
|
81
|
+
epsilon=epsilon,
|
|
82
|
+
)
|
|
@@ -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.d2.shape import Shape
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@typechecked
|
|
14
|
+
def get_shape_corner_to_incoming_directed_edge(
|
|
15
|
+
shape: Shape,
|
|
16
|
+
) -> NDArray[np.int64]:
|
|
17
|
+
"""
|
|
18
|
+
For each corner in the shape, return the index of its incoming directed edge.
|
|
19
|
+
|
|
20
|
+
See :func:`get_assembly_face_corner_to_incoming_directed_edge` for full documentation.
|
|
21
|
+
|
|
22
|
+
Examples
|
|
23
|
+
--------
|
|
24
|
+
>>> from scadpy import (
|
|
25
|
+
... get_shape_corner_to_incoming_directed_edge, polygon
|
|
26
|
+
... )
|
|
27
|
+
|
|
28
|
+
>>> # triangle: corners (2,0,1), (0,1,2), (1,2,0)
|
|
29
|
+
>>> # incoming: 2→0, 0→1, 1→2
|
|
30
|
+
>>> triangle = polygon([(0, 0), (1, 0), (0.5, 1)])
|
|
31
|
+
>>> get_shape_corner_to_incoming_directed_edge(triangle)
|
|
32
|
+
array([4, 0, 2])
|
|
33
|
+
"""
|
|
34
|
+
from scadpy.core.assembly import get_assembly_face_corner_to_incoming_directed_edge
|
|
35
|
+
|
|
36
|
+
return get_assembly_face_corner_to_incoming_directed_edge(
|
|
37
|
+
corner_to_vertex=shape.corner_to_vertex,
|
|
38
|
+
directed_edge_to_vertex=shape.directed_edge_to_vertex,
|
|
39
|
+
)
|
|
@@ -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.d2.shape import Shape
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@typechecked
|
|
14
|
+
def get_shape_corner_to_outgoing_directed_edge(
|
|
15
|
+
shape: Shape,
|
|
16
|
+
) -> NDArray[np.int64]:
|
|
17
|
+
"""
|
|
18
|
+
For each corner in the shape, return the index of its outgoing directed edge.
|
|
19
|
+
|
|
20
|
+
See :func:`get_assembly_face_corner_to_outgoing_directed_edge` for full documentation.
|
|
21
|
+
|
|
22
|
+
Examples
|
|
23
|
+
--------
|
|
24
|
+
>>> from scadpy import (
|
|
25
|
+
... get_shape_corner_to_outgoing_directed_edge, polygon
|
|
26
|
+
... )
|
|
27
|
+
|
|
28
|
+
>>> # triangle: corners (2,0,1), (0,1,2), (1,2,0)
|
|
29
|
+
>>> # outgoing: 0→1, 1→2, 2→0
|
|
30
|
+
>>> triangle = polygon([(0, 0), (1, 0), (0.5, 1)])
|
|
31
|
+
>>> get_shape_corner_to_outgoing_directed_edge(triangle)
|
|
32
|
+
array([0, 2, 4])
|
|
33
|
+
"""
|
|
34
|
+
from scadpy.core.assembly import get_assembly_face_corner_to_outgoing_directed_edge
|
|
35
|
+
|
|
36
|
+
return get_assembly_face_corner_to_outgoing_directed_edge(
|
|
37
|
+
corner_to_vertex=shape.corner_to_vertex,
|
|
38
|
+
directed_edge_to_vertex=shape.directed_edge_to_vertex,
|
|
39
|
+
)
|
|
@@ -0,0 +1,65 @@
|
|
|
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.d2.shape import Shape
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@typechecked
|
|
14
|
+
def get_shape_corner_to_vertex(
|
|
15
|
+
shape: Shape,
|
|
16
|
+
) -> NDArray[np.int64]:
|
|
17
|
+
"""
|
|
18
|
+
For each corner in the shape, return its three vertex indices (prev, curr, next).
|
|
19
|
+
|
|
20
|
+
A corner is defined by three consecutive vertices on a ring. Each ring of ``n``
|
|
21
|
+
vertices yields ``n`` corners (the ring is treated as cyclic).
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
shape : Shape
|
|
26
|
+
The shape to extract corner vertex indices from.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
NDArray[np.int64]
|
|
31
|
+
2D array of shape (n_corners, 3). Each row contains the indices
|
|
32
|
+
``[prev, curr, next]`` into the shape's global vertex array.
|
|
33
|
+
|
|
34
|
+
Examples
|
|
35
|
+
--------
|
|
36
|
+
>>> from scadpy import get_shape_corner_to_vertex, polygon
|
|
37
|
+
|
|
38
|
+
>>> triangle = polygon([(0, 0), (1, 0), (0.5, 1)])
|
|
39
|
+
>>> get_shape_corner_to_vertex( # doctest: +NORMALIZE_WHITESPACE
|
|
40
|
+
... triangle
|
|
41
|
+
... )
|
|
42
|
+
array([[2, 0, 1],
|
|
43
|
+
[0, 1, 2],
|
|
44
|
+
[1, 2, 0]])
|
|
45
|
+
"""
|
|
46
|
+
vertex_to_ring = shape.vertex_to_ring
|
|
47
|
+
|
|
48
|
+
if len(vertex_to_ring) == 0:
|
|
49
|
+
return np.empty((0, 3), dtype=np.int64)
|
|
50
|
+
|
|
51
|
+
indices = np.arange(len(vertex_to_ring), dtype=np.int64)
|
|
52
|
+
|
|
53
|
+
# for each vertex, prev and next are within the same ring (cyclic)
|
|
54
|
+
# shift by +1 and -1 within each ring using modular arithmetic per ring
|
|
55
|
+
ring_starts = np.searchsorted(vertex_to_ring, np.arange(vertex_to_ring[-1] + 1))
|
|
56
|
+
ring_sizes = np.diff(np.append(ring_starts, len(vertex_to_ring)))
|
|
57
|
+
|
|
58
|
+
# offset of each vertex within its ring
|
|
59
|
+
offsets = indices - ring_starts[vertex_to_ring]
|
|
60
|
+
ring_size_per_vertex = ring_sizes[vertex_to_ring]
|
|
61
|
+
|
|
62
|
+
prev_indices = ring_starts[vertex_to_ring] + (offsets - 1) % ring_size_per_vertex
|
|
63
|
+
next_indices = ring_starts[vertex_to_ring] + (offsets + 1) % ring_size_per_vertex
|
|
64
|
+
|
|
65
|
+
return np.stack([prev_indices, indices, next_indices], axis=1)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
"get_shape_directed_edge_directions",
|
|
3
|
+
"get_shape_directed_edge_to_corner",
|
|
4
|
+
"get_shape_directed_edge_to_edge",
|
|
5
|
+
"get_shape_directed_edge_to_vertex",
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
from .get_shape_directed_edge_directions import get_shape_directed_edge_directions
|
|
9
|
+
from .get_shape_directed_edge_to_corner import get_shape_directed_edge_to_corner
|
|
10
|
+
from .get_shape_directed_edge_to_edge import get_shape_directed_edge_to_edge
|
|
11
|
+
from .get_shape_directed_edge_to_vertex import get_shape_directed_edge_to_vertex
|
|
@@ -0,0 +1,44 @@
|
|
|
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.d2.shape import Shape
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@typechecked
|
|
14
|
+
def get_shape_directed_edge_directions(
|
|
15
|
+
shape: Shape,
|
|
16
|
+
) -> NDArray[np.float64]:
|
|
17
|
+
"""
|
|
18
|
+
For each directed edge in the shape, return its unit direction vector.
|
|
19
|
+
|
|
20
|
+
See :func:`get_assembly_directed_edge_directions` for full documentation.
|
|
21
|
+
|
|
22
|
+
Examples
|
|
23
|
+
--------
|
|
24
|
+
>>> from scadpy import get_shape_directed_edge_directions, square
|
|
25
|
+
|
|
26
|
+
>>> # unit square: 4 edges → 8 directed edges,
|
|
27
|
+
>>> # forward then backward interleaved
|
|
28
|
+
>>> square_shape = square(1)
|
|
29
|
+
>>> get_shape_directed_edge_directions(square_shape).round(4)
|
|
30
|
+
array([[ 1., 0.],
|
|
31
|
+
[-1., 0.],
|
|
32
|
+
[ 0., 1.],
|
|
33
|
+
[ 0., -1.],
|
|
34
|
+
[-1., 0.],
|
|
35
|
+
[ 1., 0.],
|
|
36
|
+
[ 0., -1.],
|
|
37
|
+
[ 0., 1.]])
|
|
38
|
+
"""
|
|
39
|
+
from scadpy.core.assembly import get_assembly_directed_edge_directions
|
|
40
|
+
|
|
41
|
+
return get_assembly_directed_edge_directions(
|
|
42
|
+
directed_edge_to_vertex=shape.directed_edge_to_vertex,
|
|
43
|
+
vertex_coordinates=shape.vertex_coordinates,
|
|
44
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
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.d2.shape import Shape
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@typechecked
|
|
14
|
+
def get_shape_directed_edge_to_corner(
|
|
15
|
+
shape: Shape,
|
|
16
|
+
) -> NDArray[np.int64]:
|
|
17
|
+
"""
|
|
18
|
+
For each directed edge in the shape, return its source and target corner indices.
|
|
19
|
+
|
|
20
|
+
See :func:`get_assembly_face_directed_edge_to_corner` for full documentation.
|
|
21
|
+
|
|
22
|
+
Examples
|
|
23
|
+
--------
|
|
24
|
+
>>> from scadpy import get_shape_directed_edge_to_corner, polygon
|
|
25
|
+
|
|
26
|
+
>>> # triangle: corners (2,0,1)=0, (0,1,2)=1, (1,2,0)=2
|
|
27
|
+
>>> triangle = polygon([(0, 0), (1, 0), (0.5, 1)])
|
|
28
|
+
>>> get_shape_directed_edge_to_corner(triangle)
|
|
29
|
+
array([[0, 1],
|
|
30
|
+
[1, 0],
|
|
31
|
+
[1, 2],
|
|
32
|
+
[2, 1],
|
|
33
|
+
[2, 0],
|
|
34
|
+
[0, 2]])
|
|
35
|
+
"""
|
|
36
|
+
from scadpy.core.assembly import get_assembly_face_directed_edge_to_corner
|
|
37
|
+
|
|
38
|
+
return get_assembly_face_directed_edge_to_corner(
|
|
39
|
+
corner_to_outgoing_directed_edge=shape.corner_to_outgoing_directed_edge,
|
|
40
|
+
corner_to_incoming_directed_edge=shape.corner_to_incoming_directed_edge,
|
|
41
|
+
)
|
|
@@ -0,0 +1,51 @@
|
|
|
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.d2.shape import Shape
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@typechecked
|
|
14
|
+
def get_shape_directed_edge_to_edge(
|
|
15
|
+
shape: Shape,
|
|
16
|
+
) -> NDArray[np.int64]:
|
|
17
|
+
"""
|
|
18
|
+
For each directed edge in the shape, return the index of its parent undirected edge.
|
|
19
|
+
|
|
20
|
+
Since directed edges are interleaved (``directed_edge 2i`` and
|
|
21
|
+
``directed_edge 2i+1`` both belong to ``edge i``), the mapping is:
|
|
22
|
+
``edge index = directed_edge index // 2``.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
shape : Shape
|
|
27
|
+
The shape to extract directed edge-to-edge indices from.
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
NDArray[np.int64]
|
|
32
|
+
1D array of shape ``(2 * n_edges,)``. Each entry is the index of
|
|
33
|
+
the parent undirected edge.
|
|
34
|
+
|
|
35
|
+
Examples
|
|
36
|
+
--------
|
|
37
|
+
>>> from scadpy import get_shape_directed_edge_to_edge, polygon, square
|
|
38
|
+
|
|
39
|
+
>>> # triangle: 3 edges → 6 directed edges
|
|
40
|
+
>>> triangle = polygon([(0, 0), (1, 0), (0.5, 1)])
|
|
41
|
+
>>> get_shape_directed_edge_to_edge(triangle)
|
|
42
|
+
array([0, 0, 1, 1, 2, 2])
|
|
43
|
+
|
|
44
|
+
>>> # square: 4 edges → 8 directed edges
|
|
45
|
+
>>> square_shape = square(1)
|
|
46
|
+
>>> get_shape_directed_edge_to_edge(square_shape)
|
|
47
|
+
array([0, 0, 1, 1, 2, 2, 3, 3])
|
|
48
|
+
"""
|
|
49
|
+
from scadpy.core.assembly import get_assembly_directed_edge_to_edge
|
|
50
|
+
|
|
51
|
+
return get_assembly_directed_edge_to_edge(n_edges=len(shape.edge_to_vertex))
|
|
@@ -0,0 +1,63 @@
|
|
|
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.d2.shape import Shape
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@typechecked
|
|
15
|
+
def get_shape_directed_edge_to_vertex(
|
|
16
|
+
shape: Shape,
|
|
17
|
+
) -> NDArray[np.int64]:
|
|
18
|
+
"""
|
|
19
|
+
For each directed edge in the shape, return the indices of its start and end vertices.
|
|
20
|
+
|
|
21
|
+
Each undirected edge ``i`` gives rise to two directed edges, interleaved:
|
|
22
|
+
|
|
23
|
+
- ``directed_edge 2i`` : forward → ``[start, end]`` (ring winding order)
|
|
24
|
+
- ``directed_edge 2i+1`` : backward → ``[end, start]``
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
shape : Shape
|
|
29
|
+
The shape to extract directed edge-to-vertex indices from.
|
|
30
|
+
|
|
31
|
+
Returns
|
|
32
|
+
-------
|
|
33
|
+
NDArray[np.int64]
|
|
34
|
+
2D array of shape ``(2 * n_edges, 2)``. Each row is
|
|
35
|
+
``[start_vertex, end_vertex]`` for the directed edge.
|
|
36
|
+
|
|
37
|
+
Examples
|
|
38
|
+
--------
|
|
39
|
+
>>> from scadpy import (
|
|
40
|
+
... get_shape_directed_edge_to_vertex, polygon, square
|
|
41
|
+
... )
|
|
42
|
+
|
|
43
|
+
>>> # triangle: 3 edges → 6 directed edges
|
|
44
|
+
>>> # (forward/backward interleaved)
|
|
45
|
+
>>> triangle = polygon([(0, 0), (1, 0), (0.5, 1)])
|
|
46
|
+
>>> get_shape_directed_edge_to_vertex(triangle)
|
|
47
|
+
array([[0, 1],
|
|
48
|
+
[1, 0],
|
|
49
|
+
[1, 2],
|
|
50
|
+
[2, 1],
|
|
51
|
+
[2, 0],
|
|
52
|
+
[0, 2]])
|
|
53
|
+
|
|
54
|
+
>>> # square: 4 edges → 8 directed edges
|
|
55
|
+
>>> square_shape = square(1)
|
|
56
|
+
>>> get_shape_directed_edge_to_vertex(square_shape).shape
|
|
57
|
+
(8, 2)
|
|
58
|
+
"""
|
|
59
|
+
from scadpy.core.assembly import get_assembly_directed_edge_to_vertex
|
|
60
|
+
|
|
61
|
+
return get_assembly_directed_edge_to_vertex(
|
|
62
|
+
edge_to_vertex=shape.edge_to_vertex,
|
|
63
|
+
)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
"get_shape_edge_lengths",
|
|
3
|
+
"get_shape_edge_midpoints",
|
|
4
|
+
"get_shape_edge_normals",
|
|
5
|
+
"get_shape_edge_to_vertex",
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
from .get_shape_edge_lengths import get_shape_edge_lengths
|
|
9
|
+
from .get_shape_edge_midpoints import get_shape_edge_midpoints
|
|
10
|
+
from .get_shape_edge_normals import get_shape_edge_normals
|
|
11
|
+
from .get_shape_edge_to_vertex import get_shape_edge_to_vertex
|
|
@@ -0,0 +1,43 @@
|
|
|
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.d2.shape import Shape
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@typechecked
|
|
14
|
+
def get_shape_edge_lengths(
|
|
15
|
+
shape: Shape,
|
|
16
|
+
) -> NDArray[np.float64]:
|
|
17
|
+
"""
|
|
18
|
+
For each edge in the shape, return its length.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
shape : Shape
|
|
23
|
+
The shape to extract edge lengths from.
|
|
24
|
+
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
NDArray[np.float64]
|
|
28
|
+
1D array of shape ``(n_edges,)``, one length per edge.
|
|
29
|
+
|
|
30
|
+
Examples
|
|
31
|
+
--------
|
|
32
|
+
>>> from scadpy import get_shape_edge_lengths, square
|
|
33
|
+
|
|
34
|
+
>>> square_shape = square(2)
|
|
35
|
+
>>> get_shape_edge_lengths(square_shape)
|
|
36
|
+
array([2., 2., 2., 2.])
|
|
37
|
+
"""
|
|
38
|
+
from scadpy.core.assembly import get_assembly_edge_lengths
|
|
39
|
+
|
|
40
|
+
return get_assembly_edge_lengths(
|
|
41
|
+
edge_to_vertex=shape.edge_to_vertex,
|
|
42
|
+
vertex_coordinates=shape.vertex_coordinates,
|
|
43
|
+
)
|
|
@@ -0,0 +1,46 @@
|
|
|
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.d2.shape import Shape
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@typechecked
|
|
14
|
+
def get_shape_edge_midpoints(
|
|
15
|
+
shape: Shape,
|
|
16
|
+
) -> NDArray[np.float64]:
|
|
17
|
+
"""
|
|
18
|
+
For each edge in the shape, return the midpoint between its two vertices.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
shape : Shape
|
|
23
|
+
The shape to extract edge midpoints from.
|
|
24
|
+
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
NDArray[np.float64]
|
|
28
|
+
2D array of shape ``(n_edges, 2)``, one midpoint per edge.
|
|
29
|
+
|
|
30
|
+
Examples
|
|
31
|
+
--------
|
|
32
|
+
>>> from scadpy import get_shape_edge_midpoints, square
|
|
33
|
+
|
|
34
|
+
>>> square_shape = square(2)
|
|
35
|
+
>>> get_shape_edge_midpoints(square_shape)
|
|
36
|
+
array([[ 0., -1.],
|
|
37
|
+
[ 1., 0.],
|
|
38
|
+
[ 0., 1.],
|
|
39
|
+
[-1., 0.]])
|
|
40
|
+
"""
|
|
41
|
+
from scadpy.core.assembly import get_assembly_edge_midpoints
|
|
42
|
+
|
|
43
|
+
return get_assembly_edge_midpoints(
|
|
44
|
+
edge_to_vertex=shape.edge_to_vertex,
|
|
45
|
+
vertex_coordinates=shape.vertex_coordinates,
|
|
46
|
+
)
|