# Copyright (c) 2026, pinkfish
#
# Licensed under the BSD 2-Clause License. See the LICENSE file in the project
# root for the full license text.
# SPDX-License-Identifier: BSD-2-Clause
# DocCategory: Foundational
# LibFile: pybosl2/solid.py
# FileSummary: Statically typed shape constructors and backend-neutral solid facade.
# FileGroup: BOSL2
"""Statically typed shape constructors and backend-neutral solid facade."""
# The backend-neutral solid facade: unified shape constructors that build on whichever backend is
# active (``"csg"`` by default, ``"sdf"`` under ``use_backend("sdf")``). Each returns a common
# :class:`~pybosl2._backend.Solid` -- a CsgSolid on the CSG backend, an SdfSolid on the SDF backend --
# so the same code realizes exact CSG or an F-Rep/signed-distance field depending on context:
#
# from pybosl2.solid import sphere, use_backend
# a = sphere(radius=10) # CSG (default) -> CsgSolid
# with use_backend("sdf"):
# b = sphere(radius=10) # libfive SDF -> SdfSolid
#
# The shape constructors below are the 3-D primitives BOTH backends expose; each dispatches by name
# through the active backend's ``construct``. n-ary CSG (union/difference/intersection) dispatches to
# the backend's own operators. The backend-specific modules (pybosl2.shapes3d, pybosl2._sdf) remain
# directly importable for anything not yet unified here.
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from pybosl2._backend import (
Solid,
current_backend,
get_backend,
given_arguments,
set_default_backend,
use_backend,
)
from pybosl2.exceptions import CrossBackendError, UnsupportedByBackendError
if TYPE_CHECKING:
from collections.abc import Sequence
from pybosl2._edges_lang import Anchor, EdgeAtom
_SHARED_3D = (
"cube",
"cuboid",
"cyl",
"cylinder",
"octahedron",
"onion",
"pie_slice",
"prismoid",
"rect_tube",
"regular_prism",
"sphere",
"spheroid",
"teardrop",
"torus",
"tube",
"wedge",
"xcyl",
"ycyl",
"zcyl",
)
__all__ = [
"cube",
"cuboid",
"cyl",
"cylinder",
"octahedron",
"onion",
"pie_slice",
"prismoid",
"rect_tube",
"regular_prism",
"sphere",
"spheroid",
"teardrop",
"torus",
"tube",
"wedge",
"xcyl",
"ycyl",
"zcyl",
"polyhedron",
"union",
"difference",
"intersection",
# backend controls, re-exported for convenience
"use_backend",
"set_default_backend",
"current_backend",
"Solid",
"CrossBackendError",
"UnsupportedByBackendError",
"given_arguments",
]
[docs]
def cube(
size: float | Sequence[float] | None = None,
*,
chamfer: float | None = None,
rounding: float | None = None,
anchor: Anchor | Sequence[float] | None = None,
center: bool | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
res: int | None = None,
) -> Solid:
"""Return a cube on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.cube`).
"""
return get_backend().construct(
"cube",
given_arguments(
{
"size": size,
"chamfer": chamfer,
"rounding": rounding,
"anchor": anchor,
"center": center,
"spin": spin,
"orient": orient,
"fn": fn,
"fa": fa,
"fs": fs,
"res": res,
}
),
)
[docs]
def cuboid(
size: float | Sequence[float] | None = None,
*,
chamfer: float | None = None,
rounding: float | None = None,
edges: EdgeAtom | list[EdgeAtom] | None = None,
except_edges: list[EdgeAtom] | None = None,
anchor: Anchor | Sequence[float] | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
res: int | None = None,
) -> Solid:
"""Return a cuboid on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.cuboid`).
"""
return get_backend().construct(
"cuboid",
given_arguments(
{
"size": size,
"chamfer": chamfer,
"rounding": rounding,
"edges": edges,
"except_edges": except_edges,
"anchor": anchor,
"spin": spin,
"orient": orient,
"fn": fn,
"fa": fa,
"fs": fs,
"res": res,
}
),
)
[docs]
def cyl(
height: float | None = None,
radius: float | None = None,
*,
center: bool | None = None,
length: float | None = None,
radius1: float | None = None,
radius2: float | None = None,
diameter: float | None = None,
diameter1: float | None = None,
diameter2: float | None = None,
chamfer: float | None = None,
chamfer1: float | None = None,
chamfer2: float | None = None,
rounding: float | None = None,
rounding1: float | None = None,
rounding2: float | None = None,
shift: Sequence[float] | None = None,
anchor: Anchor | Sequence[float] | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
res: int | None = None,
) -> Solid:
"""Return a cyl on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.cyl`).
"""
return get_backend().construct(
"cyl",
given_arguments(
{
"height": height,
"radius": radius,
"center": center,
"length": length,
"radius1": radius1,
"radius2": radius2,
"diameter": diameter,
"diameter1": diameter1,
"diameter2": diameter2,
"chamfer": chamfer,
"chamfer1": chamfer1,
"chamfer2": chamfer2,
"rounding": rounding,
"rounding1": rounding1,
"rounding2": rounding2,
"shift": shift,
"anchor": anchor,
"spin": spin,
"orient": orient,
"fn": fn,
"fa": fa,
"fs": fs,
"res": res,
}
),
)
[docs]
def cylinder(
height: float | None = None,
radius: float | None = None,
*,
chamfer: float | None = None,
chamfer1: float | None = None,
chamfer2: float | None = None,
rounding: float | None = None,
rounding1: float | None = None,
rounding2: float | None = None,
center: bool | None = None,
length: float | None = None,
radius1: float | None = None,
radius2: float | None = None,
diameter: float | None = None,
diameter1: float | None = None,
diameter2: float | None = None,
anchor: Anchor | Sequence[float] | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
res: int | None = None,
) -> Solid:
"""Return a cylinder on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.cylinder`).
"""
return get_backend().construct(
"cylinder",
given_arguments(
{
"height": height,
"radius": radius,
"chamfer": chamfer,
"chamfer1": chamfer1,
"chamfer2": chamfer2,
"rounding": rounding,
"rounding1": rounding1,
"rounding2": rounding2,
"center": center,
"length": length,
"radius1": radius1,
"radius2": radius2,
"diameter": diameter,
"diameter1": diameter1,
"diameter2": diameter2,
"anchor": anchor,
"spin": spin,
"orient": orient,
"fn": fn,
"fa": fa,
"fs": fs,
"res": res,
}
),
)
[docs]
def octahedron(
size: float | None = None,
*,
anchor: Anchor | Sequence[float] | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
res: int | None = None,
) -> Solid:
"""Return an octahedron on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.octahedron`).
"""
return get_backend().construct(
"octahedron", given_arguments({"size": size, "anchor": anchor, "spin": spin, "orient": orient, "res": res})
)
[docs]
def onion(
radius: float | None = None,
*,
angle: float | None = None,
cap_height: float | None = None,
diameter: float | None = None,
anchor: Anchor | Sequence[float] | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
res: int | None = None,
) -> Solid:
"""Return an onion on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.onion`).
"""
return get_backend().construct(
"onion",
given_arguments(
{
"radius": radius,
"angle": angle,
"cap_height": cap_height,
"diameter": diameter,
"anchor": anchor,
"spin": spin,
"orient": orient,
"fn": fn,
"fa": fa,
"fs": fs,
"res": res,
}
),
)
[docs]
def pie_slice(
height: float | None = None,
radius: float | None = None,
*,
angle: float | None = None,
radius1: float | None = None,
radius2: float | None = None,
diameter: float | None = None,
diameter1: float | None = None,
diameter2: float | None = None,
length: float | None = None,
anchor: Anchor | Sequence[float] | None = None,
center: bool | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
res: int | None = None,
) -> Solid:
"""Return a pie_slice on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.pie_slice`).
"""
return get_backend().construct(
"pie_slice",
given_arguments(
{
"height": height,
"radius": radius,
"angle": angle,
"radius1": radius1,
"radius2": radius2,
"diameter": diameter,
"diameter1": diameter1,
"diameter2": diameter2,
"length": length,
"anchor": anchor,
"center": center,
"spin": spin,
"orient": orient,
"fn": fn,
"fa": fa,
"fs": fs,
"res": res,
}
),
)
[docs]
def prismoid(
size1: Sequence[float] | None = None,
size2: Sequence[float] | None = None,
*,
height: float | None = None,
shift: Sequence[float] | None = None,
length: float | None = None,
anchor: Anchor | Sequence[float] | None = None,
center: bool | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
res: int | None = None,
) -> Solid:
"""Return a prismoid on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.prismoid`).
"""
return get_backend().construct(
"prismoid",
given_arguments(
{
"size1": size1,
"size2": size2,
"height": height,
"shift": shift,
"length": length,
"anchor": anchor,
"center": center,
"spin": spin,
"orient": orient,
"fn": fn,
"fa": fa,
"fs": fs,
"res": res,
}
),
)
[docs]
def rect_tube(
height: float | None = None,
size: float | Sequence[float] | None = None,
*,
isize: float | Sequence[float] | None = None,
wall: float | None = None,
rounding: float | Sequence[float] | None = None,
inner_rounding: float | Sequence[float] | None = None,
anchor: Anchor | Sequence[float] | None = None,
length: float | None = None,
center: bool | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
res: int | None = None,
) -> Solid:
"""Return a rect_tube on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.rect_tube`).
"""
return get_backend().construct(
"rect_tube",
given_arguments(
{
"height": height,
"size": size,
"isize": isize,
"wall": wall,
"rounding": rounding,
"inner_rounding": inner_rounding,
"anchor": anchor,
"length": length,
"center": center,
"spin": spin,
"orient": orient,
"res": res,
}
),
)
[docs]
def regular_prism(
sides: int | None = None,
height: float | None = None,
radius: float | None = None,
*,
diameter: float | None = None,
inner_radius: float | None = None,
inner_diameter: float | None = None,
side: float | None = None,
length: float | None = None,
realign: bool | None = None,
anchor: Anchor | Sequence[float] | None = None,
center: bool | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
res: int | None = None,
) -> Solid:
"""Return a regular_prism on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.regular_prism`).
"""
return get_backend().construct(
"regular_prism",
given_arguments(
{
"sides": sides,
"height": height,
"radius": radius,
"diameter": diameter,
"inner_radius": inner_radius,
"inner_diameter": inner_diameter,
"side": side,
"length": length,
"realign": realign,
"anchor": anchor,
"center": center,
"spin": spin,
"orient": orient,
"fn": fn,
"fa": fa,
"fs": fs,
"res": res,
}
),
)
[docs]
def sphere(
radius: float | None = None,
*,
diameter: float | None = None,
anchor: Anchor | Sequence[float] | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
res: int | None = None,
) -> Solid:
"""Return a sphere on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.sphere`).
"""
return get_backend().construct(
"sphere",
given_arguments(
{
"radius": radius,
"diameter": diameter,
"anchor": anchor,
"spin": spin,
"orient": orient,
"fn": fn,
"fa": fa,
"fs": fs,
"res": res,
}
),
)
[docs]
def spheroid(
radius: float | None = None,
*,
diameter: float | None = None,
anchor: Anchor | Sequence[float] | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
res: int | None = None,
) -> Solid:
"""Return a spheroid on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.spheroid`).
"""
return get_backend().construct(
"spheroid",
given_arguments(
{
"radius": radius,
"diameter": diameter,
"anchor": anchor,
"spin": spin,
"orient": orient,
"fn": fn,
"fa": fa,
"fs": fs,
"res": res,
}
),
)
[docs]
def teardrop(
height: float | None = None,
radius: float | None = None,
*,
angle: float | None = None,
cap_height: float | None = None,
radius1: float | None = None,
radius2: float | None = None,
diameter: float | None = None,
diameter1: float | None = None,
diameter2: float | None = None,
anchor: Anchor | Sequence[float] | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
res: int | None = None,
) -> Solid:
"""Return a teardrop on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.teardrop`).
"""
return get_backend().construct(
"teardrop",
given_arguments(
{
"height": height,
"radius": radius,
"angle": angle,
"cap_height": cap_height,
"radius1": radius1,
"radius2": radius2,
"diameter": diameter,
"diameter1": diameter1,
"diameter2": diameter2,
"anchor": anchor,
"spin": spin,
"orient": orient,
"fn": fn,
"fa": fa,
"fs": fs,
"res": res,
}
),
)
[docs]
def torus(
major_radius: float | None = None,
minor_radius: float | None = None,
*,
major_diameter: float | None = None,
minor_diameter: float | None = None,
outer_radius: float | None = None,
inner_radius: float | None = None,
outer_diameter: float | None = None,
inner_diameter: float | None = None,
anchor: Anchor | Sequence[float] | None = None,
center: bool | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
res: int | None = None,
) -> Solid:
"""Return a torus on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.torus`).
"""
return get_backend().construct(
"torus",
given_arguments(
{
"major_radius": major_radius,
"minor_radius": minor_radius,
"major_diameter": major_diameter,
"minor_diameter": minor_diameter,
"outer_radius": outer_radius,
"inner_radius": inner_radius,
"outer_diameter": outer_diameter,
"inner_diameter": inner_diameter,
"anchor": anchor,
"center": center,
"spin": spin,
"orient": orient,
"fn": fn,
"fa": fa,
"fs": fs,
"res": res,
}
),
)
[docs]
def tube(
height: float | None = None,
outer_radius: float | None = None,
*,
inner_radius: float | None = None,
outer_diameter: float | None = None,
inner_diameter: float | None = None,
wall: float | None = None,
length: float | None = None,
anchor: Anchor | Sequence[float] | None = None,
center: bool | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
res: int | None = None,
) -> Solid:
"""Return a tube on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.tube`).
"""
return get_backend().construct(
"tube",
given_arguments(
{
"height": height,
"outer_radius": outer_radius,
"inner_radius": inner_radius,
"outer_diameter": outer_diameter,
"inner_diameter": inner_diameter,
"wall": wall,
"length": length,
"anchor": anchor,
"center": center,
"spin": spin,
"orient": orient,
"fn": fn,
"fa": fa,
"fs": fs,
"res": res,
}
),
)
[docs]
def wedge(
size: Sequence[float] | None = None,
*,
anchor: Anchor | Sequence[float] | None = None,
center: bool | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
res: int | None = None,
) -> Solid:
"""Return a wedge on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.wedge`).
"""
return get_backend().construct(
"wedge",
given_arguments({"size": size, "anchor": anchor, "center": center, "spin": spin, "orient": orient, "res": res}),
)
[docs]
def xcyl(
height: float | None = None,
radius: float | None = None,
*,
length: float | None = None,
radius1: float | None = None,
radius2: float | None = None,
diameter: float | None = None,
diameter1: float | None = None,
diameter2: float | None = None,
chamfer: float | None = None,
chamfer1: float | None = None,
chamfer2: float | None = None,
rounding: float | None = None,
rounding1: float | None = None,
rounding2: float | None = None,
anchor: Anchor | Sequence[float] | None = None,
center: bool | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
res: int | None = None,
) -> Solid:
"""Return a xcyl on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.xcyl`).
"""
return get_backend().construct(
"xcyl",
given_arguments(
{
"height": height,
"radius": radius,
"length": length,
"radius1": radius1,
"radius2": radius2,
"diameter": diameter,
"diameter1": diameter1,
"diameter2": diameter2,
"chamfer": chamfer,
"chamfer1": chamfer1,
"chamfer2": chamfer2,
"rounding": rounding,
"rounding1": rounding1,
"rounding2": rounding2,
"anchor": anchor,
"center": center,
"spin": spin,
"orient": orient,
"fn": fn,
"fa": fa,
"fs": fs,
"res": res,
}
),
)
[docs]
def ycyl(
height: float | None = None,
radius: float | None = None,
*,
length: float | None = None,
radius1: float | None = None,
radius2: float | None = None,
diameter: float | None = None,
diameter1: float | None = None,
diameter2: float | None = None,
chamfer: float | None = None,
chamfer1: float | None = None,
chamfer2: float | None = None,
rounding: float | None = None,
rounding1: float | None = None,
rounding2: float | None = None,
anchor: Anchor | Sequence[float] | None = None,
center: bool | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
res: int | None = None,
) -> Solid:
"""Return a ycyl on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.ycyl`).
"""
return get_backend().construct(
"ycyl",
given_arguments(
{
"height": height,
"radius": radius,
"length": length,
"radius1": radius1,
"radius2": radius2,
"diameter": diameter,
"diameter1": diameter1,
"diameter2": diameter2,
"chamfer": chamfer,
"chamfer1": chamfer1,
"chamfer2": chamfer2,
"rounding": rounding,
"rounding1": rounding1,
"rounding2": rounding2,
"anchor": anchor,
"center": center,
"spin": spin,
"orient": orient,
"fn": fn,
"fa": fa,
"fs": fs,
"res": res,
}
),
)
[docs]
def zcyl(
height: float | None = None,
radius: float | None = None,
*,
length: float | None = None,
radius1: float | None = None,
radius2: float | None = None,
diameter: float | None = None,
diameter1: float | None = None,
diameter2: float | None = None,
chamfer: float | None = None,
chamfer1: float | None = None,
chamfer2: float | None = None,
rounding: float | None = None,
rounding1: float | None = None,
rounding2: float | None = None,
anchor: Anchor | Sequence[float] | None = None,
center: bool | None = None,
spin: float | None = None,
orient: Anchor | Sequence[float] | None = None,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
res: int | None = None,
) -> Solid:
"""Return a zcyl on the active backend.
See :func:`use_backend`; identical call, backend-appropriate realization. Only the
arguments actually given are passed on, so each backend sees the ones it knows:
*res* is the SDF backend's resolution and *spin*/*orient*/*fn*/*fa*/*fs* are the CSG
backend's. Anything outside this shared set lives on the backend's own constructor
(:func:`pybosl2.shapes3d.zcyl`).
"""
return get_backend().construct(
"zcyl",
given_arguments(
{
"height": height,
"radius": radius,
"length": length,
"radius1": radius1,
"radius2": radius2,
"diameter": diameter,
"diameter1": diameter1,
"diameter2": diameter2,
"chamfer": chamfer,
"chamfer1": chamfer1,
"chamfer2": chamfer2,
"rounding": rounding,
"rounding1": rounding1,
"rounding2": rounding2,
"anchor": anchor,
"center": center,
"spin": spin,
"orient": orient,
"fn": fn,
"fa": fa,
"fs": fs,
"res": res,
}
),
)
[docs]
def polyhedron(points: Any, faces: Any = None, convexity: int | None = None) -> Solid:
"""Return a polyhedron on the active backend.
Backends differ on what a polyhedron means (this is not part of the shared primitive surface):
the CSG backend builds the exact mesh from *points* and *faces* (both required); the SDF backend
ignores *faces* and builds the convex hull of *points* as a distance field.
"""
return get_backend().polyhedron(points, faces, convexity=convexity)
[docs]
def union(*solids: Solid) -> Solid:
"""Return the union of *solids* on the active backend (all operands must share the active backend)."""
return get_backend().union(solids)
[docs]
def difference(*solids: Solid) -> Solid:
"""Return the first solid minus the rest, on the active backend."""
return get_backend().difference(solids)
[docs]
def intersection(*solids: Solid) -> Solid:
"""Return the intersection of *solids* on the active backend."""
return get_backend().intersection(solids)