algorithms¤
Decomposition of a domain into a coarse quad layout along its topological skeleton, and the propagation and triangulation helpers it uses.
Classes¤
SkeletonDecomposition
¤
Coarse quad mesh from a topological skeleton and its singularities (Oval's thesis, chapter 4).
The thesis-to-method map and the known deviations are in design_notes/algorithms.md.
- Tutorial Overview Two routes to a coarse layout
Methods:¤
boundary_interior_angle
¤
The interior angle of the domain at a boundary vertex, in radians, from incident face angles.
A convex corner reads below pi, a reentrant one above, a free curve-feature tip 2 pi.
branches_boundary
¤
Get the boundary branches, split at corner and split vertices: CLOSING (thesis S4.2.2).
Returns:
-
list–List of polylines as list of point XYZ-coordinates.
branches_singularity_to_boundary
¤
Get new branches from singular faces to their split vertices: GRAFTING (thesis S4.2.2).
Returns:
-
list–List of polylines as list of point XYZ-coordinates.
branches_singularity_to_singularity
¤
Get the skeleton branches between singularities only: PRUNING (thesis S4.2.2).
Returns:
-
list–List of polylines as list of point XYZ-coordinates.
branches_splitting_boundary_kinks
¤
Add branches at concave boundary kinks the skeleton missed (thesis S4.2.3.1).
Returns:
-
new_branches(list) –List of polylines as list of point XYZ-coordinates.
branches_splitting_collapsed_boundaries
¤
Add branches to split boundaries with fewer than three splits (thesis S4.2.3.4).
Runs last, because the other corrections can already solve it.
Returns:
-
new_branches(list) –List of polylines as list of point XYZ-coordinates.
branches_splitting_flipped_faces
¤
Add branches to split patches that would form flipped faces (thesis S4.2.3.3).
Returns:
-
new_branches(list) –List of polylines as list of point XYZ-coordinates.
coarse_mesh
¤
The coarse quad layout, built once from the domain's point features and cached.
Returns the same object on repeat calls, so densities set on it are kept.
Parameters:
-
poles(list[[x, y, z]], default:None) –Preferred pole positions.
Nonetakes them from thepoint_featuresoffrom_boundary, or none at all for a decomposition built from a mesh or a skeleton. An explicit list wins. Different poles miss the cache. -
force(bool, default:False) –Rebuild even on a cache hit -- for a caller that wants a clean layout back after mutating the one it was given.
Returns:
-
CoarsePseudoQuadMesh–
corner_faces
¤
Get the indices of the corner faces in the Delaunay mesh, i.e. the ones with one neighbour.
Thesis S4.2.1 calls these END faces: "end faces have one adjacent face". Regular faces have two and singular faces three.
Returns:
-
list–List of face keys.
corner_vertices
¤
Get the indices of the corner vertices of the topological skeleton, i.e. the two-valent boundary vertices in the Delaunay mesh.
The "two-valent boundary vertices" the CLOSING operation of thesis S4.2.2 splits the boundary at.
Returns:
-
list–List of vertex keys.
decomposition_mesh
¤
Return a quad mesh based on the decomposition polylines. Some fixes are added to convert the mesh formed by the decomposition polylines into a (coarse) quad mesh.
Returns:
-
mesh–A coarse quad mesh based on the topological skeleton from a Delaunay mesh.
decomposition_polyline
¤
Retrieve the decomposition polyline with extremities corresponding to two geoemtric keys.
Parameters:
-
geom_key_1(float) –Geometric key of one extremity.
-
geom_key_2(float) –Geometric key of the other extremity.
Returns:
-
(list, None)–A polyline as a list of point XYZ-coordinates if a polyline corresponds to the geometric keys, None otherwise.
decomposition_polylines
¤
Get all branch polylines of the decomposition: pruning, grafting, closing, then the S4.2.3 corrections.
Returns:
-
list–List of polylines as list of point XYZ-coordinates.
edges_to_curves
¤
edges_to_curves(
coarse: CoarseQuadMesh | CoarsePseudoQuadMesh | None = None,
wall_sampling: float | None = None,
snap: bool = True,
) -> tuple[dict[tuple[int, int], list[list[float]]], dict[str, int]]
The shape of every coarse edge, for densification: wall piece, matching branch, or chord.
Parameters:
-
coarse(CoarseQuadMesh, default:None) –The layout to describe. Defaults to
mesh. -
wall_sampling(float, default:None) –How finely to resample the walls. Defaults to a quarter of the background
target_length, the ratioCMD_coarse_meshuses. -
snap(bool, default:True) –Put the layout's boundary corners on the walls first. Mutates the layout; it must happen before the arcs are built.
Returns:
-
(dict, dict)–{(u, v): polyline}to hand todensification, and the tally. A non-zerochordcount on a boundary edge is a layout that quietly lost its curvature.
Raises:
-
ValueError–If this decomposition was not built by
from_boundary, so there are no walls to derive the curves from.
find_symmetry
¤
find_symmetry(
tol: float | None = None,
include: Iterable[str] = ("walls", "holes", "guides", "poles"),
max_order: int = 12,
) -> SymmetryReport
Detect the symmetry of the domain (walls, holes, curve and point features).
The result is kept on symmetry_report.
Returns:
-
SymmetryReport–printit for the group and its keys;report.geometry()gives the centre, mirror lines and rotation arcs to draw.
free_tip_vertices
¤
Get the vertices at free extremities of curve features (interior angle 360 degrees).
Thesis S4.3.2 makes each one a singularity, so it stays a node of the decomposition.
Returns:
-
list–List of vertex keys.
from_boundary
classmethod
¤
from_boundary(
outer_boundary: list[list[float]],
inner_boundaries: list[list[list[float]]] | None = None,
polyline_features: list[list[list[float]]] = [],
point_features: list[list[float]] = [],
target_length: float | None = None,
alpha: float | None = 0.02,
d_min: int | None = 5,
) -> SkeletonDecomposition
Triangulate a domain given by its walls, ready to decompose.
Parameters:
-
outer_boundary(list[[x, y, z]]) –The outer wall, as an open loop -- the last point is not the first.
-
inner_boundaries(list[list[[x, y, z]]], default:None) –The holes, same convention.
-
polyline_features(list[list[[x, y, z]]], default:[]) –Feature curves the decomposition must follow. See
examples/000_testing.pyfor every benchmark stage by stage, andmarkdowns/HOW_IT_WORKS.mdsection 5 for what still does not work. -
point_features(list[[x, y, z]], default:[]) –Points the decomposition must pass through. They become the POLES of the layout, and
coarse_meshtakes them from here. -
target_length(float, default:None) –Background spacing the walls are discretised to, not the quad size.
Noneusesalphatimes the bounding-box diagonal; passalpha=Noneas well to triangulate the loops as handed in. -
alpha(float, default:0.02) –Fraction of the bounding-box diagonal to use as the target length when none is given. Thesis eq. 4.1; see
compas_singular.geometry.discretise_boundary. -
d_min(int, default:5) –Fewest points per boundary loop, whatever the target length says.
Returns:
from_mesh
classmethod
¤
from_mesh(mesh: Mesh) -> SkeletonDecomposition
Construct a SkeletonDecomposition object from a Mesh.
Returns:
-
Skeleton–A skeleton object.
from_skeleton
classmethod
¤
from_skeleton(skeleton: Skeleton) -> SkeletonDecomposition
Construct a SkeletonDecomposition object from a Skeleton.
Returns:
-
Skeleton–A skeleton object.
merge_graft_targets
¤
quadrangulate_polygonal_faces
¤
Turn polygonal faces left by a curve feature into quads by seam propagation (thesis Fig 4.20d).
Triangles are left alone: they are pseudo-quads with a pole.
repair_polygonal_faces
¤
Split any face seam propagation could not quadrangulate along its own diagonals.
Last resort, inert unless a face larger than a quad survived.
Parameters:
-
poles(list, default:()) –Point features, passed on as preferred pole positions.
solve_triangular_faces
¤
Make the decomposition mesh all-quad by fixing triangles from coinciding singular faces (thesis S4.2.3.2).
Deviation: does vertex surgery on the built mesh instead of inserting the missing branch.
split_vertices
¤
Get the indices of the boundary split vertices, i.e. the vertices of the singular faces.
The other half of what CLOSING splits the boundary at (thesis S4.2.2): "the vertices of the singular faces".
Returns:
-
list–List of vertex keys.
store_pole_data
¤
Record, for every triangular face, which corner is the pole.
Triangles without a point feature at a corner get one chosen by _choose_pole.
Parameters:
-
poles(list) –Point features, as XYZ coordinates. A triangle with one of these at a corner collapses there, exactly as before.
symmetry_unit
¤
symmetry_unit(
keys: list[str] | None = None,
centre: str = "route",
seam: float | None = None,
report: SymmetryReport | None = None,
) -> SymmetricUnit
Mesh one symmetric unit of the domain with this route, cut along the seams of keys.
Densify with quad_mesh() and expand with expand_symmetrically().
Parameters:
-
keys(list[str], default:None) –Keys from
find_symmetry--['M0', 'M90']. The subgroup they GENERATE is enforced.Noneenforces everything detected. -
centre('route', default:'route') – -
seam(float, default:None) –Rotation-only groups: the angle of the first seam, radians.
-
report(SymmetryReport, default:None) –Defaults to
symmetry_report, detected now if missing.
Returns:
-
SymmetricUnit–
Triangulation¤
boundary_triangulation
¤
boundary_triangulation(
outer_boundary: Polyline | list,
inner_boundaries: list,
polyline_features: Polyline | list = [],
point_features: list = [],
delaunay: Callable[..., Any] | None = None,
) -> Mesh
Delaunay triangulation of boundary (and feature) points, cut open along curve features (thesis S4.2.1, S4.3.2).
The caller does the sampling; the constrained Delaunay of Fig 4.20 is not implemented.
Parameters:
-
outer_boundary(Polyline | list) –Planar outer boundary, as a list of vertex coordinates or a
compas.geometry.Polyline. A final point coincident with the first is dropped. -
inner_boundaries(list) –Planar inner boundaries, as a list of the above.
-
polyline_features(Polyline | list, default:[]) –Planar curve features, as a list of the above -- or one curve on its own. A closed feature must repeat its first point at the end, and one given as a closed
Polylinealready does. -
point_features(list, default:[]) –Planar point features, as a flat list of
[x, y, z]or ofcompas.geometry.Point. NOT a list of lists of points. -
delaunay(callable or proxy, default:None) –Delaunay triangulation function.
Returns:
-
delaunay_mesh(Mesh) –The Delaunay mesh.
weld_polyline_features
¤
Weld feature polylines that share a point into chains split at every non-two-valent junction.
Crossings without a shared point are left alone; see arrange_polyline_features.
Parameters:
-
polyline_features(list) –List of planar polylines as lists of vertex coordinates.
Returns:
-
list–The welded polylines. A single polyline, or an empty list, is returned unchanged.
arrange_polyline_features
¤
arrange_polyline_features(
polyline_features: list[list[list[float]]], tol: float = 1e-09
) -> list[list[list[float]]]
Put a vertex where two curve features cross, so the Delaunay can hold both cuts.
Parameters:
-
polyline_features(list) –Planar polylines as lists of vertex coordinates.
-
tol(float, default:1e-09) –Distance below which a crossing counts as an existing vertex.
Returns:
-
list–The polylines, with a vertex inserted at every crossing.
as_points
¤
Coerce one curve (Polyline, anything with .points, or a point list) to a list of [x, y, z].
Parameters:
-
curve(Polyline | list) –The curve.
-
close(bool, default:None) –Falsedrops a final point coincident with the first, which is the convention for a boundary loop.Trueadds one, which is the convention for a closed curve feature -- the cut has to come back round to where it started.Noneleaves the ends alone.
Returns:
as_curves
¤
Coerce one curve, or a list of curves, to a list of lists of [x, y, z].
A single curve is wrapped in a list, so polyline_features=my_polyline
means the same as polyline_features=[my_polyline].
Parameters:
-
curves(Polyline | list) –One curve or a list of curves, in any of the forms
as_pointsaccepts. -
close(bool, default:None) –Passed through to
as_pointsfor every curve.
Returns:
Propagation¤
quadrangulate_mesh
¤
Quadrangulate the faces of a mesh by adding edges from vertex sources.
Parameters:
-
mesh(Mesh) –A mesh to quadrangulate.
-
sources(list) –A list of vertex keys to use as sources to add edges for quandrangulation.
References
- Oval et al., Feature-based Topology Finding of Patterns for Shell Structures. Automation in Construction. 2019.
quadrangulate_faces
¤
quadrangulate_faces(
mesh: Mesh, face_sources: dict[int, list[int]], max_faces: int | None = None
) -> bool
Quadrangulate the polygonal faces of a mesh, each from its own sources, in place.
Returns False if stopped at max_faces; the mesh should then be discarded.
Parameters:
-
mesh(Mesh) –The mesh to quadrangulate, modified in place.
-
face_sources(dict) –Face keys pointing to the vertex keys that are sources for that face.
-
max_faces(int, default:None) –Stop once the mesh has more faces than this.
Returns:
-
bool–False if the propagation was stopped at
max_faces. The mesh is then left part-way and should be discarded.
discrete_coons_patch_mesh
¤
discrete_coons_patch_mesh(
mesh: Mesh, ab: list[int], bc: list[int], dc: list[int], ad: list[int]
) -> None