"""Thin builders for native aperiodic Delaunay and Voronoi lattices."""
# OpenVCAD
import pyvcad as pv
# Local
from .fields import _coerce_attribute
from .points import StochasticPointSet
_DELAUNAY_GRAPH_MODES = {
"mesh_edges": pv.StochasticGraphExtraction.DELAUNAY_EDGES,
"vertex_centroid": pv.StochasticGraphExtraction.DELAUNAY_FACET_VERTEX_CENTROID,
"dual": pv.StochasticGraphExtraction.DELAUNAY_BARYCENTRIC_DUAL,
}
_BOUNDARY_MODES = {
"open": pv.StochasticBoundaryMode.OPEN_CLIP,
"closed": pv.StochasticBoundaryMode.CLOSED_CONFINE,
}
def _points(value):
return list(value.points if isinstance(value, StochasticPointSet) else value)
def _boundary(value):
if isinstance(value, pv.StochasticBoundaryMode):
return value
try:
return _BOUNDARY_MODES[value]
except (KeyError, TypeError):
raise ValueError("boundary must be 'open', 'closed', or StochasticBoundaryMode") from None
[docs]
def stochastic_graph_lattice(
host,
points,
extraction,
beam_thickness=1.0,
node_radius=None,
boundary="open",
field_tolerance=0.05,
boundary_tolerance=0.1,
member_blend_radius=0.0,
prune_loose=False,
allow_outside_seeds=False,
):
"""Build a native host-confined graph lattice from explicit seed points.
``prune_loose=True`` drops strut pieces that the host cuts off at both ends (chords
through a corner or a concave part of the host), which would otherwise print as loose
fragments. ``allow_outside_seeds=True`` accepts seeds outside the host, for example a
layer that bounds the Voronoi cells at the host surface; the host still clips the
geometry.
"""
beam_radius = _coerce_attribute(beam_thickness) * 0.5
joint_field = None if node_radius is None else _coerce_attribute(node_radius)
return pv.StochasticGraphLattice(
host,
_points(points),
extraction,
beam_radius,
node_radius=joint_field,
boundary_mode=_boundary(boundary),
field_tolerance=float(field_tolerance),
boundary_tolerance=float(boundary_tolerance),
member_blend_radius=float(member_blend_radius),
prune_loose_fragments=bool(prune_loose),
allow_outside_seeds=bool(allow_outside_seeds),
)
[docs]
def delaunay_graph_lattice(
host,
points,
beam_thickness=1.0,
node_radius=None,
mode="mesh_edges",
boundary="open",
field_tolerance=0.05,
boundary_tolerance=0.1,
member_blend_radius=0.0,
prune_loose=False,
allow_outside_seeds=False,
):
"""Build Delaunay beams using mesh-edge, vertex-centroid, or dual extraction."""
try:
extraction = _DELAUNAY_GRAPH_MODES[mode]
except KeyError:
names = ", ".join(sorted(_DELAUNAY_GRAPH_MODES))
raise ValueError(f"mode must be one of: {names}") from None
return stochastic_graph_lattice(
host,
points,
extraction,
beam_thickness,
node_radius,
boundary,
field_tolerance,
boundary_tolerance,
member_blend_radius,
prune_loose,
allow_outside_seeds,
)
[docs]
def voronoi_graph_lattice(
host,
points,
beam_thickness=1.0,
node_radius=None,
boundary="open",
field_tolerance=0.05,
boundary_tolerance=0.1,
member_blend_radius=0.0,
prune_loose=False,
allow_outside_seeds=False,
):
"""Build beams along bounded Voronoi-cell edges.
See :func:`stochastic_graph_lattice` for ``prune_loose`` and ``allow_outside_seeds``.
"""
return stochastic_graph_lattice(
host,
points,
pv.StochasticGraphExtraction.VORONOI_EDGES,
beam_thickness,
node_radius,
boundary,
field_tolerance,
boundary_tolerance,
member_blend_radius,
prune_loose,
allow_outside_seeds,
)
[docs]
def stochastic_plate_lattice(
host,
points,
extraction,
wall_thickness=1.0,
boundary="open",
surface_tolerance=0.05,
boundary_tolerance=0.1,
member_blend_radius=0.0,
):
"""Build a native host-confined plate lattice from explicit seed points.
Args:
host (pv.Node): Solid that confines the lattice.
points (list[pv.Vec3] | StochasticPointSet): Unique world-space seed points inside
the host.
extraction (pv.StochasticFaceExtraction): ``DELAUNAY_FACETS`` or ``VORONOI_FACES``.
wall_thickness (float | pv.FloatAttribute | callable): Full plate thickness in mm,
default ``1.0``.
boundary (str | pv.StochasticBoundaryMode): ``"open"`` (default, ``OPEN_CLIP``)
cuts plates at the host surface and leaves them open; ``"closed"``
(``CLOSED_CONFINE``) confines them and closes them against the host surface with
contact patches and seams.
surface_tolerance (float): Largest thickness interpolation error in mm, default
``0.05``.
boundary_tolerance (float): Host-boundary tolerance in mm, default ``0.1``.
member_blend_radius (float): Smooth-union radius between plates in mm, default
``0.0`` (sharp).
Returns:
pv.StochasticFaceLattice: The plate lattice node.
Raises:
ValueError: If ``boundary`` is not ``"open"``, ``"closed"`` or a
``pv.StochasticBoundaryMode``.
"""
return pv.StochasticFaceLattice(
host,
_points(points),
extraction,
_coerce_attribute(wall_thickness),
boundary_mode=_boundary(boundary),
surface_tolerance=float(surface_tolerance),
boundary_tolerance=float(boundary_tolerance),
member_blend_radius=float(member_blend_radius),
)
[docs]
def delaunay_plate_lattice(
host,
points,
wall_thickness=1.0,
boundary="open",
surface_tolerance=0.05,
boundary_tolerance=0.1,
member_blend_radius=0.0,
):
"""Build symmetric plates around Delaunay tetrahedron facets.
See :func:`stochastic_plate_lattice` for the arguments.
"""
return stochastic_plate_lattice(
host,
points,
pv.StochasticFaceExtraction.DELAUNAY_FACETS,
wall_thickness,
boundary,
surface_tolerance,
boundary_tolerance,
member_blend_radius,
)
[docs]
def voronoi_plate_lattice(
host,
points,
wall_thickness=1.0,
boundary="open",
surface_tolerance=0.05,
boundary_tolerance=0.1,
member_blend_radius=0.0,
):
"""Build symmetric plates around bounded Voronoi-cell faces.
See :func:`stochastic_plate_lattice` for the arguments.
"""
return stochastic_plate_lattice(
host,
points,
pv.StochasticFaceExtraction.VORONOI_FACES,
wall_thickness,
boundary,
surface_tolerance,
boundary_tolerance,
member_blend_radius,
)