svg_path_editor.path_shade#

Attributes#

Classes#

ImageFormat

Abstract RGBA image encoder.

WebpFormat

Lossless WebP encoder.

PngFormat

PNG encoder.

PathShading

SVG fragments for shaded bevels.

Functions#

lambert_from_angle(normal, *[, z_light])

Lambertian diffuse intensity from a 2D surface normal.

lambert_shading_base64(*, r, phi, locally_convex, ...)

Render a Lambert–shaded elliptical cone; return encoded bytes and base64 URI.

shade_path(svg, *, d, resolution[, z_light, ...])

Per-bevel Lambert shading for an SVG path.

Module Contents#

svg_path_editor.path_shade.lambert_from_angle(normal, *, z_light=1)[source]#

Lambertian diffuse intensity from a 2D surface normal.

The 2D normal is interpreted in the \(xy\)-plane, with an implicit \(z\)-component 1:

\[\mathbf{n} = (n_x, n_y, 1).\]

The light position is

\[\mathbf{L}_\text{pos} = (0, 1, z_{\text{light}}),\]

and the light direction is

\[\hat{\mathbf{L}} = \frac{\mathbf{L}_\text{pos}}{\|\mathbf{L}_\text{pos}\|}.\]

The Lambert intensity is

\[I = \max(0, \hat{\mathbf{n}} \cdot \hat{\mathbf{L}}),\]

where \(\hat{\mathbf{n}}\) is the normalized surface normal.

Parameters:
  • normal (Point) – 2D surface normal in the \(xy\)-plane.

  • z_light (Number) – \(z\) height of the light source.

Returns:

Lambertian intensity in \([0, 1]\).

Return type:

decimal.Decimal

class svg_path_editor.path_shade.ImageFormat[source]#

Bases: Protocol

Abstract RGBA image encoder.

Variables:
  • media_type – MIME type of the encoded image.

  • extension – File extension (without dot).

media_type: ClassVar[str]#
extension: ClassVar[str]#
encode(data)[source]#

Encode an RGBA image.

Parameters:

data (numpy.ndarray) – (H, W, 4) uint8 array in RGBA order.

Returns:

Encoded image bytes.

Return type:

bytes | bytearray

class svg_path_editor.path_shade.WebpFormat[source]#

Lossless WebP encoder.

Uses imagecodecs.webp_encode(lossless=True)().

media_type: ClassVar[str] = 'image/webp'#
extension: ClassVar[str] = 'webp'#
encode(data)[source]#
Parameters:

data (numpy.ndarray)

Return type:

bytes | bytearray

class svg_path_editor.path_shade.PngFormat[source]#

PNG encoder.

Uses imagecodecs.png_encode(level=9)().

media_type: ClassVar[str] = 'image/png'#
extension: ClassVar[str] = 'png'#
encode(data)[source]#
Parameters:

data (numpy.ndarray)

Return type:

bytes | bytearray

svg_path_editor.path_shade.WEBP#
svg_path_editor.path_shade.PNG#
svg_path_editor.path_shade.lambert_shading_base64(*, r, phi, locally_convex, resolution, z_light=1.0, t=0.5, format=WEBP, seed=None)[source]#

Render a Lambert–shaded elliptical cone; return encoded bytes and base64 URI.

The cone radii are \(r = (r_x, r_y)\) in image space. For a point \((x, y)\) on the grid

\[(x, y) \in \left[-\frac{1}{r_x}, \frac{1}{r_x}\right] \times \left[-\frac{1}{r_y}, \frac{1}{r_y}\right],\]

the unnormalized normal is

\[\mathbf{n}(x, y) = \left(\frac{x}{r_x^2}, \frac{y}{r_y^2}, 1\right).\]

The Lambert term is

\[I(x, y) = \max\left(0, \hat{\mathbf{n}}(x, y)\cdot\hat{\mathbf{L}}\right),\]

where \(\hat{\mathbf{L}}\) is the unit vector from the light position \((0, s, z_{\text{light}})\) with \(s = -1\) if locally_convex else \(+1\), rotated in the \(xy\)-plane by \(-\varphi\) degrees.

Grayscale is binary (0 or 255) according to \(I > t\). Alpha is a symmetric remap of \(I\) around \(t\):

\[\begin{split}\alpha(I) = \begin{cases} \dfrac{I - t}{1 - t}, & I \geq t,\\ \dfrac{t - I}{t}, & I < t. \end{cases}\end{split}\]

Before 8-bit quantization, uniform noise in \([0, 1]\) is added to alpha for dithering.

The final RGBA image is encoded with format and returned both as raw bytes and as a data:...;base64,... URI.

Parameters:
  • r (Point) – Ellipse radii in \(x\) and \(y\).

  • phi (decimal.Decimal) – Rotation in degrees of the light direction in image space (clockwise in screen coordinates).

  • locally_convex (bool) – If true, the base light position is \((0, -1, z_{\text{light}})\), otherwise \((0, 1, z_{\text{light}})\).

  • resolution (float) – Pixels per SVG unit. Image size is \(\lceil 2 r_x \, \mathrm{resolution} \rceil \times \lceil 2 r_y \, \mathrm{resolution} \rceil\).

  • z_light (float) – \(z\) height of the light source.

  • t (float) – Neutral Lambert intensity in \([0, 1]\) (threshold).

  • format (ImageFormat) – Image format to use.

  • seed (int | None) – RNG seed for alpha dithering; None is non-deterministic.

Returns:

(img_bytes, img_data_uri); the URI is suitable for SVG href.

Return type:

tuple[bytes, str]

class svg_path_editor.path_shade.PathShading[source]#

SVG fragments for shaded bevels.

Variables:
  • defs_body – Elements for a document-wide <defs> (e.g. shared <image> or <clipPath> definitions).

  • body – Per-path drawing elements (e.g. <path>, <g>, <use>) that reference defs_body.

defs_body: list[str]#
body: list[str]#
svg_path_editor.path_shade.shade_path(svg, *, d, resolution, z_light=1, threshold=0.5, max_opacity=1, format=WEBP, shade_offset=0, clip_offset=0, seed=None, prec=None, fmt_spec='')[source]#

Per-bevel Lambert shading for an SVG path.

The path is decomposed into bevel regions via svg_path_editor.path_offset.bevel_path().

  • Flat bevel polygons are shaded analytically with lambert_from_angle().

  • Curved bevel arcs are shaded using a small Lambert RGBA texture from lambert_shading_base64(), referenced by <image> / <use> and clipped to the arc geometry.

For each bevel polygon:

  • One <path> is emitted with fill="white" or fill="black" depending on whether the intensity is above or below threshold.

  • Opacity is a symmetric remap of the intensity around threshold in \([0, 1]\), scaled by max_opacity.

For each bevel arc:

  • A Lambert cone texture is generated (or reused from a cache) for (r.x, r.y, phi, locally_convex).

  • A base <image> at the origin with size \(2 r_x \times 2 r_y\) is placed in PathShading.defs_body once per unique key.

  • For each occurrence, a <clipPath> with the bevel geometry and a <use> of the base image are emitted into PathShading.body, translated so the image origin matches the lower-left corner of the ellipse bounding box, and rotated by phi around the ellipse center if non-zero.

  • <use> carries opacity max_opacity when it is less than 1.

To integrate into an SVG, place all defs_body entries inside a single document-level <defs> and insert body where the path should render.

Parameters:
  • svg (SvgPath) – Input path to bevel and shade.

  • d (Number) – Offset distance for bevel_path().

  • resolution (float) – Pixels per SVG unit for generated textures.

  • z_light (Number) – \(z\) height of the light source.

  • threshold (Number) – Neutral Lambert intensity in \([0, 1]\), shared between flat bevels and textures.

  • max_opacity (Number) – Global opacity scale in \([0, 1]\).

  • format (ImageFormat) – Texture format, e.g. WEBP or PNG.

  • shade_offset (int) – Starting index for generated shade IDs.

  • clip_offset (int) – Starting index for generated clip-path IDs.

  • seed (int | None) – RNG seed for alpha dithering in lambert_shading_base64(); None for non-deterministic.

  • prec (Precision | Literal['auto', 'auto-intersections'] | None) – Precision/geometry mode passed to bevel_path(). "auto" and "auto-intersections" select automatic strategies; None uses the default.

  • fmt_spec (str) – The formatting specification for the paths.

Returns:

PathShading with SVG fragments for defs and body.

Return type:

PathShading