Bezier curves, paths and surfaces

Evaluate, analyze and build Bezier curves, paths, and surface patches (BOSL2 beziers.scad).

Pure-Python port of the Bezier CURVE and PATH API from BOSL2’s beziers.scad. Every operation lives on the Bezier class – there are no module-level bezier functions, mirroring how pybosl2/paths.py hangs every path operation off Path2D. No osuse()/BOSL2 runtime dependency.

A Bezier is a list of control points: a single curve, or a bezier PATH of degree-N curves that share endpoints (a flat list of control points where len % N == 1). Ported, matching beziers.scad:

  • curve evaluation/analysis: points, curve, derivative, tangent, curvature, closest_point, length, line_intersection

  • path evaluation/analysis: path_points, path_curve, path_closest_point, path_length, close_to_axis, path_offset, and Bezier.from_path() (the BOSL2 path_to_bezpath equivalent)

  • control-point construction: Bezier.begin/tang/joint/end (BOSL2 bez_begin/bez_tang/bez_joint/bez_end), with the scalar-angle, direction -vector, and 3-D spherical-angle (p=) forms, and Bezier.flatten

The Bezier SURFACE subsystem is on the BezierPatch class, built on a VNF port (pybosl2/vnf.py) and a sweep port (pybosl2/skin.py):

  • patches: points, normals, reverse, flat, is_patch, vnf, to_vnf, vnf_degenerate (bezier_vnf_degenerate_patch), sheet (bezier_sheet), and debug (debug_bezier_patches)

  • sweeping a shape along a bezier/bezier-path: Bezier.sweep (bezier_sweep) and Bezier.sweep, plus Bezier.debug (debug_bezier)

  • path_to_bezcornerpath() is ported as to_bezcornerpath() via the _bezcorner helper in pybosl2/rounding.py.

points() – the hot path – uses numpy: it builds the bezier-to-power-basis matrix (the same “matrix representation” BOSL2 uses, generalized to any degree N via M[i][j] = C(N,j)*C(N-j,i-j)*(-1)^(i-j) rather than BOSL2’s hardcoded per-degree table) and evaluates every sample with one matrix multiply. The point-valued methods return numpy ndarrays.

class pybosl2.beziers.Bezier(control_points=())[source]

Bases: object

A Bezier curve or path: a list of control points, with every bezier operation as a method.

Subclasses list (the same trick as pybosl2.paths.Path2D), so it is a drop-in for the raw control-point lists the toolkit passes around, while giving the chained object form:

Bezier([[44, 5], [48, 6], [64, -15]]).points([0.2 * i for i in range(6)])
Bezier.flatten([Bezier.begin([0, 0], -20, 0.4), Bezier.end([1, 0], 230, 1)]).curve(20)

A curve is one set of control points (degree len - 1). A path is a flat list of degree-N curves sharing endpoints (len % N == 1); the path_* methods interpret the Bezier that way. The point-valued methods return numpy ndarrays; the control-point builders (begin/tang/joint/end) are staticmethods returning raw ndarray groups that flatten concatenates into a new Bezier.

Parameters:
control_points : Sequence[Sequence[float]] | np.ndarray

the control points (anything array-like; 2-D or 3-D points)

Examples

Sweeping a circular profile along a 3-D bezier curve into a solid tube:

import math
import numpy as np
from pybosl2 import Bezier

circle = [[2 * math.cos(t), 2 * math.sin(t)] for t in np.linspace(0, 2 * math.pi, 24, endpoint=False)]
tube = Bezier([[0, 0, 5], [0, 0, 20], [25, 12, 15], [30, 4, 6]]).sweep(circle, splinesteps=24)
tube.polyhedron().show()
Loading 3-D preview…

⬇ Download STL mesh

classmethod from_list(points)[source]

Create a Bezier from a plain list of control points.

Parameters:
points : Sequence[Sequence[float]] | ndarray

A sequence of 2-D or 3-D control points.

Return type:

Bezier

property to_list : list[list[float]]

The underlying control-point list.

property array : ndarray

The control points as an (N, dim) numpy array.

points(u)[source]

Evaluate this curve at parameter(s) u (each in [0, 1]).

Returns an ndarray of points (or a length-dim ndarray for a scalar u). Uses the bezier-to-power-basis matrix to evaluate all samples with a single matrix multiply for maximum performance.

Parameters:
u : float | Sequence[float] | ndarray

A parameter value in [0, 1], or a sequence of values.

Returns:

An ndarray of evaluated points. For a scalar u the result is a 1-D vector; for a list of u values the result is a 2-D array of row vectors.

Return type:

ndarray

curve(splinesteps=16, endpoint=True)[source]

Sample splinesteps segments uniformly along the curve.

Returns an ndarray of splinesteps*+1 points (or *splinesteps if endpoint is False) by evaluating the curve at evenly spaced parameter values between 0 and 1.

Parameters:
splinesteps : int

Number of uniform segments to sample along the curve.

endpoint : bool

If True, include the endpoint at u=1.

Returns:

An ndarray of splinesteps*+1 points (or *splinesteps if endpoint is False) sampled uniformly along the curve.

Return type:

ndarray

Examples: .. pythonscad-example:

from pybosl2 import Bezier

pts = Bezier([[44, 5], [48, 6], [64, -15]]).curve(20)
pts.stroke(width=2).linear_extrude(height=3).show()
derivative(u, order=1)[source]

Compute the order-th derivative of the curve at parameter(s) u.

Returns an ndarray of derivative vectors. For order 0 this is equivalent to calling points(). Higher orders are computed recursively by first reducing the control polygon via differencing.

Parameters:
u : float | Sequence[float] | ndarray

A parameter value in [0, 1], or a sequence of values.

order : int

The derivative order (0 = position, 1 = first derivative, 2 = second, etc.).

Returns:

An ndarray of derivative vectors. For a scalar u the result is a 1-D vector; for a list of u values the result is a 2-D array.

Raises:

AssertionError – If order is not a non-negative integer.

Return type:

ndarray

tangent(u)[source]

Return unit tangent vector(s) at parameter(s) u.

Returns an ndarray of normalized derivative vectors. For a scalar u the result is a 1-D vector; for a list of u values the result is a 2-D array of row vectors.

Parameters:
u : float | Sequence[float] | ndarray

A parameter value in [0, 1], or a sequence of values.

Returns:

An ndarray of unit tangent vectors. For a scalar u the result is a 1-D vector; for a list of u values the result is a 2-D array.

Return type:

ndarray

curvature(u)[source]

Curvature value(s) at parameter(s) u (inverse tangent-circle radius).

Computes the scalar curvature κ = |r' × r''| / |r'|³ at each parameter value. For a scalar u returns a single float; for a list of u values returns a numpy array of floats.

Parameters:
u : float | Sequence[float] | ndarray

A parameter value in [0, 1], or a sequence of values.

Returns:

A float (for scalar u) or numpy array of curvature values. The curvature κ = |r' × r''| / |r'|³ is the inverse radius of the tangent circle.

Return type:

ndarray

closest_point(pt, max_err=0.01, u=0.0, end_u=1.0)[source]

Return the parameter u of the point on this curve closest to pt.

Uses recursive bisection to find the curve parameter that minimizes distance to the target point within max_err tolerance. The search is bounded to the interval [u, end_u] and falls back to the nearer endpoint when no local minimum is detected.

Parameters:
pt : ndarray

The target point to find the closest curve position to.

max_err : float

Maximum allowed error tolerance for the bisection search.

u : float

Start of the search interval along the curve.

end_u : float

End of the search interval along the curve.

Returns:

The parameter u in [0, 1] of the point on this curve closest to the target pt.

Return type:

float

arc_length(start_u=0.0, end_u=1.0, max_deflect=0.01)[source]

Approximate arc length of the curve between start_u and end_u.

Uses adaptive subdivision to compute the length: samples the curve, measures the maximum deviation from linear segments, and subdivides when the deviation exceeds max_deflect.

Parameters:
start_u : float

Start parameter along the curve (default 0).

end_u : float

End parameter along the curve (default 1).

max_deflect : float

Maximum deviation tolerance before subdividing further.

Returns:

The approximate arc length of the curve segment as a float.

Return type:

float

line_intersection(line)[source]

Return the u values where this 2-D curve crosses line (two points).

Computes the intersection parameters in [0, 1] by finding the real roots of the algebraic equation that expresses the signed distance from the curve to the infinite line defined by two points.

Parameters:
line : ndarray

A 2xdim array defining two points of the infinite line to intersect against.

Returns:

A list of parameter values u in [0, 1] where the curve crosses the infinite line defined by the two input points.

Return type:

list[float]

path_points(curveind, u, n_degree=3)[source]

Evaluate curve number curveind of this bezier PATH at parameter(s) u.

Extracts the control points for the given segment of a degree-N bezier path and evaluates that sub-curve at the requested parameter values. Returns an ndarray of points.

Parameters:
curveind : int

Zero-based index of the curve segment within the bezier path.

u : float | Sequence[float] | ndarray

A parameter value in [0, 1], or a sequence of values.

n_degree : int

Degree of each curve segment in the bezier path.

Returns:

An ndarray of evaluated points for the given curve segment.

Return type:

ndarray

path_curve(splinesteps=16, n_degree=3, endpoint=True)[source]

Sample this bezier PATH into a Path2D of points.

Evaluates a degree-N bezier path (len % N == 1) by sampling each segment uniformly and concatenating the results. Returns a Path2D for 2-D points or Path3D for 3-D.

Parameters:
splinesteps : int

Number of uniform segments to sample per curve segment.

n_degree : int

Degree of each curve segment in the bezier path.

endpoint : bool

Whether to include the final endpoint in the output.

Returns:

A Path2D for 2-D points or Path3D for 3-D points containing the sampled bezier path.

Return type:

Path

Examples: .. pythonscad-example:

from pybosl2 import Bezier

bz = Bezier([[0, 0], [25, 30], [50, 0], [75, -30], [100, 0]])
bz.path_curve(32, n_degree=2).stroke(width=2).linear_extrude(height=3).show()
path_closest_point(pt, n_degree=3, max_err=0.01)[source]

Find the closest position on this bezier PATH to pt.

Returns a tuple [segnum, u] where segnum is the 0-based curve segment index and u is the local parameter along that segment. Uses a two-pass search: coarse scan across segments followed by fine bisection within the best segment.

Parameters:
pt : ndarray

The target point to find the closest position to.

n_degree : int

Degree of each curve segment in the bezier path.

max_err : float

Maximum allowed error tolerance for the bisection search.

Returns:

A tuple (segment_index, u) where segment_index is the 0-based curve segment number and u is the local parameter along that segment.

Raises:

ValueError – If no closest point could be found.

Return type:

tuple[int, float]

path_arc_length(n_degree=3, max_deflect=0.001)[source]

Approximate arc length of this bezier PATH.

Sums the adaptive arc length of each individual degree-N curve segment. The max_deflect parameter controls subdivision accuracy within each segment’s length() call.

Parameters:
n_degree : int

Degree of each curve segment in the bezier path.

max_deflect : float

Maximum deviation tolerance before subdividing further.

Returns:

The approximate total arc length of the bezier path as a float.

Return type:

float

close_to_axis(axis='X', n_degree=3)[source]

Close this 2-D bezier PATH down to the given axis.

Returns a new Bezier that connects the path’s start and end to the specified axis (“X” or “Y”) and closes back to form a loop, using linear blending segments of degree n_degree.

Parameters:
axis : str

Which axis to close down to, either "X" or "Y".

n_degree : int

Degree of the linear blending segments used to close.

Returns:

A new Bezier forming a closed loop that connects the path’s start and end to the specified axis.

Raises:

AssertionError – If axis is not "X" or "Y", or if the patch is not 2-D.

Return type:

Bezier

path_offset(offset, n_degree=3)[source]

Close this 2-D bezier PATH with a reversed copy offset by offset.

Returns a new Bezier that pairs the original path with an offset duplicate connected by linear blend segments, forming a closed loop suitable for extrusion.

Parameters:
offset : ndarray

The 2-D offset vector to apply to the reversed copy.

n_degree : int

Degree of the linear blending segments used to close.

Returns:

A new Bezier forming a closed loop combining the original path with its offset copy.

Raises:

AssertionError – If the bezier is not 2-D.

Return type:

Bezier

classmethod from_path(path, closed=False, tangents=None, uniform=False, size=None, relsize=None)[source]

Cubic bezier PATH through every point of path (BOSL2 path_to_bezpath).

Deprecated, use the top-level create_bezier() instead.

Parameters:
path : Path

The input path of points to fit a bezier through.

closed : bool

Whether the path is closed.

tangents : Path | None

Optional user-supplied tangent vectors for each point.

uniform : bool

If True, compute tangents assuming uniform spacing.

size : float | None

Fixed control-point magnitude for all segments.

relsize : float | None

Relative control-point magnitude proportional to segment length.

Returns:

A cubic Bezier path interpolating every point of the input path.

Return type:

Bezier

sweep(shape, splinesteps=16, n_degree=None, method=SweepMethod.INCREMENTAL, endpoint=True, normal=None, closed=False, twist=0.0, twist_by_length=True, scale=1.0, scale_by_length=True, symmetry=1, last_normal=None, caps=CapType.BUTT, style=VNFStyle.MIN_EDGE, transforms=False)[source]

Sweep the 2-D shape along this bezier curve or path into a VNF.

If n_degree is given and len(self) % n_degree == 1 this treats the bezier as a degree-N path, sampling each segment separately. Otherwise the bezier is treated as a single curve. All other parameters are passed through to _path_sweep().

Parameters:
shape : Path

2-D shape as a list of points to sweep.

splinesteps : int

Number of uniform segments per curve or curve-segment.

n_degree : int | None

Curve degree for path mode; None uses curve mode.

method : SweepMethod

Sweep method.

endpoint : bool

If True, include the endpoint at u=1.

normal : Point | None

Optional normal vector for the sweep.

closed : bool

Whether the swept shape should be closed (a tube).

twist : float

Total twist angle in degrees applied along the sweep.

twist_by_length : bool

If True, twist is scaled by relative arc length.

scale : float

Scale factor applied along the sweep.

scale_by_length : bool

If True, scale is distributed by relative arc length.

symmetry : int

Rotational symmetry count of the shape.

last_normal : Point | None

Last normal vector for closed sweeps.

caps : CapsSpec

Whether to add end caps.

style : VNFStyle

VNF triangulation style.

transforms : bool

If True, return transformation matrices instead of a mesh.

Returns:

A VNF vertex-face mesh of the swept shape.

Return type:

VNF | Bosl2Solid

Examples

Curve mode (single curve sweep):

import math
import numpy as np
from pybosl2 import Bezier
from math import cos, sin
circle = [[2 * cos(t), 2 * sin(t)] for t in np.linspace(0, 2 * math.pi, 24, endpoint=False)]
tube = Bezier([[0, 0, 5], [0, 0, 20], [25, 12, 15], [30, 4, 6]]).sweep(circle, splinesteps=24)
tube.polyhedron().show()
Loading 3-D preview…

⬇ Download STL mesh

Path2D mode (degree-3 bezier path sweep):

import math
import numpy as np
from pybosl2 import Bezier
from math import cos, sin
shape = [[cos(t), sin(t)] for t in np.linspace(0, 2 * math.pi, 12, endpoint=False)]
path = Bezier.flatten([Bezier.begin([0, 0], 0, 20), Bezier.end([50, 0], 180, 20)])
path.sweep(shape, n_degree=3, splinesteps=24).polyhedron().show()
Loading 3-D preview…

⬇ Download STL mesh

static begin(pt, angle, radius=None, phi=None)[source]

Return the starting endpoint and control point of a cubic bezier path.

Returns a (2, dim) ndarray of [endpoint, control_point]. For 2-D points angle is a scalar angle; for 3-D points angle is a scalar angle in the XY plane and phi is the angle down from Z+.

Parameters:
pt : ndarray

The starting endpoint position.

angle : float | Sequence[float]

A scalar angle in the XY plane, or a direction vector. Required.

radius : float | None

Distance from pt to the control point; required when angle is scalar.

phi : float | None

For 3-D points: angle down from the Z+ axis.

Returns:

A (2, dim) ndarray of [endpoint, control_point].

Return type:

ndarray

static tang(pt, angle, radius1=None, radius2=None, phi=None)[source]

Smooth joint in a cubic bezier path with collinear control points.

Returns a (3, dim) ndarray of [approaching_cp, fixed_point, departing_cp]. The two control points are collinear with the fixed point, forming a smooth (G1-continuous) bend. angle can be a scalar angle or a direction vector; radius1 and radius2 control the distances from the fixed point.

Parameters:
pt : ndarray

The fixed point position.

angle : float | Sequence[float]

A scalar angle or direction vector defining the tangent direction.

radius1 : float | None

Distance from pt to the approaching control point.

radius2 : float | None

Distance from pt to the departing control point; defaults to radius1.

phi : float | None

For 3-D points: angle down from the Z+ axis.

Returns:

A (3, dim) ndarray of [approaching_cp, fixed_point, departing_cp].

Return type:

ndarray

static joint(pt, angle1, angle2, radius1=None, radius2=None, phi1=None, phi2=None)[source]

Disjoint corner joint in a cubic bezier path.

Returns a (3, dim) ndarray of [approaching_cp, fixed_point, departing_cp] with the two control points in independent directions. angle1 and angle2 define the approach and departure directions as scalar angles or direction vectors.

Parameters:
pt : ndarray

The fixed corner point position.

angle1 : float | Sequence[float]

Approach direction as a scalar angle or direction vector.

angle2 : float | Sequence[float]

Departure direction as a scalar angle or direction vector.

radius1 : float | None

Distance from pt to the approaching control point.

radius2 : float | None

Distance from pt to the departing control point.

phi1 : float | None

For 3-D points: approach angle down from Z+.

phi2 : float | None

For 3-D points: departure angle down from Z+.

Returns:

A (3, dim) ndarray of [approaching_cp, fixed_point, departing_cp], with independent approach and departure directions.

Return type:

ndarray

static end(pt, angle, radius=None, phi=None)[source]

Approaching control point and endpoint of a cubic bezier path.

Returns a (2, dim) ndarray of [control_point, endpoint], the mirror of begin(). The control point approaches the endpoint from the direction specified by angle.

Parameters:
pt : ndarray

The ending endpoint position.

angle : float | Sequence[float]

A scalar angle or direction vector for the approaching control point.

radius : float | None

Distance from the control point to pt; required when angle is scalar.

phi : float | None

For 3-D points: angle down from the Z+ axis.

Returns:

A (2, dim) ndarray of [control_point, endpoint].

Return type:

ndarray

debug(width=1.0, n_degree=3)[source]

Visualize this bezier PATH as native geometry (BOSL2 debug_bezier).

Renders the swept curve (cyan), control net (green), and control points (blue for endpoints, red for interior) as solid geometry using tubes and spheres.

Parameters:
width : float

Diameter of the visualised curve tube and control net lines.

n_degree : int

Degree of each curve segment in the bezier path.

Returns:

A native geometry solid rendering the bezier path with colored curve, control net, and control-point markers.

Return type:

Any

Examples: .. pythonscad-example:

from pybosl2 import Bezier

path = Bezier.flatten([
    Bezier.begin([0, 0, 0], -20, 0.4),
    Bezier.tang([5, 8, 2], 45, 0.2),
    Bezier.end([10, 0, 5], 230, 1),
])
path.debug(width=0.5)
static flatten(groups)[source]

Concatenate control-point groups into one Bezier.

Flattens a list of ndarray groups (from begin(), tang(), joint(), end()) by concatenating them along axis 0 into a single Bezier instance. Also supports flat lists of points directly.

Parameters:
groups : Sequence[ndarray]

A sequence of ndarray groups from begin(), tang(), joint(), or end().

Returns:

A new Bezier with all control-point groups concatenated into a single flat control-point list.

Return type:

Bezier

pybosl2.beziers.create_bezier(path, closed=False, tangents=None, uniform=False, size=None, relsize=None)[source]

Cubic bezier PATH through every point of path (BOSL2 path_to_bezpath).

Constructs a piecewise-cubic bezier that interpolates the given points, matching the path’s tangents. size or relsize control the tension; omit both to use the default (relsize=0.1).

Parameters:
path : Path

The input path of points to fit a cubic bezier through.

closed : bool

Whether the path is closed (last point connects to first).

tangents : Path | None

Optional user-supplied tangent vectors for each point.

uniform : bool

If True, compute tangents assuming uniform spacing along the path.

size : float | None

Fixed control-point magnitude for all curve segments.

relsize : float | None

Relative control-point magnitude proportional to each segment’s length.

Returns:

A cubic Bezier path whose curve passes through every point of the input path.

Raises:

AssertionError – If both size and relsize are specified, or if any path segment has zero length.

Return type:

Bezier

class pybosl2.beziers.BezierPatch(rows=())[source]

Bases: object

A rectangular Bezier surface patch: a 2-D array (rows x cols) of 3-D control points.

Evaluate it with points(), get surface normals with normals(), and mesh it into a VNF with vnf() (which renders via polyhedron()). Build several patches into one VNF with to_vnf() (BOSL2 bezier_vnf), and make a flat patch with flat() (BOSL2 bezier_patch_flat):

BezierPatch.flat([100, 100]).vnf(splinesteps=8).polyhedron()

Ported from beziers.scad’s Bezier SURFACE section: bezier_patch_points/_normals/_reverse/ _flat, is_bezier_patch, and bezier_vnf. NOT ported: bezier_vnf_degenerate_patch (handles collapsed-edge patches), bezier_sheet (offset-shell), and bezier_sweep/sweep (need BOSL2’s un-ported path_sweep), plus the debug_* visualization modules.

Parameters:
rows : np.ndarray

a list of rows, each a list of [x, y, z] control points

Examples

A bezier surface patch, thickened into a solid sheet:

from pybosl2 import BezierPatch

patch = [
    [[-50, -50, 0], [-16, -50, 20], [16, -50, -20], [50, -50, 0]],
    [[-50, -16, 20], [-16, -16, 20], [16, -16, -20], [50, -16, 20]],
    [[-50, 16, 20], [-16, 16, -20], [16, 16, 20], [50, 16, 20]],
    [[-50, 50, 0], [-16, 50, -20], [16, 50, 20], [50, 50, 0]],
]
BezierPatch(patch).sheet([0, -6], splinesteps=16).polyhedron().show()
Loading 3-D preview…

⬇ Download STL mesh

classmethod from_list(rows)[source]

Create a BezierPatch from a plain list of control-point rows.

Parameters:
rows : ndarray

A list of rows of 3-D control points.

Return type:

BezierPatch

property to_list : list[list[list[float]]]

The underlying control-point row list.

property array : ndarray

The control points as an (rows, cols, 3) numpy array.

static is_patch(x)[source]

Check if x looks like a bezier patch.

Returns True if x is a rectangular 2-D array of point vectors where the first element is a numeric vector (not a nested list of vectors) and all rows have equal length.

Parameters:
x : Any

The object to test.

Returns:

True if x is a rectangular 2-D array of numeric point vectors with equal-length rows.

Return type:

bool

points(u, v)[source]

Sample the patch at parameter(s) u and v.

u is the inner/column axis and v is the outer/row axis. Scalar u and v return a single point; lists/ranges return a rectangular (len(u) x len(v)) grid of points as an ndarray.

Parameters:
u : float | Sequence[float] | ndarray

Parameter along the column (inner) axis in [0, 1], or a sequence of values.

v : float | Sequence[float] | ndarray

Parameter along the row (outer) axis in [0, 1], or a sequence of values.

Returns:

An ndarray of sampled surface points. Scalar u and v return a single point; lists/ranges return a (len(u) x len(v)) grid of points.

Return type:

ndarray

Examples: .. pythonscad-example:

from pybosl2 import BezierPatch

patch = BezierPatch.flat([100, 100], n_degree=3)
pts = patch.points(0, [i / 16 for i in range(17)])
pts.stroke(width=2).linear_extrude(height=3).show()
normals(u, v)[source]

Return unit surface normal(s) at parameter(s) u, v.

Same shape rules as points(): scalar inputs return a single normal vector, while list inputs return a grid of normals computed as the cross product of the u and v tangents.

Parameters:
u : float | Sequence[float] | ndarray

Parameter along the column (inner) axis in [0, 1], or a sequence of values.

v : float | Sequence[float] | ndarray

Parameter along the row (outer) axis in [0, 1], or a sequence of values.

Returns:

An ndarray of unit normal vectors computed as the cross product of the u and v tangents. Scalar inputs return a single vector; list inputs return a grid of normals.

Return type:

ndarray

reverse()[source]

Reverse each row of the patch, flipping the surface orientation.

Returns a new BezierPatch with the same control points but each row in reversed order, which flips the face normals for VNF meshing.

Returns:

A new BezierPatch with reversed row order, suitable for flipping the mesh orientation.

Return type:

BezierPatch

vnf(splinesteps=16, style=VNFStyle.DEFAULT)[source]

Mesh this patch into a VNF.

Samples the patch at splinesteps intervals in both u and v directions (or per-axis if given as [usteps, vsteps]) and builds a vertex-face mesh using vertex_array().

Parameters:
splinesteps : int

Number of sampling steps per axis, or [usteps, vsteps] pair.

style : VNFStyle

VNF triangulation style, passed to vertex_array().

Returns:

A VNF vertex-face mesh of the sampled patch surface.

Return type:

VNF

Examples: .. pythonscad-example:

from pybosl2 import BezierPatch

patch = BezierPatch.flat([100, 100], n_degree=3)
vnf = patch.vnf(splinesteps=16)
vnf.polyhedron().show()
static to_vnf(patches, splinesteps=16, style=VNFStyle.DEFAULT)[source]

Convert one or more patches into a single VNF (BOSL2 bezier_vnf).

Accepts either a single patch (2-D control-point array) or a list of patches and returns their combined VNF mesh, joined via union().

Parameters:
patches : ndarray | Sequence[ndarray]

A single patch control-point array or a sequence of patches to mesh.

splinesteps : int

Number of sampling steps per axis, or [usteps, vsteps] pair.

style : VNFStyle

VNF triangulation style, passed to vertex_array().

Returns:

A combined VNF vertex-face mesh of all patches.

Return type:

VNF

Examples: .. pythonscad-example:

from pybosl2 import BezierPatch

p1 = BezierPatch.flat([50, 50], n_degree=3)
p2 = BezierPatch.flat([50, 50], n_degree=2, trans=(60, 0, 0))
BezierPatch.to_vnf([p1, p2], splinesteps=16).polyhedron().show()
static flat(size, n_degree=1, spin=0.0, orient=Anchor.TOP, trans=(0.0, 0.0, 0.0))[source]

Create a flat rectangular degree-n_degree patch.

Generates a patch of the given size centered on the XY plane, then reorients it using spin and orient. Supports translation and rotation relative to the standard XY orientation.

Parameters:
size : float | Sequence[float]

Patch size as a scalar (square) or [width, height] pair.

n_degree : int

Degree of the patch in each direction.

spin : float

Rotation angle in degrees around the Z axis.

orient : Anchor | Sequence[float]

Orientation vector for the patch normal.

trans : Sequence[float]

Translation vector [x, y, z].

Returns:

A new BezierPatch of the given dimensions, centered on the XY plane and reoriented as specified.

Return type:

BezierPatch

Examples: .. pythonscad-example:

from pybosl2 import BezierPatch

patch = BezierPatch.flat([100, 100], n_degree=3, spin=45)
patch.vnf(splinesteps=16).polyhedron().show()
sheet(delta, splinesteps=16, style=VNFStyle.DEFAULT)[source]

Offset the patch along surface normals to form a thin sheet (BOSL2 bezier_sheet).

Creates a solid by meshing two copies of the patch offset in opposite normal directions and connecting the boundary edges. delta is a 2-vector [d0, d1] of the two offset distances; a scalar d is equivalent to [0, -d]. The resulting VNF can be rendered directly with polyhedron().

Parameters:
delta : float

Offset distances [d0, d1] along surface normals; a scalar d is equivalent to [0, -d].

splinesteps : int

Number of sampling steps per axis, or [usteps, vsteps] pair.

style : VNFStyle

VNF triangulation style, passed to vertex_array().

Returns:

A VNF solid mesh formed by offsetting the patch in opposite normal directions and connecting the boundary edges.

Raises:

AssertionError – If the patch has degenerate normals.

Return type:

VNF

Examples: .. pythonscad-example:

from pybosl2 import BezierPatch

patch = BezierPatch.flat([100, 100], n_degree=3)
patch.sheet([0, -6], splinesteps=16).polyhedron().show()
vnf_degenerate(splinesteps=16, reverse=False, return_edges=False)[source]

Mesh a degenerate patch (BOSL2 bezier_vnf_degenerate_patch).

Handles patches where some corners or edges are collapsed, avoiding excess triangles by using adaptive triangulation. When return_edges is True, returns a [vnf, edges] tuple where edges is [left, right, top, bottom] point lists.

Parameters:
splinesteps : int

Number of sampling steps along each edge.

reverse : bool

If True, reverse the face orientation.

return_edges : bool

If True, also return edge point lists as [left, right, top, bottom].

Returns:

A VNF mesh, or a (VNF, edges) tuple if return_edges is True, where edges is [left, right, top, bottom] point lists.

Return type:

VNF | tuple[VNF, list[list[list[float]]]]

debug(splinesteps=16, showcps=True, showdots=False, showpatch=True, size=None, style=VNFStyle.DEFAULT)[source]

Visualize this patch as native geometry (BOSL2 debug_bezier_patches).

Renders the surface, control-point net lines, and control points as solid geometry. showpatch enables the surface mesh, showcps draws the control net, and showdots highlights the mesh vertices.

Parameters:
splinesteps : int

Number of sampling steps for the surface mesh.

showcps : bool

If True, render the control-point net.

showdots : bool

If True, highlight the mesh vertices.

showpatch : bool

If True, render the surface mesh.

size : float | None

Optional marker diameter; auto-scaled if None.

style : VNFStyle

VNF triangulation style, passed to vertex_array().

Returns:

A Bosl2Solid wrapping the rendered patch surface, control net, and control-point markers.

Return type:

Bosl2Solid

Examples: .. pythonscad-example:

from pybosl2 import BezierPatch

patch = BezierPatch.flat([100, 100], n_degree=3)
patch.debug(splinesteps=8, showcps=True, showpatch=True)
pybosl2.beziers.debug_bezier_patches(patches, size=None, splinesteps=16, showcps=True, showdots=False, showpatch=True, style=VNFStyle.DEFAULT)[source]

Native geometry showing bezier patches: surfaces, control points and control-net lines.

Returns a Bosl2Solid wrapping the rendered patches. Requires the real PythonSCAD app; builds on VNF.polyhedron() and the ported path_sweep tube.

Parameters:
patches : np.ndarray | Sequence[np.ndarray]

A single patch or list of patches to debug-visualise.

size : float | None

Optional marker diameter; auto-scaled if None.

splinesteps : int

Number of sampling steps for the surface mesh.

showcps : bool

If True, render the control-point net.

showdots : bool

If True, highlight the mesh vertices.

showpatch : bool

If True, render the surface mesh.

style : VNFStyle

VNF triangulation style, passed to vertex_array().

Returns:

A Bosl2Solid wrapping the rendered patch surfaces, control nets, and control-point markers.

Return type:

Bosl2Solid