Rounding

Pure-Python port of the path-rounding core of BOSL2’s rounding.scad: round_corners() rounds every corner of a path, and smooth_path() fits a continuous-curvature curve through a path. Both work on 2-D and 3-D paths and are methods on Path2D and Path3D:

Path([[0, 0], [40, 0], [40, 30], [0, 30]]).round_corners(radius=5)
Path([[0, 0], [40, 0], [40, 30], [0, 30]]).round_corners(method="smooth", joint=8)
Path([[0, 0], [10, 30], [30, -10], [50, 20]], closed=False).smooth_path(relsize=0.4)

round_corners supports three corner styles – "circle" (a constant-radius arc), "smooth" (a continuous-curvature bezier, so no curvature discontinuity where the round meets the edge), and "chamfer" (a straight bevel) – sized by exactly one of radius/r (circle only), cut (depth toward the corner), joint (distance back along each edge), or width (chamfer only). k (smooth only) tunes the curvature match. Both functions are pinned point-for-point to the real BOSL2 output in tests/test_pybosl2_reorient.py; the circle case is bit-identical to the toolkit’s original round_corners.

Coverage of BOSL2 rounding.scad

BOSL2 function

Status

Notes

round_corners

ported

round_corners() – all three methods, all four size measures, open/closed, 2-D and 3-D. The roundover-overflow (scale-factor) check is included.

smooth_path

ported (method="edges")

smooth_path() – a bezier fit through the points; the method="corners" variant is not ported.

path_join

not ported

join paths end-to-end with rounded joints – a follow-up.

offset_stroke / offset_sweep (+ os_*) / convex_offset_extrude

not ported

variable-width strokes and rounded-edge extrusions – a large follow-up.

rounded_prism / join_prism / prism_connector / attach_prism / bent_cutout_mask

not ported

the continuous-curvature 3-D prism generators (thousands of lines) – a large follow-up.

Examples

A square rounded three ways (circle, smooth, chamfer), extruded:

from pybosl2 import Path2D

sq = [[0, 0], [40, 0], [40, 30], [0, 30]]
a = Path2D(sq).round_corners(method="circle", radius=6).polygon().linear_extrude(height=4)
b = Path2D(sq).round_corners(method="smooth", joint=10).polygon().linear_extrude(height=4).right(50)
c = Path2D(sq).round_corners(method="chamfer", joint=8).polygon().linear_extrude(height=4).right(100)
(a | b | c).show()
Loading 3-D preview…

⬇ Download STL mesh

A wiggly path smoothed into a flowing ribbon:

from pybosl2 import Path2D

pts = [[0, 0], [10, 30], [30, -10], [50, 20], [70, 0]]
Path2D(pts).smooth_path(relsize=0.4).stroke(width=2).linear_extrude(height=3).show()
Loading 3-D preview…

⬇ Download STL mesh

API reference

Path-rounding core: round_corners and smooth_path (BOSL2 rounding.scad).

class pybosl2.rounding.Roundable[source]

Bases: object

Mixin adding the rounding.scad path operators as methods on Path2D and.

Path3D.

round_corners(radius=None, method=RoundingMethod.CIRCLE, cut=None, joint=None, width=None, curvature=None, closed=None, fn=None, fa=None, fs=None, k=None)[source]

Round every corner of this path.

method is "circle" (a constant-radius arc), "smooth" (a continuous-curvature bezier), or "chamfer" (a straight bevel). Size the roundover with exactly one of radius (circle only), cut (depth toward the corner), joint (distance back from the corner along each edge), or width (chamfer only) – each a scalar or a per-corner list. curvature (smooth only, 0..1) tunes how tight the curvature match is. Works on 2-D and 3-D paths.

Parameters:
radius : float | None

The rounding radius. A single float applies to all corners; a list applies per-corner radii.

method : RoundingMethod

The rounding method ("circle", "smooth", etc.).

cut : float | Sequence[float] | None

Cut depth for chamfers.

joint : float | Sequence[float] | None

Joint distance for rounding.

width : float | None

Width for rounding.

curvature : float | None

Curvature value for rounding.

closed : bool | None

Override whether paths are treated as closed.

fn : int | None

Fixed number of fragments per full circle; ambient default when omitted. Omitted, the ambient use_defaults(fn=...) value applies; fn=0 opts back out to fa/fs.

fa : float | None

Minimum fragment angle in degrees. Omitted, the ambient use_defaults(fa=...) value applies.

fs : float | None

Minimum fragment size in millimetres. Omitted, the ambient use_defaults(fs=...) value applies.

k : float | None

Smoothing parameter for continuous-curvature rounding, from 0 (sharp) to 1.

Returns:

A Path2D (2-D) or Path3D (3-D).

Return type:

Self

Examples

A rounded, smoothed and chamfered square (three copies):

from pybosl2 import Path2D, Path3D
from pybosl2.enums import RoundingMethod

sq = [[0, 0], [40, 0], [40, 40], [0, 40]]
Path2D(sq).round_corners(method=RoundingMethod.SMOOTH, joint=10).polygon().linear_extrude(
    height=4
).show()
Loading 3-D preview…

⬇ Download STL mesh

A 2-D path with circle-rounded corners:

from pybosl2 import Path2D, Path3D
from pybosl2.enums import RoundingMethod

path = Path2D([[0, 0], [20, 0], [20, 10], [10, 15], [0, 10]])
path.round_corners(method=RoundingMethod.CIRCLE, radius=3).polygon().linear_extrude(height=5).show()
Loading 3-D preview…

⬇ Download STL mesh

smooth_path(tangents=None, size=None, relsize=None, splinesteps=10, uniform=False, closed=None)[source]

Fit a smooth continuous-curvature curve through this path.

Runs a cubic bezier through every point, matching the path’s tangents, and samples it with splinesteps points per segment. size / relsize bound how far the curve may bow away from the straight path (relsize is a fraction of each segment, default 0.1). The BOSL2 method="corners" variant is not ported.

Parameters:
tangents : Sequence[Sequence[float]] | None

Tangent directions, one per point, instead of deriving them.

size : float | Sequence[float] | None

The size, one number or one per axis.

relsize : float | None

Corner size as a fraction of the shorter adjacent segment.

splinesteps : int

How many segments each bezier is flattened into.

uniform : bool

Sample by arc length rather than by parameter.

closed : bool | None

Treat the path as closed.

Returns:

A Path2D (2-D) or Path3D (3-D).

Return type:

Self

Examples

A wiggly control path smoothed into a flowing curve:

from pybosl2 import Path2D, Path3D

pts = [[0, 0], [10, 30], [30, -10], [50, 20], [70, 0]]
Path2D(pts).smooth_path(relsize=0.4).stroke(width=2).linear_extrude(height=3).show()
Loading 3-D preview…

⬇ Download STL mesh

A sawtooth path smoothed with explicit size and relsize:

from pybosl2 import Path2D, Path3D

path = Path2D([[0, 0], [10, 5], [20, 0], [30, 10]])
path.smooth_path(relsize=0.1).stroke(width=1).linear_extrude(height=3).show()
Loading 3-D preview…

⬇ Download STL mesh

offset_stroke(width=1.0, closed=None, endcap=CapType.ROUND, joint=CapType.ROUND)[source]

Offset this 2-D path to create a thickened outline Region.

Parameters:
width : float

Width of the result.

closed : bool | None

Treat the path as closed.

endcap : CapType

Treatment applied to the ends.

joint : CapType

Rounding size as the distance along each leg from the corner.

Return type:

Region

offset_sweep(height, bottom=None, top=None, steps=None, caps=CapType.BUTT, style=VNFStyle.MIN_EDGE, fn=None, fa=None, fs=None)[source]

Offset sweep/extrusion of this 2-D shape.

Parameters:
height : float

Extrusion height.

bottom : object

Rim treatment for the bottom edge (an os_* profile).

top : object

Rim treatment for the top edge.

steps : int | None

Slices per rim treatment; resolved from the rim radius and the ambient facet controls when omitted.

caps : CapsSpec

End caps for the extrusion.

style : VNFStyle

Quad-subdivision style for the mesh.

fn : int | None

Fixed fragment count for the rim arcs; ambient default when omitted. Omitted, the ambient use_defaults(fn=...) value applies; fn=0 opts back out to fa/fs.

fa : float | None

Minimum fragment angle for the rim arcs. Omitted, the ambient use_defaults(fa=...) value applies.

fs : float | None

Minimum fragment size for the rim arcs. Omitted, the ambient use_defaults(fs=...) value applies.

Returns:

The extruded solid.

Return type:

Solid

convex_offset_extrude(height, bottom=None, top=None, steps=16, caps=CapType.BUTT, style=VNFStyle.MIN_EDGE)[source]

Offset sweep/extrusion of this 2-D shape.

Parameters:
height : float

Height of the result.

bottom : object

Treatment applied to the bottom.

top : object

Treatment applied to the top.

steps : int

How many segments the rounded corner is built from.

caps : CapsSpec

Close the open ends.

style : VNFStyle

How each grid cell is split into triangles.

Return type:

Solid

rounded_prism(top=None, height=None, joint_top=None, joint_bottom=None, joint_sides=None, curvature_sides=None, steps=16, caps=CapType.BUTT, style=VNFStyle.MIN_EDGE, joint_bot=None, k_sides=None)[source]

Return the rounded prism between this path and a top path.

Parameters:
top : Sequence[Sequence[float]] | None

Treatment applied to the top.

height : float | None

Height of the result.

joint_top : float | dict[str, object] | None

Joint distance at the top.

joint_bottom : float | dict[str, object] | None

Joint distance at the bottom.

joint_sides : float | list[float] | None

Joint distance on the side edges.

curvature_sides : float | list[float] | None

Continuous-curvature smoothness on the side edges, from 0 to 1.

steps : int

How many segments the rounded corner is built from.

caps : CapsSpec

Close the open ends.

style : VNFStyle

How each grid cell is split into triangles.

joint_bot : float | dict[str, object] | None

Joint distance at the bottom.

k_sides : float | list[float] | None

Continuous-curvature smoothness on the side edges, from 0 to 1.

Return type:

Solid

join_prism(height, fillet=0.0, steps=16, caps=CapType.BUTT, style=VNFStyle.MIN_EDGE)[source]

Join this prism to a base plane with a filleted transition.

Parameters:
height : float

Height of the result.

fillet : float

Fillet radius.

steps : int

How many segments the rounded corner is built from.

caps : CapsSpec

Close the open ends.

style : VNFStyle

How each grid cell is split into triangles.

Return type:

Solid

prism_connector(length, fillet=0.0, fillet1=None, fillet2=None, steps=16, caps=CapType.BUTT, style=VNFStyle.MIN_EDGE)[source]

Construct a filleted prism connecting two objects.

Parameters:
length : float

Length of the result.

fillet : float

Fillet radius.

fillet1 : float | None

Fillet radius at the start.

fillet2 : float | None

Fillet radius at the end.

steps : int

How many segments the rounded corner is built from.

caps : CapsSpec

Close the open ends.

style : VNFStyle

How each grid cell is split into triangles.

Return type:

Solid

attach_prism(length, fillet=0.0, rounding=0.0, steps=None, caps=CapType.BUTT, style=VNFStyle.MIN_EDGE, fn=None, fa=None, fs=None)[source]

Attach a filleted prism with optional rounded end.

Parameters:
length : float

Length of the prism.

fillet : float

Fillet radius where the prism meets the surface.

rounding : float

Rounding radius on the free end.

steps : int | None

Slices per fillet/rounding arc; resolved from the radius and the ambient facet controls when omitted.

caps : CapsSpec

End caps for the prism.

style : VNFStyle

Quad-subdivision style for the mesh.

fn : int | None

Fixed fragment count for the arcs; ambient default when omitted. Omitted, the ambient use_defaults(fn=...) value applies; fn=0 opts back out to fa/fs.

fa : float | None

Minimum fragment angle for the arcs. Omitted, the ambient use_defaults(fa=...) value applies.

fs : float | None

Minimum fragment size for the arcs. Omitted, the ambient use_defaults(fs=...) value applies.

Returns:

The prism solid.

Return type:

Solid

bent_cutout_mask(radius, thickness, style=VNFStyle.MIN_EDGE)[source]

Create a mask to generate a round-edged cutout in a cylindrical shell.

Parameters:
radius : float

Rounding radius.

thickness : float

Wall thickness.

style : VNFStyle

How each grid cell is split into triangles.

Return type:

Solid

path_join(other_paths, radius=None, cut=None, joint=None, curvature=None, relocate=True, closed=None, k=None, fn=None, fa=None, fs=None)[source]

Join multiple paths to this path end-to-end (see path_join()).

Parameters:
other_paths : Sequence[Sequence[Sequence[float]]]

The further paths to join onto this one, in order.

radius : float | list[float] | None

Rounding radius at each join, one value or one per join.

cut : float | list[float] | None

Rounding size given as the cut distance from the corner instead of a radius.

joint : float | list[float] | None

Rounding size given as the joint distance along each leg instead of a radius.

curvature : float | list[float] | None

Continuous-curvature smoothness at each join, from 0 (sharp) to 1.

relocate : bool

Move each path so its start meets the previous path’s end, rather than requiring them to already touch.

closed : bool | None

Join the last path back to the first. Defaults to this path’s own flag.

k : float | list[float] | None

Smoothing parameter for the continuous-curvature joins, one value or one per join.

fn : int | None

Fixed fragment count for curved surfaces. Omitted, the ambient use_defaults(fn=...) value applies; fn=0 opts back out to fa/fs.

fa : float | None

Minimum fragment angle in degrees. Omitted, the ambient use_defaults(fa=...) value applies.

fs : float | None

Minimum fragment size in millimetres. Omitted, the ambient use_defaults(fs=...) value applies.

Return type:

Self