Svg

Load SVG drawings into a Region of outlines

Load SVG drawings into a Region of outlines.

pybosl2.svg.svg_outlines(file, fn=None, fa=None, fs=2.0, flip_y=True)[source]

Return an SVG’s outlines as plain [[x, y], ...] point rings.

Every subpath of every shape becomes one closed ring. Curves (cubic/quadratic béziers and arcs) are flattened to sampled points; the number of samples is controlled by fn, fa and fs (the same $fn/$fa/$fs triplet OpenSCAD uses). Straight segments keep their endpoints.

The raw rings, with no nesting applied – see region_from_svg() to get a Region with holes resolved.

Parameters:
file : str

Path to the SVG.

fn : int | None

Minimum fragment count per curved segment. When set (>= 3) it gives an absolute point count; otherwise points are derived from fs. Omitted, the ambient use_defaults(fn=...) value applies; fn=0 opts back out to fa/fs.

fa : float | None

Minimum angle in degrees between fragments (accepted for API parity; only relevant for circular arcs). Omitted, the ambient use_defaults(fa=...) value applies.

fs : float

Minimum fragment size in SVG user units. Each curved segment gets max(3, ceil(length / fs)) points. Omitted, the ambient use_defaults(fs=...) value applies.

flip_y : bool

Negate Y. SVG’s Y axis points DOWN and OpenSCAD’s points UP, so without this every imported drawing comes out mirrored.

Returns:

One list of [x, y] points per ring, in the SVG’s own USER UNITS. The renderer’s importer instead converts px to mm at 72 dpi (a factor of 25.4/72); nothing here guesses at a physical size, so resize the result to whatever the part needs.

Raises:

ImportError – If svgelements is not installed.

Return type:

list[list[list[float]]]

pybosl2.svg.region_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.

The alternative to handing the file to the renderer’s importer: that returns an opaque handle you can only transform, while this returns real outlines you can measure, offset, round, tessellate or use as a lid pattern – and it needs no renderer, so SVG-derived shapes become testable without one.

Nesting is resolved with the EVEN-ODD rule (even_odd()) among shapes of the SAME colour – what a traced drawing means by a hole: an outline inside one solid cuts it, an outline inside that hole is solid again. Shapes of DIFFERENT colours are composited in SVG paint order instead, each covering whatever was drawn before it, so the returned pieces never overlap.

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; see svg_outlines()). 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 (e.g. "#ff0000"). Pass None (the default) to use the SVG’s own colours.

strokes : str

"polygon" (default) converts stroked paths into 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 if one is declared, so shapes that paint outside the intended canvas are trimmed away.

Returns:

A Region of the drawing.

Raises:

ImportError – If svgelements is not installed.

Return type:

Region

Examples

An imported drawing, inset and extruded into a plate:

import os, tempfile
tmp = tempfile.NamedTemporaryFile(suffix=".svg", mode="w", delete=False)
tmp.write('<svg xmlns="http://www.w3.org/2000/svg"><path d="M10,10H90V90H10Z"/></svg>')
tmp.close()

from pybosl2 import Region

result = Region.from_svg(tmp.name).offset(delta=-0.5).geometry().linear_extrude(height=2)
result.show()
os.unlink(tmp.name)
pybosl2.svg.svg_rings_with_colors(file, fn=None, fa=None, fs=2.0, flip_y=True, color=None, strokes='polygon', clip_to_viewbox=True)[source]

Every closed ring in an SVG, flattened, with the fill colour of each.

The element each ring came from is NOT preserved – use svg_element_groups() when that matters (it does for compositing: nesting means “hole” only WITHIN one element).

Parameters:
file : str

Path to the SVG file to read.

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

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

flip_y : bool

Flip the Y axis, since SVG counts Y downwards and this library counts it up.

color : str | None

Colour to use for rings the file does not give one.

strokes : str

How to treat stroked-but-unfilled paths.

clip_to_viewbox : bool

Drop anything outside the document’s viewBox.

Return type:

tuple[list[‘Path2D’], list[str | None]]

pybosl2.svg.svg_element_groups(file, fn=None, fa=None, fs=2.0, flip_y=True, color=None, strokes='polygon', clip_to_viewbox=True)[source]

Return an SVG’s shapes, IN PAINT ORDER, each with its own rings kept together.

This grouping is the thing compositing needs and flattening destroys: a ring nested inside another means “hole” only when both came from the SAME element (that is what fill-rule decides). Two separate elements that happen to nest – a flag’s green field drawn on top of its red one – are not a solid and a hole, they are two shapes, the later one painted over the earlier.

Every subpath of every shape becomes one closed Path2D; the colour list gives the hex fill colour of the shape each ring came from (or None for shapes with no fill / pattern fills). The rings themselves are identical to those returned by svg_outlines() but as typed objects.

The return value can be unpacked directly:

paths, colors = svg_rings_with_colors("drawing.svg")
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; see svg_outlines()). 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.

color : str | None

When set, every returned ring gets this colour instead of the SVG’s own fill colours. Pass None (the default) to use the colours from the SVG.

strokes : str

How to handle stroked paths. "polygon" (default) converts each stroked path to a filled polygon via shapely buffering so strokes appear as solid coloured pieces. "ignore" skips shapes that have a stroke but no fill colour.

clip_to_viewbox : bool

When True (the default), clip shapes to the SVG’s viewBox if one is declared.

Returns:

[(colour, [Path2D, ...]), ...] in document (paint) order – one entry per filled shape, plus one per stroked shape (a stroke paints over its own fill).

Raises:

ImportError – If svgelements is not installed.

Return type:

list[tuple[str | None, list[Path2D]]]

pybosl2.svg.regions_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 list of Region objects, coloured.

Shapes with the same fill colour are grouped into one Region; shapes with no fill colour (or pattern/gradient fills) are grouped into an uncoloured Region. The result is a list of Regions that can each be offset, extruded or rendered independently, each carrying the SVG’s colour through to the output.

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; see svg_outlines()). 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.

color : str | None

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

strokes : str

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

clip_to_viewbox : bool

When True (the default), clip shapes to the SVG’s viewBox if one is declared.

Returns:

A list of Region objects, one per distinct fill colour found in the SVG. Each Region has its colour set via Region.color().

Raises:

ImportError – If svgelements is not installed.

Return type:

list[‘Region’]

Examples

An imported drawing with two colours, extruded and shown side by side:

import os, tempfile
tmp = tempfile.NamedTemporaryFile(suffix=".svg", mode="w", delete=False)
tmp.write('<svg xmlns="http://www.w3.org/2000/svg">'
          '<path d="M10,10H40V40H10Z" fill="#ff0000"/>'
          '<path d="M50,10H80V40H50Z" fill="#0000ff"/>'
          '</svg>')
tmp.close()

from pybosl2.svg import regions_from_svg

parts = regions_from_svg(tmp.name)
import pythonscad as ps
for region in parts:
    region.geometry().linear_extrude(height=3)
ps.show()
os.unlink(tmp.name)