Regions

Object API for 2-D paths and regions.

Path2D and Region: object wrappers over the 2-D point maths in paths.py/rounding.py/ transforms.py, so a polygon can be built once and then chained (Path2D(pts).offset(radius=-2).round_corners(radius=1).polygon()) instead of threading raw point lists through free functions.

class pybosl2.regions.Region(paths=())[source]

Bases: object

A 2-D region backed by shapely (not OpenSCAD/PythonSCAD).

Stores MultiPolygon internally and derives Path2D outlines only when requested. All Boolean operations (union, intersection, difference, symmetric difference) use shapely directly. Operator overloads (|, &, -, ^) are provided.

Create a region from a single outline (no holes):

Region([[0, 0], [80, 0], [80, 60], [0, 60]])

Create a region with holes (outline first, then hole paths):

Region([
    [[0, 0], [80, 0], [80, 60], [0, 60]],           # outer outline
    [[20, 20], [60, 20], [60, 40], [20, 40]],       # hole 1
])

Or use the shorthand with_holes() for a more readable call.

For native-geometry output (e.g. extrusion), call geometry() which converts paths to Bosl2Shape2D.

Parameters:
paths : Any

The outlines; each is coerced to a Path2D. A single flat point list is treated as one outline. A shapely.Polygon or shapely.MultiPolygon is also accepted.

Examples

A rectangular plate with a rectangular hole (outline + one hole), extruded into a solid:

from pybosl2 import Region

region = Region([
    [[0, 0], [80, 0], [80, 60], [0, 60]],
    [[20, 20], [60, 20], [60, 40], [20, 40]],
])
region.geometry().linear_extrude(height=5).show()
Loading 3-D preview…

⬇ Download STL mesh

simplify(tolerance)[source]

Return a copy with each polygon simplified, keeping its colour.

Douglas-Peucker, topology-preserving, applied per polygon so the per-piece colours survive – shapely’s own simplify on the whole geometry would return bare polygons and drop them.

Worth doing on traced or imported artwork, where the point count reflects the drawing tool rather than anything the output can show: Wikipedia’s Flag of Portugal carries 33150 points, and a tolerance of 0.25 (0.02mm once that flag is 60mm wide) takes it to 11315 with the area unchanged to five figures.

Parameters:
tolerance : float

maximum deviation, in the region’s own units.

Returns:

A new Region; self is unchanged.

Return type:

Region

color(c)[source]

Return a copy of this region with the given Color.

Parameters:
c : Color

The colour to apply.

Return type:

Region

color_all(c)[source]

Return a simplified copy with every polygon set to the same colour.

All polygons are unioned together (same-colour overlaps merge into one shape) and the per-polygon colour list is collapsed to a single entry. The result has _polygon_colors cleared since every piece is the same colour, and _color set to c.

Parameters:
c : Color

The colour to apply to every polygon. Must not be None.

Returns:

A new Region where every polygon has colour c and overlapping polygons are merged into non-overlapping pieces.

Return type:

Region

copy()[source]

Return a shallow copy of this region.

Return type:

Region

property paths : list[Path2D]

The list of Path2D objects derived from the geometry.

Extracted on-demand from the underlying shapely Polygon or MultiPolygon.

Returns:

A list of Path2D objects.

property geom : MultiPolygon

The underlying shapely geometry.

to_shapely()[source]

Return the shapely geometry for this region.

Equivalent to the geom property; provided for explicit usage.

Returns:

A shapely.Polygon or shapely.MultiPolygon.

Return type:

MultiPolygon

classmethod from_svg(file, fn=None, fa=None, fs=2.0, flip_y=True, color=None, strokes='polygon', clip_to_viewbox=True)[source]

Load an SVG drawing as a Region of outlines (see pybosl2.svg.region_from_svg()).

Real path data rather than the renderer’s opaque imported handle, so the drawing can be measured, offset, rounded or tessellated like anything else – and needs no renderer.

Parameters:
file : str

Path to the SVG.

fn : int | None

Minimum fragment count per curved segment (>= 3 → absolute point count). Omitted, the ambient use_defaults(fn=...) value applies; fn=0 opts back out to fa/fs.

fa : float | None

Minimum angle in degrees (accepted for API parity). Omitted, the ambient use_defaults(fa=...) value applies.

fs : float

Minimum fragment size in SVG user units (default 2.0). Omitted, the ambient use_defaults(fs=...) value applies.

flip_y : bool

Negate Y so the drawing is not mirrored (SVG’s Y axis points down).

color : str | None

When set, overrides every shape’s fill colour with this hex string. Pass None (the default) to use the SVG’s own colours.

strokes : str

"polygon" (default) converts stroked paths to filled polygons. "ignore" skips shapes that have only a stroke and no fill.

clip_to_viewbox : bool

When True (the default), clip the drawing to the SVG’s viewBox. Pass False for a drawing whose content already sits inside its viewBox, which is the usual case for a CAD-style silhouette, to skip the work.

Returns:

A Region of the drawing, nested by the even-odd rule.

Return type:

Region

classmethod even_odd(paths)[source]

Create a region from outlines nested by the EVEN-ODD rule.

Colours are read from each Path2D object (via color()). Same-colour solids are unioned together; different-colour solids are kept as separate non-overlapping polygons inside the region – the first colour in order wins any overlap so geometry() and linear_extrude() render each in its own colour. Uncoloured paths are transparent: they do not participate in overlap resolution.

The default constructor is outer-plus-holes: outline 0 bounds the region and every other outline is a hole in it. Even-odd instead decides each outline by how many others CONTAIN it, which is the rule SVG and OpenSCAD’s multi-path polygon() use.

Parameters:
paths : Sequence[Path2D]

The outlines, each a Path2D (SPEC C-7a). Coloured Path2D objects carry their colour through to the result.

Returns:

A Region whose solid area is the even-odd interpretation of paths. The _polygon_colors list tracks which polygon has which colour.

Return type:

Region

Examples

Two disjoint squares, each with a hole – four outlines, two solids:

Region.even_odd([outer_a, hole_a, outer_b, hole_b])

Overlapping red and blue squares – first colour wins the overlap:

from pybosl2 import Path2D, Region
from pybosl2.color import Color

red = Path2D([[0,0],[30,0],[30,20],[0,20]]).color(Color("#ff0000"))
blue = Path2D([[20,0],[50,0],[50,20],[20,20]]).color(Color("#0000ff"))
Region.even_odd([red, blue]).geometry().linear_extrude(height=3).show()
classmethod with_holes(outline, *holes)[source]

Create a region from an outline plus hole outlines.

Convenience constructor. Equivalent to Region([outline, \\*holes]). See __init__() for the full constructor.

Parameters:
outline : Path2D

The outer outline Path2D.

holes : Path2D

Zero or more hole outline Path2D objects.

Returns:

A Region with the outline as the first path and holes as subsequent paths.

Return type:

Region

property outline : Path2D

The outer path.

Returns:

The first Path2D in the region, which is the outer outline.

property holes : list[Path2D]

The hole paths.

Returns:

All Path2D objects after the first, which are the interior holes.

offset(radius=None, delta=None, chamfer=False, fn=None, fa=None, fs=None)[source]

Offset every path in the region.

Parameters:
radius : float | None

The corner-rounding radius for the offset.

delta : float | None

The absolute offset distance.

chamfer : bool

Whether to chamfer corners instead of rounding them.

fn : int | None

Fixed number of fragments for the rounded corners; 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 rounded corners. Omitted, the ambient use_defaults(fa=...) value applies.

fs : float | None

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

Returns:

A new Region with every path offset by the given parameters.

Return type:

Region

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

Round the corners of every path in the region.

Parameters:
radius : float | list[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 | None

Cut depth for chamfers.

joint : 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.

Returns:

A new Region with rounded corners on every path.

Return type:

Region

translate(v)[source]

Translate every path in the region by the given vector.

Parameters:
v : Sequence[float]

A 2-D or 3-D translation vector.

Returns:

A new Region with every path translated.

Return type:

Region

bounds()[source]

Return the axis-aligned bounding box over every path in the region (SPEC S-2b).

Returns:

The Bounds2D box – the same type every other bounds() in the library answers, rather than the bare NumPy [[min], [max]] array this used to hand back.

Raises:

Bosl2ValueError – If the region holds no paths.

Return type:

Bounds2D

Examples

from pybosl2 import Path2D, Region

region = Region([Path2D([[0, 0], [30, 0], [30, 20], [0, 20]], closed=True)])
print(region.bounds().size)     # (30.0, 20.0)
region.geometry().linear_extrude(height=4).show()
Loading 3-D preview…

⬇ Download STL mesh

geometry()[source]

2-D geometry: every polygon in the region, each with its holes subtracted.

When _polygon_colors is populated each polygon is coloured individually before being unioned into the final shape. Otherwise the region’s single _color is applied to every piece.

Returns:

A Bosl2Shape2D, so the result chains straight into the 2-D operators and the extruders.

Return type:

Flat

fill()[source]

Return this region as 2-D geometry with its holes filled in.

Equivalent to just the outline (OpenSCAD fill()).

Returns:

A Flat with its holes closed.

Return type:

Flat

classmethod hull(*others)[source]

Return the 2-D convex hull of all the given regions and paths.

Uses shapely convex_hull() on the union of all input geometries. Accepts a flat list or multiple arguments:

Region.convex_hull(rect, circle)
Region.convex_hull([rect, circle])
Parameters:
others : Region | Path2D

The regions or closed paths to hull together.

Returns:

A Region representing the convex hull.

Return type:

Region

linear_extrude(height, center=False, twist=0.0, scale=1.0, slices=None, fn=None, fa=None, fs=None, color_heights=None)[source]

Extrude this region along +Z into a 3-D solid with holes included.

When the region contains multiple colour groups (via _polygon_colors), each group is extruded separately and the results are unioned – so different-colour parts of an SVG drawing become different-colour solids without manual splitting.

The result depends on the active backend: a Bosl2Solid under the default CSG backend, or a PyShape under use_backend("sdf"). See pybosl2.paths.Path2D.linear_extrude() for per-backend options.

The SDF backend’s prism is the union of the outlines’ fields, so it can only express a region of DISJOINT islands; a region with holes raises UnsupportedByBackendError there.

Parameters:
height : float

The extrusion height along +Z.

center : bool

Extrude symmetrically along Z if True (default False).

twist : float

Twist angle in degrees over the full height (default 0).

scale : float

Scale factor for the top cross-section (default 1.0).

slices : int | None

Number of intermediate layers for twist/scale (auto if None).

fn : int | None

Smoothness override for the angular resolution. Omitted, the ambient use_defaults(fn=...) value applies; fn=0 opts back out to fa/fs.

fa : float | None

Smoothness override for the minimum angle. Omitted, the ambient use_defaults(fa=...) value applies.

fs : float | None

Smoothness override for the minimum segment length. Omitted, the ambient use_defaults(fs=...) value applies.

color_heights : dict[str | Color, float] | None

Optional dict mapping colour names ("#ff0000", "red", or Color objects) to a specific extrusion height. Coloured pieces that match a key are extruded by that height; pieces whose colour is not in the mapping use the default height.

Returns:

A Bosl2Solid (CSG) or PyShape (SDF).

Return type:

Solid

Example:

red = Path2D.square(20).color("red")
blue = Path2D.square(20).color("blue")
region = Region.even_odd([red, blue.translate([25, 0])])
solid = region.linear_extrude(
    height=5,
    color_heights={"red": 10, "blue": 3},
)
solid.show()
# The red square is 10 mm tall, the blue square is 3 mm tall.
rotate_extrude(angle=360.0, fn=None, fa=None, fs=None)[source]

Revolve this region about the Y axis into a 3-D solid.

Parameters:
angle : float

The rotation angle in degrees.

fn : int | None

Number of polygon segments for curved geometry. Omitted, the ambient use_defaults(fn=...) value applies; fn=0 opts back out to fa/fs.

fa : float | None

Minimum angle for polygon segments. Omitted, the ambient use_defaults(fa=...) value applies.

fs : float | None

Minimum size for polygon segments. Omitted, the ambient use_defaults(fs=...) value applies.

Returns:

A Solid from the active backend (SPEC A-10). Revolving is CSG-only, so the SDF backend refuses rather than approximating (SPEC B-4).

Return type:

Solid

stroke(width=1, closed=None, endcap1=CapType.ROUND, endcap2=CapType.ROUND, joints=CapType.ROUND)[source]

Stroke every path in the region (closed) and return the union as a Region.

Parameters:
width : float

Stroke width.

closed : bool | None

Accepted for signature parity; a region’s paths are always stroked closed.

endcap1 : CapType | CapSpec

Cap style for the start of each path.

endcap2 : CapType | CapSpec

Cap style for the end of each path.

joints : CapType | CapSpec

Cap style where segments meet.

Returns:

A Region of the stroked outlines.

Return type:

Region

dashed_stroke(dashpat=None, closed=None, fit=True, mindash=0.5)[source]

Break every path in the region into dashed polygon outlines.

Returns a Region of all dash polygons.

Parameters:
dashpat : Any

The dash pattern, as alternating on and off lengths.

closed : bool | None

Join the last point back to the first.

fit : bool

Stretch the pattern so a whole number of dashes fits the path.

mindash : float

Shortest dash to keep; anything shorter is dropped.

Return type:

Region

debug_region(size=1, vertices=True)[source]

Visualize this region with vertex labels for debugging.

Produces the filled region as a thin flat solid with every path’s vertices labelled in red – path a gets labels a0, a1, ..., path b b0, b1, ... (BOSL2 debug_region()). A single-path region defers to debug_polygon().

Parameters:
size : float

Text size for vertex labels.

vertices : bool

If False, omit vertex labels and return only the filled region.

Returns:

A Bosl2Solid.

Return type:

Any

intersection(other)[source]

Return the 2-D intersection of this region with other (the area they share).

Uses shapely for exact polygon coordinates. The result inherits self’s colour.

Parameters:
other : Region | Path2D

the region to intersect with.

Returns:

A Region with the intersection area.

Return type:

Region

Examples

Two overlapping squares share a rectangular strip:

from pybosl2 import Region

a = Region([[0, 0], [40, 0], [40, 30], [0, 30]])
b = Region([[20, 0], [60, 0], [60, 30], [20, 30]])
a.intersection(b).geometry().linear_extrude(height=3).show()
Loading 3-D preview…

⬇ Download STL mesh

union(other)[source]

Return the 2-D union of this region and other (all area covered by either).

Uses shapely for exact polygon coordinates. When self and other carry different colours, the union inherits self’s colour – the first path in the set wins the overlap.

Parameters:
other : Region | Path2D

the region to union with.

Returns:

A Region with the combined area.

Return type:

Region

Examples

Two adjacent squares merge into an L-shape:

from pybosl2 import Region

a = Region([[0, 0], [30, 0], [30, 30], [0, 30]])
b = Region([[20, 0], [50, 0], [50, 30], [20, 30]])
a.union(b).geometry().linear_extrude(height=3).show()
Loading 3-D preview…

⬇ Download STL mesh

difference(other)[source]

Return the 2-D difference: self with the area of other subtracted.

Uses shapely for exact polygon coordinates. The result inherits self’s colour.

Parameters:
other : Region | Path2D

the region to subtract.

Returns:

A Region with the subtracted area.

Return type:

Region

Examples

Punch a rectangular notch out of a square:

from pybosl2 import Region

plate = Region([[0, 0], [60, 0], [60, 40], [0, 40]])
notch = Region([[20, 10], [40, 10], [40, 30], [20, 30]])
plate.difference(notch).geometry().linear_extrude(height=4).show()
Loading 3-D preview…

⬇ Download STL mesh

symmetric_difference(other)[source]

Return the 2-D symmetric difference (XOR): area in either region but not both.

Uses shapely for exact polygon coordinates. The result inherits self’s colour.

Parameters:
other : Region | Path2D

the region to xor with.

Returns:

A Region with the symmetric difference area.

Return type:

Region

Path and Path3D are re-exported here for convenience but documented on the Paths page.