Assembly topologies¶
- scadpy.core.assembly.topologies.get_assembly_directed_edge_directions(directed_edge_to_vertex, vertex_coordinates)¶
For each directed edge, return its unit direction vector.
The direction vector points from the start vertex to the end vertex, normalized to unit length.
- Parameters:
- directed_edge_to_vertex
NDArray[np.int64] 2D array of shape
(n_directed_edges, 2)mapping each directed edge to its[start_vertex, end_vertex]indices.- vertex_coordinates
NDArray[np.float64] 2D array of shape
(n_vertices, 2)with vertex coordinates.
- directed_edge_to_vertex
- Returns:
NDArray[np.float64]2D array of shape
(n_directed_edges, 2). Each row is a unit vector[dx, dy]pointing from start to end vertex.
Examples
>>> import numpy as np >>> from scadpy import get_assembly_directed_edge_directions
>>> # square: forward edges go right, up, left, down; >>> # backward edges are reversed >>> directed_edge_to_vertex = np.array([ ... [0, 1], [1, 0], ... [1, 2], [2, 1], ... [2, 3], [3, 2], ... [3, 0], [0, 3], ... ], dtype=np.int64) >>> vertex_coordinates = np.array( ... [[0., 0.], [1., 0.], [1., 1.], [0., 1.]] ... ) >>> get_assembly_directed_edge_directions( ... directed_edge_to_vertex, vertex_coordinates ... ).round(4) array([[ 1., 0.], [-1., 0.], [ 0., 1.], [ 0., -1.], [-1., 0.], [ 1., 0.], [ 0., -1.], [ 0., 1.]])
- scadpy.core.assembly.topologies.get_assembly_directed_edge_to_edge(n_edges)¶
For each directed edge, return the index of its parent undirected edge.
Since directed edges are interleaved (
directed_edge 2ianddirected_edge 2i+1both belong toedge i), the mapping is:edge index = directed_edge index // 2
- Parameters:
- n_edges
int Number of undirected edges.
- n_edges
- Returns:
NDArray[np.int64]1D array of shape
(2 * n_edges,). Each entry is the index of the parent undirected edge.
Examples
>>> import numpy as np >>> from scadpy import get_assembly_directed_edge_to_edge
>>> # triangle: 3 edges → 6 directed edges >>> get_assembly_directed_edge_to_edge(3) array([0, 0, 1, 1, 2, 2])
>>> # square: 4 edges → 8 directed edges >>> get_assembly_directed_edge_to_edge(4) array([0, 0, 1, 1, 2, 2, 3, 3])
- scadpy.core.assembly.topologies.get_assembly_directed_edge_to_vertex(edge_to_vertex)¶
For each directed edge, return the indices of its start and end vertices.
Each undirected edge
igives rise to two directed edges, interleaved:directed_edge 2i: forward →[start, end]directed_edge 2i+1: backward →[end, start]
- Parameters:
- edge_to_vertex
NDArray[np.int64] 2D array of shape
(n_edges, 2)mapping each edge to its[start_vertex, end_vertex]indices.
- edge_to_vertex
- Returns:
NDArray[np.int64]2D array of shape
(2 * n_edges, 2). Each row is[start_vertex, end_vertex]for the directed edge.
Examples
>>> import numpy as np >>> from scadpy import get_assembly_directed_edge_to_vertex
>>> # triangle: 3 edges → 6 directed edges >>> edge_to_vertex = np.array( ... [[0, 1], [1, 2], [2, 0]], dtype=np.int64 ... ) >>> get_assembly_directed_edge_to_vertex(edge_to_vertex) array([[0, 1], [1, 0], [1, 2], [2, 1], [2, 0], [0, 2]])
- scadpy.core.assembly.topologies.get_assembly_edge_lengths(edge_to_vertex, vertex_coordinates)¶
For each edge, return the Euclidean distance between its two vertices.
- Parameters:
- edge_to_vertex
NDArray[np.int64] 2D array of shape
(n_edges, 2)mapping each edge to its[start_vertex, end_vertex]indices.- vertex_coordinates
NDArray[np.float64] 2D array of shape
(n_vertices, d)with vertex coordinates.
- edge_to_vertex
- Returns:
NDArray[np.float64]1D array of shape
(n_edges,), one length per edge.
Examples
>>> import numpy as np >>> from scadpy.core.assembly import get_assembly_edge_lengths
>>> edge_to_vertex = np.array( ... [[0, 1], [1, 2], [2, 0]], dtype=np.int64 ... ) >>> vertex_coordinates = np.array([[0., 0.], [3., 0.], [0., 4.]]) >>> get_assembly_edge_lengths(edge_to_vertex, vertex_coordinates) array([3., 5., 4.])
- scadpy.core.assembly.topologies.get_assembly_edge_midpoints(edge_to_vertex, vertex_coordinates)¶
For each edge, return the midpoint between its two vertices.
- Parameters:
- edge_to_vertex
NDArray[np.int64] 2D array of shape
(n_edges, 2)mapping each edge to its[start_vertex, end_vertex]indices.- vertex_coordinates
NDArray[np.float64] 2D array of shape
(n_vertices, d)with vertex coordinates.
- edge_to_vertex
- Returns:
NDArray[np.float64]2D array of shape
(n_edges, d), one midpoint per edge.
Examples
>>> import numpy as np >>> from scadpy.core.assembly import get_assembly_edge_midpoints
>>> edge_to_vertex = np.array( ... [[0, 1], [1, 2], [2, 0]], dtype=np.int64 ... ) >>> vertex_coordinates = np.array([[0., 0.], [2., 0.], [1., 2.]]) >>> get_assembly_edge_midpoints( ... edge_to_vertex, vertex_coordinates ... ) array([[1. , 0. ], [1.5, 1. ], [0.5, 1. ]])
- scadpy.core.assembly.topologies.get_assembly_edge_normals(edge_to_vertex, vertex_coordinates)¶
For each edge, return its outward unit normal.
The outward normal is the 90° clockwise rotation of the edge direction vector
(dx, dy) → (dy, -dx). For exterior rings (CCW winding), this points away from the filled area. For interior rings (CW winding, i.e. holes), this also points away from the filled area (outward into the hole).- Parameters:
- edge_to_vertex
NDArray[np.int64] 2D array of shape
(n_edges, 2)mapping each edge to its[start_vertex, end_vertex]indices.- vertex_coordinates
NDArray[np.float64] 2D array of shape
(n_vertices, 2)with vertex coordinates.
- edge_to_vertex
- Returns:
NDArray[np.float64]2D array of shape
(n_edges, 2). Each row is a unit vector[nx, ny]perpendicular to the edge and pointing outward.
Examples
>>> import numpy as np >>> from scadpy import get_assembly_edge_normals
>>> # square centered at origin: 4 edges, normals point outward >>> edge_to_vertex = np.array( ... [[0, 1], [1, 2], [2, 3], [3, 0]], dtype=np.int64 ... ) >>> vertex_coordinates = np.array( ... [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]] ... ) >>> get_assembly_edge_normals( ... edge_to_vertex, vertex_coordinates ... ).round(4) array([[ 0., -1.], [ 1., -0.], [ 0., 1.], [-1., -0.]])
- scadpy.core.assembly.topologies.get_assembly_face_vertex_angles(vertex_neighborhoods, vertex_coordinates)¶
For each vertex, return its interior angle in degrees.
The angle is always positive, in the range (0°, 180°). It represents the turning angle at the vertex, regardless of whether the vertex is convex or concave. Use convexity information separately to distinguish the two cases.
The angle is computed as the absolute value of the signed angle from the incoming edge to the outgoing edge at each vertex, using the 2D cross product to determine orientation.
- Parameters:
- vertex_neighborhoods
NDArray[np.int64] 2D array of shape
(n_vertices, 2). Each row is[prev_vertex, next_vertex].- vertex_coordinates
NDArray[np.float64] 2D array of shape
(n_vertices, 2)with vertex coordinates.
- vertex_neighborhoods
- Returns:
NDArray[np.float64]1D array of shape
(n_vertices,), one angle per vertex in degrees. All values are in the range (0°, 180°).
Examples
>>> import numpy as np >>> from scadpy import get_assembly_face_vertex_angles
>>> # square: 4 right-angle vertices >>> vertex_neighborhoods = np.array( ... [[3, 1], [0, 2], [1, 3], [2, 0]], ... dtype=np.int64 ... ) >>> vertex_coordinates = np.array( ... [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]] ... ) >>> get_assembly_face_vertex_angles( ... vertex_neighborhoods, vertex_coordinates ... ) array([90., 90., 90., 90.])
- scadpy.core.assembly.topologies.get_assembly_face_vertex_normals(vertex_neighborhoods, vertex_coordinates, are_vertices_convex, epsilon=1e-10)¶
For each vertex, return its outward unit normal.
The normal is the bisector of the two adjacent edge normals (90° CW rotation of each edge direction), oriented outward for convex vertices and inward for concave ones. For degenerate 180° vertices where the bisector vanishes, falls back to the incoming edge normal.
The term face distinguishes this from a purely topological vertex: the computation relies on face geometry (vertex coordinates) and face orientation (convexity), making it applicable to both 2D shape rings and 3D solid faces.
- Parameters:
- vertex_neighborhoods
NDArray[np.int64] 2D array of shape
(n_vertices, 2). Each row is[prev_vertex, next_vertex].- vertex_coordinates
NDArray[np.float64] 2D array of shape
(n_vertices, d)with vertex coordinates.- are_vertices_convex
NDArray[np.bool_] 1D boolean array of shape
(n_vertices,). True if the vertex is convex (normal points outward), False if concave (normal points inward).- epsilon
float,optional Threshold below which the bisector norm is considered degenerate (straight 180° vertex). Defaults to
1e-10.
- vertex_neighborhoods
- Returns:
NDArray[np.float64]2D array of shape
(n_vertices, 2). Each row is a unit vector[nx, ny].
Examples
>>> import numpy as np >>> from scadpy import get_assembly_face_vertex_normals
>>> # square: 4 convex vertices, normals point outward >>> # (diagonal directions) >>> vertex_neighborhoods = np.array( ... [[3, 1], [0, 2], [1, 3], [2, 0]], ... dtype=np.int64 ... ) >>> vertex_coordinates = np.array( ... [[-1., -1.], [1., -1.], [1., 1.], [-1., 1.]] ... ) >>> are_vertices_convex = np.array([True, True, True, True]) >>> get_assembly_face_vertex_normals( ... vertex_neighborhoods, ... vertex_coordinates, ... are_vertices_convex, ... ).round(4) array([[-0.7071, -0.7071], [ 0.7071, -0.7071], [ 0.7071, 0.7071], [-0.7071, 0.7071]])
- scadpy.core.assembly.topologies.get_assembly_face_vertex_to_incoming_directed_edge(vertex_neighborhoods, directed_edge_to_vertex)¶
For each vertex, return the index of its incoming directed edge.
The incoming directed edge of vertex neighborhood
(prev, curr, next)isprev → curr.- Parameters:
- vertex_neighborhoods
NDArray[np.int64] 2D array of shape
(n_vertices, 2). Each row is[prev_vertex, next_vertex].- directed_edge_to_vertex
NDArray[np.int64] 2D array of shape
(n_directed_edges, 2). Each row is[start_vertex, end_vertex].
- vertex_neighborhoods
- Returns:
NDArray[np.int64]1D array of shape
(n_vertices,). Each entry is the index of the incoming directed edge for that vertex.
Examples
>>> import numpy as np >>> from scadpy import ( ... get_assembly_face_vertex_to_incoming_directed_edge, ... )
>>> # triangle: directed edges >>> # [0→1]=0, [1→0]=1, [1→2]=2, [2→1]=3, [2→0]=4, [0→2]=5 >>> directed_edge_to_vertex = np.array( ... [[0, 1], [1, 0], [1, 2], [2, 1], [2, 0], [0, 2]], ... dtype=np.int64, ... ) >>> # vertices: (2,1), (0,2), (1,0) >>> # incoming: 2→0, 0→1, 1→2 >>> vertex_neighborhoods = np.array( ... [[2, 1], [0, 2], [1, 0]], dtype=np.int64 ... ) >>> get_assembly_face_vertex_to_incoming_directed_edge( ... vertex_neighborhoods, directed_edge_to_vertex ... ) array([4, 0, 2])
- scadpy.core.assembly.topologies.get_assembly_face_vertex_to_outgoing_directed_edge(vertex_neighborhoods, directed_edge_to_vertex)¶
For each vertex, return the index of its outgoing directed edge.
The outgoing directed edge of vertex neighborhood
(prev, curr, next)iscurr → next.- Parameters:
- vertex_neighborhoods
NDArray[np.int64] 2D array of shape
(n_vertices, 2). Each row is[prev_vertex, next_vertex].- directed_edge_to_vertex
NDArray[np.int64] 2D array of shape
(n_directed_edges, 2). Each row is[start_vertex, end_vertex].
- vertex_neighborhoods
- Returns:
NDArray[np.int64]1D array of shape
(n_vertices,). Each entry is the index of the outgoing directed edge for that vertex.
Examples
>>> import numpy as np >>> from scadpy import ( ... get_assembly_face_vertex_to_outgoing_directed_edge, ... )
>>> # triangle: directed edges >>> # [0→1]=0, [1→0]=1, [1→2]=2, [2→1]=3, [2→0]=4, [0→2]=5 >>> directed_edge_to_vertex = np.array( ... [[0, 1], [1, 0], [1, 2], [2, 1], [2, 0], [0, 2]], ... dtype=np.int64, ... ) >>> # vertices: (2,1), (0,2), (1,0) >>> # outgoing: 0→1, 1→2, 2→0 >>> vertex_neighborhoods = np.array( ... [[2, 1], [0, 2], [1, 0]], dtype=np.int64 ... ) >>> get_assembly_face_vertex_to_outgoing_directed_edge( ... vertex_neighborhoods, directed_edge_to_vertex ... ) array([0, 2, 4])
- scadpy.core.assembly.topologies.get_assembly_part_colors(assembly)¶
For each part in the assembly, return its color (r, g, b, a).
- Parameters:
- assembly
VertexableAssembly[G] The assembly object to extract part colors from.
- assembly
- Returns:
NDArray[np.float64]2D array of shape (n_parts, 4), one row per part.
Examples
>>> from shapely.geometry import Polygon >>> from scadpy import ( ... BLUE, RED, get_assembly_part_colors, Part, Shape ... ) ... >>> polygon1 = Polygon( ... shell=[(0, 0), (2, 0), (2, 2), (0, 2)], ... holes=[[(0.5, 0.5), (1.5, 0.5), (1.5, 1.5), (0.5, 1.5)]] ... ) >>> polygon2 = Polygon( ... shell=[(10, 10), (12, 10), (12, 12), (10, 12)] ... ) >>> get_assembly_part_colors( ... Shape.from_parts([ ... Part[Polygon].from_geometry(polygon1, BLUE), ... Part[Polygon].from_geometry(polygon2, RED) ... ]), ... ) array([[0.1, 0.3, 0.9, 1. ], [0.9, 0.1, 0.1, 1. ]])
- scadpy.core.assembly.topologies.get_assembly_vertex_coordinates(parts, get_part_vertex_coordinates, dimensions)¶
For each vertex in the assembly, return its coordinates.
- Parameters:
- parts
Sequence[Part[G]] The parts of the assembly.
- get_part_vertex_coordinates
Callable[[Part[G]],NDArray[np.float64]] Function that extracts vertex coordinates from a single part.
- dimensions
int Number of spatial dimensions (used when
partsis empty).
- parts
- Returns:
NDArray[np.float64]2D array of shape (n_vertices, dimensions), one row per vertex.
Examples
>>> from shapely.geometry import Polygon >>> from scadpy import ( ... get_assembly_vertex_coordinates, ... get_shape_part_vertex_coordinates, ... Shape, ... )
>>> polygon1 = Polygon( ... shell=[(0, 0), (2, 0), (2, 2), (0, 2)], ... holes=[[(0.5, 0.5), (1.5, 0.5), (1.5, 1.5), (0.5, 1.5)]] ... ) >>> polygon2 = Polygon( ... shell=[(10, 10), (12, 10), (12, 12), (10, 12)] ... ) >>> shape = Shape.from_geometries([polygon1, polygon2]) >>> get_assembly_vertex_coordinates( ... shape._parts, ... get_shape_part_vertex_coordinates, ... 2, ... ) array([[ 0. , 0. ], [ 2. , 0. ], [ 2. , 2. ], ... [12. , 10. ], [12. , 12. ], [10. , 12. ]])
- scadpy.core.assembly.topologies.get_assembly_vertex_to_part(parts, get_part_vertex_coordinates)¶
For each vertex in the assembly, return its part index.
- Parameters:
- parts
Sequence[Part[G]] The parts of the assembly.
- get_part_vertex_coordinates
Callable[[Part[G]],NDArray[np.float64]] Function that extracts vertex coordinates from a single part.
- parts
- Returns:
NDArray[np.int64]1D array of shape (n_vertices,), one element per vertex.
Examples
>>> from shapely.geometry import Polygon >>> from scadpy import ( ... get_assembly_vertex_to_part, ... get_shape_part_vertex_coordinates, ... Shape, ... )
>>> polygon1 = Polygon( ... shell=[(0, 0), (2, 0), (2, 2), (0, 2)], ... holes=[[(0.5, 0.5), (1.5, 0.5), (1.5, 1.5), (0.5, 1.5)]] ... ) >>> polygon2 = Polygon( ... shell=[(10, 10), (12, 10), (12, 12), (10, 12)] ... ) >>> shape = Shape.from_geometries([polygon1, polygon2]) >>> get_assembly_vertex_to_part( ... shape._parts, ... get_shape_part_vertex_coordinates, ... ) array([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1])