svg_path/offset

Path offset construction.

This module follows the same basic model as svgpathsio: lines and circular arcs are offset exactly, while other curves are offset by fitted cubic Beziers. Cubic approximations are checked by sampling the true normal extrusion of the source curve and measuring its distance to the proposed offset. If the error is too large, the source curve is split and each half is offset recursively.

Subpath and path offsets first normalize the source, split it into stalled and not-stalled pieces, fit the offset pieces, heal their boundaries, add joins, and cull adjacent reversal loops. A trimmed single offset then nodes that walk with its final refined zero-offset source. For each closed source subpath, the arrangement dual floods the signed source-side faces and removes offside offset occurrences. A second arrangement classifies the remaining offset edges by their measured and expected winding pairs, removes mismatches, burns dangling degree-1 edges, and reconstructs the survivors in source order.

Band construction shares source refinement between its two sides. Each side is first noded against the zero-offset source and trimmed in source order, retaining submerged runs that contain no reversed preimage. The two surviving sides are then noded together; winding mismatches and dangling degree-1 edges are removed before the final band walks are reconstructed. Because trimming can split an offset or remove it entirely, subpath and path offsets return Path.

The *_untrimmed helpers expose the untrimmed offset walk directly. It is useful for debugging, drawing raw construction geometry, or implementing a different trimming policy.

subpath_band and path_band construct two signed offset walks and trim them together without adding endpoint caps. subpath_stroke and path_stroke add endpoint caps for open subpaths. Closed strokes use the same capless per-subpath band construction.

Types

Cap style used at open stroke endpoints.

pub type Cap {
  Butt
  Square
  RoundCap
}

Constructors

  • Butt

    End the stroke exactly at the endpoint.

  • Square

    Extend the stroke by half its width beyond the endpoint.

  • RoundCap

    Add a semicircular endpoint cap.

Errors returned by offset helpers.

pub type Error {
  PathError(svg_path.Error)
  ArrangementGraphError(arrangement.Error)
  SourceNormalizationError(degeneracy.Error)
  InvalidTolerance(tolerance: Float)
  InvalidSamples(samples: Int)
  InvalidMaxDepth(max_depth: Int)
  InvalidMiterLimit(miter_limit: Float)
  InvalidStalledOffsetDiameter(diameter: Float)
  InvalidStrokeWidth(width: Float)
  BandSubpathNotClosed
  DegenerateTangent(t: Float)
  MaxDepthReached(error: Float)
  NonFinite
  InternalSegmentImageCountMismatch
  InternalEmptySegmentImage(segment_index: Int)
  InternalMissingEdgeImage(edge_id: Int)
  InternalMissingIndexedSegment(segment_index: Int)
  InternalMissingWindingOpinion(segment_index: Int)
  InternalSurvivorOrderMismatch
}

Constructors

  • PathError(svg_path.Error)

    An underlying path operation failed.

  • ArrangementGraphError(arrangement.Error)

    Arrangement construction failed while noding offset geometry.

  • SourceNormalizationError(degeneracy.Error)

    Source normalization failed before offset construction.

  • InvalidTolerance(tolerance: Float)

    The offset tolerance must be finite and greater than zero.

  • InvalidSamples(samples: Int)

    The number of divergence samples must be greater than zero.

  • InvalidMaxDepth(max_depth: Int)

    The recursive subdivision limit must be greater than zero.

  • InvalidMiterLimit(miter_limit: Float)

    The miter limit must be finite and greater than zero.

  • InvalidStalledOffsetDiameter(diameter: Float)

    The stalled offset diameter must be finite and non-negative.

  • InvalidStrokeWidth(width: Float)

    Stroke width must be finite and greater than zero.

  • BandSubpathNotClosed

    Band payloads used for inside classification must be closed.

  • DegenerateTangent(t: Float)

    A segment tangent was too small to define a stable normal direction.

  • MaxDepthReached(error: Float)

    Refinement could not produce an offset within the requested tolerance.

  • NonFinite

    A calculation produced a non-finite coordinate.

  • InternalSegmentImageCountMismatch

    Arrangement segment-image counts did not match offset segment counts.

  • InternalEmptySegmentImage(segment_index: Int)

    A source segment’s arrangement image contained no graph edges.

  • InternalMissingEdgeImage(edge_id: Int)

    An arrangement edge had no source-image record.

  • InternalMissingIndexedSegment(segment_index: Int)

    An arrangement source image referenced no indexed offset segment.

  • InternalMissingWindingOpinion(segment_index: Int)

    An indexed offset segment had no winding-side opinion.

  • InternalSurvivorOrderMismatch

    Surviving arrangement segments no longer formed their source-order walk.

Cubic fitting controls used by the recursive offset builders.

tolerance bounds the sampled geometric error of a fitted offset curve, samples controls the number of check samples, and max_depth limits recursive subdivision.

pub type FittingOptions {
  FittingOptions(tolerance: Float, samples: Int, max_depth: Int)
}

Constructors

  • FittingOptions(tolerance: Float, samples: Int, max_depth: Int)

Join style used when offsetting adjacent subpath segments.

This covers the common SVG stroke-linejoin values bevel, miter, and round. SVG 2 also describes miter-clip and arcs; those are not exposed here yet.

pub type Join {
  Bevel
  Miter(miter_limit: Float)
  Round
}

Constructors

  • Bevel

    Connect adjacent offset segments with a straight line.

  • Miter(miter_limit: Float)

    Extend the offset tangents toward their intersection when the miter stays within miter_limit; otherwise fall back to Bevel.

  • Round

    Connect adjacent offset segments with a circular SVG arc.

Options for offset construction.

fitting controls offset approximation. trimming controls projection and root-finding used while pruning. stalled_offset_diameter decides when the stalled-run builder treats an offset piece as too small to keep as an ordinary independently fitted segment. tangent_heal_angle_degrees is the maximum tangent direction mismatch, in degrees, allowed by post-healing continuity checks at stable smooth boundaries.

pub type Options {
  Options(
    fitting: FittingOptions,
    trimming: svg_path.DistanceOptions,
    stalled_offset_diameter: Float,
    tangent_heal_angle_degrees: Float,
    join: Join,
  )
}

Constructors

Values

pub fn default_fitting_options() -> FittingOptions

Return default options for offset construction.

pub fn default_options() -> Options

Return default options for offset construction.

pub fn path(
  path: svg_path.Path,
  distance distance: Float,
) -> Result(svg_path.Path, Error)

Offset every subpath in a path by a signed distance.

pub fn path_band(
  path: svg_path.Path,
  distance_a distance_a: Float,
  distance_b distance_b: Float,
) -> Result(svg_path.Path, Error)

Offset every subpath in a path at two signed distances and trim each pair of sides together.

pub fn path_band_untrimmed(
  path: svg_path.Path,
  distance_a distance_a: Float,
  distance_b distance_b: Float,
) -> Result(svg_path.Path, Error)

Offset every subpath in a path at two signed distances without trimming any side.

pub fn path_band_untrimmed_with(
  path path: svg_path.Path,
  distance_a distance_a: Float,
  distance_b distance_b: Float,
  options options: Options,
) -> Result(svg_path.Path, Error)

Offset every subpath in a path at two signed distances without trimming any side, using explicit options.

pub fn path_band_with(
  path path: svg_path.Path,
  distance_a distance_a: Float,
  distance_b distance_b: Float,
  options options: Options,
) -> Result(svg_path.Path, Error)

Offset every subpath in a path at two signed distances using explicit options.

pub fn path_stroke(
  path: svg_path.Path,
  width width: Float,
) -> Result(svg_path.Path, Error)

Stroke every subpath in a path with the default butt cap.

pub fn path_stroke_with(
  path path: svg_path.Path,
  width width: Float,
  cap cap: Cap,
  options options: Options,
) -> Result(svg_path.Path, Error)

Stroke every subpath in a path using explicit cap and offset options.

pub fn path_untrimmed(
  path: svg_path.Path,
  distance distance: Float,
) -> Result(svg_path.Path, Error)

Offset every subpath in a path without trimming self-intersections.

pub fn path_untrimmed_with(
  path path: svg_path.Path,
  distance distance: Float,
  options options: Options,
) -> Result(svg_path.Path, Error)

Offset every subpath in a path without trimming self-intersections using explicit options.

pub fn path_with(
  path path: svg_path.Path,
  distance distance: Float,
  options options: Options,
) -> Result(svg_path.Path, Error)

Offset every subpath in a path by a signed distance using explicit options.

pub fn segment(
  segment: svg_path.Segment,
  distance distance: Float,
) -> Result(svg_path.Subpath, Error)

Offset one segment by a signed distance.

Positive distances offset to the visual left of the segment direction. For a line from (0, 0) to (10, 0), distance: 2.0 returns a line from (0, -2) to (10, -2).

Curves return an open subpath because the result may need several pieces to stay within tolerance. Circular arcs offset to circular arcs; non-circular arcs and Beziers use cubic fitting.

pub fn segment_with(
  segment segment: svg_path.Segment,
  distance distance: Float,
  options options: Options,
) -> Result(svg_path.Subpath, Error)

Offset one segment by a signed distance using explicit options.

pub fn subpath(
  subpath: svg_path.Subpath,
  distance distance: Float,
) -> Result(svg_path.Path, Error)

Offset a subpath by a signed distance.

Positive distances offset to the visual left of the subpath direction. Adjacent offset segments are connected using default_options().join. The result is a path because trimming self-intersections can split the offset into multiple subpaths or remove it entirely.

Trimming nodes the untrimmed offset together with its final refined zero-offset source pieces, removes zero-source-only, submerged, and dangling arrangement edges, and reconstructs surviving offset edges in untrimmed traversal order.

pub fn subpath_band(
  subpath: svg_path.Subpath,
  distance_a distance_a: Float,
  distance_b distance_b: Float,
) -> Result(svg_path.Path, Error)

Offset a subpath at two signed distances and trim the two sides together.

No endpoint caps are added. The two untrimmed offset walks are trimmed together as a capless band. This supports ordinary capless stroke sides, one-sided bands, and asymmetric bands such as two positive offsets.

pub fn subpath_band_untrimmed(
  subpath: svg_path.Subpath,
  distance_a distance_a: Float,
  distance_b distance_b: Float,
) -> Result(svg_path.Path, Error)

Offset a subpath at two signed distances without trimming either side.

This returns the two untrimmed offset walks in one path, with the distance_a side first and the distance_b side second. No caps, bridges, pairwise trimming, self-intersection pruning, or fill-rule interpretation are added.

pub fn subpath_band_untrimmed_with(
  subpath subpath: svg_path.Subpath,
  distance_a distance_a: Float,
  distance_b distance_b: Float,
  options options: Options,
) -> Result(svg_path.Path, Error)

Offset a subpath at two signed distances without trimming either side, using explicit options.

pub fn subpath_band_with(
  subpath subpath: svg_path.Subpath,
  distance_a distance_a: Float,
  distance_b distance_b: Float,
  options options: Options,
) -> Result(svg_path.Path, Error)

Offset a subpath at two signed distances using explicit options.

Each synchronized side is first noded and trimmed on its own. A submerged run without any reversed source preimage is retained because it does not represent a reversal-generated fold. The surviving sides are then assembled into a band and trimmed together using their directed winding-side opinions.

pub fn subpath_offset_map(
  subpath: svg_path.Subpath,
) -> Result(
  fn(svg_path.Point) -> Result(svg_path.Point, Error),
  Error,
)

Build a local coordinate map around a subpath.

The returned function interprets its input point as local path coordinates: x is true arc length along the source subpath, and y is signed offset from that point. Positive offsets use this module’s usual convention: to the visual left of the subpath direction.

Open subpaths reject x values outside 0.0..subpath_length. Closed subpaths wrap x modulo the subpath length. Empty and zero-length subpaths cannot define a stable normal direction and return an error.

pub fn subpath_offset_map_with(
  subpath: svg_path.Subpath,
  options options: svg_path.LengthOptions,
) -> Result(
  fn(svg_path.Point) -> Result(svg_path.Point, Error),
  Error,
)

Build a local coordinate map around a subpath using explicit length options.

pub fn subpath_stroke(
  subpath: svg_path.Subpath,
  width width: Float,
) -> Result(svg_path.Path, Error)

Stroke a subpath with the default butt cap.

Open subpaths build one closed untrimmed stroke boundary from the two offset sides and endpoint caps, then keep sections that separate points inside the intended stroke from points outside it. Closed subpaths use the same capless construction as subpath_band.

pub fn subpath_stroke_with(
  subpath subpath: svg_path.Subpath,
  width width: Float,
  cap cap: Cap,
  options options: Options,
) -> Result(svg_path.Path, Error)

Stroke a subpath using explicit cap and offset options.

pub fn subpath_untrimmed(
  subpath: svg_path.Subpath,
  distance distance: Float,
) -> Result(svg_path.Subpath, Error)

Offset a subpath without trimming self-intersections.

This returns the untrimmed one-sided offset walk. Adjacent segment offsets are connected with default_options().join; the result may self-intersect or contain sections that a trimmed offset would remove.

pub fn subpath_untrimmed_with(
  subpath subpath: svg_path.Subpath,
  distance distance: Float,
  options options: Options,
) -> Result(svg_path.Subpath, Error)

Offset a subpath without trimming self-intersections using explicit options.

pub fn subpath_with(
  subpath subpath: svg_path.Subpath,
  distance distance: Float,
  options options: Options,
) -> Result(svg_path.Path, Error)

Offset a subpath by a signed distance using explicit options.

Search Document