Sign in

    moonclip

    Integer and decimal polygon Boolean, PolyTree, offset, stroke, simplification and Minkowski Sum for MoonBit

    geometry
    polygon
    Download zip
    Version
    0.2.1
    License
    BSL-1.0
    Last updated
    3 days ago
    Downloads
    23

    No dependencies

    #MoonClip

    MoonClip 是用 MoonBit 实现的二维多边形几何库,提供整数与统一十进制网格的小数入口。0.2 系列提供整数/小数 Boolean、偏移与描边的孔洞层级输出。运行库不依赖 C++ 或其他几何运行时。

    版本内容见 CHANGELOG,下载、校验和及验收摘要见 GitHub Release v0.2.0。0.2.0 发布记录区分发布源码、注册表安装和后续状态提交;0.1.0 历史记录保持原样,成果说明保留来源、贡献和申报边界。

    #从 Mooncakes 使用

    在自己的 MoonBit 模块中运行:

    moon add BigSaltyMan/moonclip@0.2.1

    在包的 moon.pkg 中导入 BigSaltyMan/moonclip,配置见下文。0.2.0 已完成独立 native/JS 注册表消费者验证;0.2.1 只修正文档,运行库与公开接口保持一致。数值与预算边界按 scope.md 解释。

    #0.1.0 已有、0.2.0 继续提供的能力

    用途公开入口成功结果
    区域布尔boolean_op / boolean_op_d整数 / 小数平铺轮廓
    孔洞层级boolean_tree整数 PolyTree64
    闭合区域偏移offset_polygons / offset_polygons_d整数 / 小数平铺轮廓
    开放路线加宽stroke_polyline / stroke_polyline_d整数 / 小数多边形区域
    显式简化simplify_paths / simplify_paths_d轮廓、删点数及三类退化索引
    单图案扫掠minkowski_sum整数平铺轮廓

    Boolean 支持 Intersection、Union、Difference(Subject 减 Clip)、Xor,以及公开 EvenOdd / NonZero;Positive / Negative 仍是内部能力。Offset 和 Stroke 支持 Miter、Bevel、Square、Round 四种连接;Stroke 提供 Butt、Square、Round 三种端点。方向、边界框、精确两倍有向面积、点包含及无损重复点整理也已实现。完整接口见 pkg.generated.mbti,入口默认值与限制见 支持范围。

    #0.2.0 新增树接口

    下列增量已随 0.2.0 正式发布,并完成 registry 来源的独立 native/JS 消费者验证。新增库入口共 5 个:小数 Boolean 树与整数/小数偏移树、描边树;整数 boolean_tree 已在首版提供。0.2 系列均提供这些入口,0.1.0 不包含这些增量。完整发布事实见 0.2.0 记录。

    只需要结果边界可继续使用平铺入口;需要孔洞归属则直接调用对应树入口;已有树可用 to_paths() 取得独立轮廓副本,但会丢失显式层级。完整迁移路径见树接口升级说明,历史 CI、测试口径与六入口候选消费者见增量验收材料。

    stroke_tree(path, width, cap=, options=, limits=) 返回 Result[PolyTree64, ClipError],stroke_tree_d(path, width, precision=, cap=, options=, limits=) 返回 Result[PolyTreeD, ClipError]。width 是完整宽度,默认 Butt,支持三种端点和四种连接。输入是一条开放路线,返回清理后填充区域的层级;首尾重合仍保留开放端点语义。输入整理、生成、清理、树构建和解码共用预算。两个描边树入口均未随 0.1.0 发布,示例见 组合说明。

    offset_tree(paths, delta, fill_rule=, options=, limits=) 返回 Result[PolyTree64, ClipError];offset_tree_d(paths, delta, precision=, fill_rule=, options=, limits=) 返回 Result[PolyTreeD, ClipError]。默认 NonZero,沿用四种连接与现有偏移参数。树表达最终偏移区域,扩张、收缩可能合并区域、分裂轮廓或消灭孔洞。规范化、生成、清理、树构建及小数解码共用一份累计预算。两个偏移树入口均未随 Mooncakes 0.1.0 发布。示例见 组合说明。

    boolean_tree_d(subject, clip, operation, precision=, fill_rule=, limits=) 返回 Result[PolyTreeD, ClipError],默认 Precision=3、EvenOdd。PolyTreeD 提供 roots() / to_paths();节点提供 contour() / children() / depth() / is_hole(),访问器数组均独立复制。

    该入口已随 0.2.0 发布,Mooncakes 0.1.0 不包含此接口。层级沿用量化后整数区域已验证的拓扑,Precision 变化可能影响可表达关系;坐标解码为 Double,to_paths() 会丢失显式父子关系。用法及预算见 小数树说明,可运行的完整断言见下文 CLI 示例。当前源码接口见 .mbti;首版 0.1.0 的接口范围仍按历史记录解释。

    #数据与使用边界

    • Point64 保存不可变的完整 Int64;Path64 / Paths64 是有序可变数组。几何运算的每轴范围为 ±1,000,000,000,包含边界。存储范围与运算范围不同。
    • 构造原样保存空路径、单点、两点及重复点;闭合轮廓通常不重复存储首点,由算法解释闭合边。结构比较严格保留坐标、长度和顺序。
    • 数组赋值可以共享存储。需要隔离时用 copy_path / copy_paths(小数有 _d 版本);集合复制同时隔离内外数组和输入别名。
    • Precision::new 支持 0—6,默认 3。小数公开调用先检查并量化到十进制网格,再执行整数核心;范围随精度缩小。量化可能报合并或退化错误,不能把它当成任意精度浮点几何。
    • 计算入口返回 Result,失败不修改输入、不发布部分结果。默认原始输入预算为 10,000 条路径、100,000 个点;内部另有累计节点和工作预算,合法坐标及数量不保证复杂输入一定完成。
    • simplify_paths 需要显式调用,不保证拓扑保持。面积为零不能直接判断自交输入没有区域;point_in_polygon 仅解释单轮廓 EvenOdd。

    数值、舍入、错误和预算详见 numerics.md、decimal.md、composition.md。孔洞方向、简化报告、扫掠闭合语义及尚未提供的能力集中列在 scope.md。

    #从本地源码运行

    使用 固定工具链。Windows 使用 Git Bash;Linux 使用 Bash:

    git clone https://github.com/BigSaltyMan/moonclip.git cd moonclip moon run --target native cmd/example

    cmd/example/main.mbt 是受检的公开调用示例,包含复制隔离、精确几何、Boolean / PolyTree、带孔偏移、路线描边、小数组合、显式简化和图案扫掠。它检查完整轮廓与错误,成功输出:

    Decimal tree: outer/hole/island OK Simplify vertices: 9 -> 8; input_degenerate=[2]; degenerate=[2]; collapsed=[]; offset/tree OK Minkowski: rectangle/polyline, offcenter/diagonal and closed route/hole OK MoonClip geometry, Boolean, PolyTree, offset, stroke, composition, decimal, simplify and Minkowski OK

    该包的 moon.pkg 导入根模块 BigSaltyMan/moonclip。0.2.0 包内示例检查小数树、偏移树及描边树;0.1.0 的旧示例和注册表消费者仍按该版本接口解释。

    导入配置与随包示例相同(cmd/example/moon.pkg):

    import {
    "BigSaltyMan/moonclip",
    }

    pkgtype(kind: "executable")

    包导入配置与 cmd/example/main.mbt 是受检的使用起点,各类 API 按上表进入 scope.md。发布 ZIP 包含运行库、接口、该示例和文字文档;演示、测试、参考工具与基准从 Git 仓库使用。scripts/check_release.py 只审计源码候选及解压消费者,不执行发布;0.2.0 的真实注册表安装验证见 发布记录。

    #浏览器交互演示

    cd demo npm run dev

    打开 http://127.0.0.1:4173。区域 Boolean、带孔偏移、路线加宽三个场景可切换整数 / 小数及 Precision;路线页签另有仅整数的图案扫掠。显式简化默认关闭,出现退化时显示报告并停止后续计算。计算调用 MoonBit 核心,JS 处理交互、绘制和下载。

    演示无 npm 依赖,无需先安装包。在 demo 目录运行 npm test 复用已构建产物做 20 项桥接/导出冒烟;npm run build 生成可静态托管的 demo/dist,npm run build -- --reuse 可只打包已有产物。SVG 独立打开并保留孔洞;JSON 保存原输入、参数、精度、真实结果及简化统计。输入变更或错误会禁用导出。

    0.2.0 源码的演示提供孔洞层级查看:Boolean、Offset、Stroke 的整数/小数六个树入口可展开父子节点、选择边界高亮;SVG 仍只导出填充区域,树 JSON 使用版本 2 保存层级与可复现请求。平铺模式默认行为和版本 1 JSON 保持兼容,Minkowski 只支持平铺。

    操作说明见 demo.md,三张主要场景截图及实际操作记录见 浏览器验收。

    #检查与性能基线

    bash scripts/check.sh bash scripts/bench.sh --target native --report _build/bench/report.json

    统一检查执行格式、接口、native / JS 回归、构建、CLI 示例和离线参考工具;普通 CI 另构建演示、执行桥接冒烟及一个极小基准入口。修改代码后运行 moon info && moon fmt 并审查接口变化。

    已发布 0.1.0 的正确性基线为同一批 2234 项库测试。0.2.0 相对首版累计新增 89 项树定向测试(小数 Boolean 33、偏移 27、描边 29),当前源码完整回归计数为 2323;演示的 20 项桥接测试独立统计,native、JS 分别执行,不相加。1384 条独立参考案例对应 2276 项能力检查,旧数据未改,也不宣称覆盖新增入口。固定种子只覆盖公开 Boolean 平铺结果。覆盖矩阵及既有执行证据见 兼容报告,失败缩减见 随机回归说明。

    native release 的 11 个固定基准(含三个有上限的压力探针)已测量,环境、输入输出规模、中位数及最小 / 最大值见 性能基线。没有测上游速度,不作跨库性能结论;CI 不设耗时阈值。

    #来源与许可

    固定参考 Clipper2 2.0.1 / Clipper2_2.0.1 / 21ebba05db8894f0c7217ad35ea518080f324946,BSL-1.0。C# 是主要引擎阅读和移植参考;固定 C++ 实现作为测试参考适配器,仅用于生成和复核数据。普通 CI 读取已提交参考,不下载、构建或执行 C++。

    Path64

    type Path64 = Array[Point64]

    An ordered, mutable point array. No normalization is implicit.

    PathD

    type PathD = Array[PointD]

    An Array is a collection of values that supports random access and can grow in size.

    Paths64

    type Paths64 = Array[Array[Point64]]

    An ordered, mutable path array; paths may be empty or share storage.

    PathsD

    type PathsD = Array[Array[PointD]]

    An Array is a collection of values that supports random access and can grow in size.

    Axis

    pub(all) enum Axis {
    X
    Y
    } derive(Eq,
    Debug
    )

    Bounds64

    pub(all) struct Bounds64 {
    min_x : Int64
    min_y : Int64
    max_x : Int64
    max_y : Int64
    } derive(Eq,
    Debug
    )

    CapType

    pub(all) enum CapType {
    Butt
    Square
    Round
    } derive(Eq,
    Debug
    )

    ClipError

    pub(all) enum ClipError {
    InvalidInput(ErrorLocation, String)
    CoordinateOutOfRange(ErrorLocation, Axis, Int64)
    ExecutionFailure(ErrorLocation, String)
    NumericFailure(ErrorLocation, String)
    ResourceLimitExceeded(ErrorLocation, ResourceKind, Int)
    QuantizationFailure(ErrorLocation, Axis?, ErrorLocation?, String)
    DecimalCoordinateOutOfRange(ErrorLocation, Axis, Double, Int)
    } derive(Eq,
    Debug
    )

    Errors are returned as values; messages are English.

    ClipError::message

    fn ClipError::message(self : ClipError) -> String

    ClipType

    pub(all) enum ClipType {
    Intersection
    Union
    Difference
    Xor
    } derive(Eq,
    Debug
    )

    ErrorLocation

    pub(all) struct ErrorLocation {
    stage : Stage
    path_index : Int?
    vertex_index : Int?
    } derive(Eq,
    Debug
    )

    Indices are zero-based in the original input, before normalization.

    FillRule

    pub(all) enum FillRule {
    EvenOdd
    NonZero
    } derive(Eq,
    Debug
    )

    JoinType

    pub(all) enum JoinType {
    Miter
    Bevel
    Square
    Round
    } derive(Eq,
    Debug
    )

    Limits

    pub(all) struct Limits {
    max_input_paths : Int
    max_input_vertices : Int
    } derive(Eq,
    Debug
    )

    impl Default for Limits

    OffsetOptions

    pub struct OffsetOptions {
    join : JoinType
    miter_limit : Double
    arc_tolerance : Double
    } derive(Eq,
    Debug
    )

    OffsetOptions::new

    fn OffsetOptions::new(join? : JoinType, miter_limit? : Double, arc_tolerance? : Double) -> OffsetOptions

    Orientation

    pub(all) enum Orientation {
    CounterClockwise
    Clockwise
    Collinear
    } derive(Eq,
    Debug
    )

    PathMode

    pub(all) enum PathMode {
    Open
    Closed
    } derive(Eq,
    Debug
    )

    Point64

    pub(all) struct Point64 {
    x : Int64
    y : Int64
    } derive(Eq,
    Debug
    )

    An immutable integer point. Coordinates are stored without floating-point conversion.

    Point64::new

    fn Point64::new(x : Int64, y : Int64) -> Point64

    Construct a point without restricting the Int64 storage range.

    PointD

    pub(all) struct PointD {
    x : Double
    y : Double
    } derive(Eq,
    Debug
    )

    Raw immutable floating-point storage; geometry entry points validate and quantize.

    PointD::new

    fn PointD::new(x : Double, y : Double) -> PointD

    PointLocation

    pub(all) enum PointLocation {
    Outside
    Inside
    OnBoundary
    } derive(Eq,
    Debug
    )

    PolyNode64

    pub struct PolyNode64 {
    // private fields
    }

    PolyNode64::children

    fn PolyNode64::children(self : PolyNode64) -> Array[PolyNode64]

    PolyNode64::contour

    fn PolyNode64::contour(self : PolyNode64) -> Array[Point64]

    PolyNode64::depth

    fn PolyNode64::depth(self : PolyNode64) -> Int

    PolyNode64::is_hole

    fn PolyNode64::is_hole(self : PolyNode64) -> Bool

    PolyNodeD

    pub struct PolyNodeD {
    // private fields
    }

    PolyNodeD::children

    fn PolyNodeD::children(self : PolyNodeD) -> Array[PolyNodeD]

    PolyNodeD::contour

    fn PolyNodeD::contour(self : PolyNodeD) -> Array[PointD]

    PolyNodeD::depth

    fn PolyNodeD::depth(self : PolyNodeD) -> Int

    PolyNodeD::is_hole

    fn PolyNodeD::is_hole(self : PolyNodeD) -> Bool

    PolyTree64

    pub struct PolyTree64 {
    // private fields
    }

    PolyTree64::roots

    fn PolyTree64::roots(self : PolyTree64) -> Array[PolyNode64]

    A new array of immutable node views; virtual root has no contour.

    PolyTree64::to_paths

    fn PolyTree64::to_paths(self : PolyTree64) -> Array[Array[Point64]]

    Copies every contour and both array levels. Ordering is unspecified.

    PolyTreeD

    pub struct PolyTreeD {
    // private fields
    }

    Immutable topology from the quantized integer region. Introduced in 0.2.0.

    PolyTreeD::roots

    fn PolyTreeD::roots(self : PolyTreeD) -> Array[PolyNodeD]

    Returns a fresh array of immutable views. Root contours have depth one.

    PolyTreeD::to_paths

    fn PolyTreeD::to_paths(self : PolyTreeD) -> Array[Array[PointD]]

    Copies both array levels; explicit parent-child relationships are discarded.

    Precision

    pub struct Precision {
    // private fields
    } derive(Eq,
    Debug
    )

    Immutable decimal digits. Construction validates the supported 0..6 range.

    Precision::digits

    fn Precision::digits(self : Precision) -> Int

    Precision::new

    fn Precision::new(digits : Int) -> Result[Precision, ClipError]

    ResourceKind

    pub(all) enum ResourceKind {
    InputPaths
    InputVertices
    InternalNodes
    ScanWork
    } derive(Eq,
    Debug
    )

    SimplifyOptions

    pub(all) struct SimplifyOptions {
    epsilon : Double
    mode : PathMode
    } derive(Eq,
    Debug
    )

    SimplifyOptions::new

    fn SimplifyOptions::new(epsilon? : Double, mode? : PathMode) -> SimplifyOptions

    SimplifyResult

    pub struct SimplifyResult {
    // private fields
    } derive(
    Debug
    )

    SimplifyResult::collapsed_indices

    fn SimplifyResult::collapsed_indices(self : SimplifyResult) -> Array[Int]

    SimplifyResult::degenerate_indices

    fn SimplifyResult::degenerate_indices(self : SimplifyResult) -> Array[Int]

    SimplifyResult::input_degenerate_indices

    fn SimplifyResult::input_degenerate_indices(self : SimplifyResult) -> Array[Int]

    SimplifyResult::paths

    SimplifyResult::removed_per_path

    fn SimplifyResult::removed_per_path(self : SimplifyResult) -> Array[Int]

    SimplifyResult::removed_vertices

    fn SimplifyResult::removed_vertices(self : SimplifyResult) -> Int

    SimplifyResultD

    pub struct SimplifyResultD {
    // private fields
    } derive(
    Debug
    )

    SimplifyResultD::collapsed_indices

    fn SimplifyResultD::collapsed_indices(self : SimplifyResultD) -> Array[Int]

    SimplifyResultD::degenerate_indices

    fn SimplifyResultD::degenerate_indices(self : SimplifyResultD) -> Array[Int]

    SimplifyResultD::input_degenerate_indices

    fn SimplifyResultD::input_degenerate_indices(self : SimplifyResultD) -> Array[Int]

    SimplifyResultD::paths

    SimplifyResultD::removed_per_path

    fn SimplifyResultD::removed_per_path(self : SimplifyResultD) -> Array[Int]

    SimplifyResultD::removed_vertices

    fn SimplifyResultD::removed_vertices(self : SimplifyResultD) -> Int

    Stage

    pub(all) enum Stage {
    ValidateInputs
    Cross
    Orientation
    Bounds
    Area2
    PointOnSegment
    PointInPolygon
    NormalizePath
    NormalizePaths
    EnginePrepare
    ScanEvents
    ActiveEdges
    ScanBand
    ScanTop
    Intersections
    HorizontalEvents
    OutputTopology
    BooleanOp
    PolyTree
    OffsetParameters
    OffsetGenerate
    OffsetCleanup
    StrokeParameters
    StrokeGenerate
    StrokeCleanup
    DecimalParameters
    DecimalQuantize
    DecimalOutput
    SimplifyParameters
    SimplifyEvaluate
    SimplifyReport
    SimplifyOutput
    MinkowskiParameters
    MinkowskiGenerate
    MinkowskiCleanup
    } derive(Eq,
    Debug
    )

    COORDINATE_LIMIT

    let COORDINATE_LIMIT : Int64

    Inclusive coordinate limit for geometry, not Point64 storage.

    area2

    fn area2(path : Array[Point64], limits? : Limits) -> Result[
    BigInt
    , ClipError]

    Exact twice signed area; closes the last edge implicitly. Zero area is not a validity test for self-intersecting or degenerate contours.

    boolean_op

    fn boolean_op(subject : Array[Array[Point64]], clip : Array[Array[Point64]], operation : ClipType, fill_rule? : FillRule, limits? : Limits) -> Result[Array[Array[Point64]], ClipError]

    Closed paths, independent operand filling; fixed preserve_collinear=false, reverse_solution=false. Difference is Subject minus Clip.

    boolean_op_d

    fn boolean_op_d(subject : Array[Array[PointD]], clip : Array[Array[PointD]], operation : ClipType, precision? : Precision, fill_rule? : FillRule, limits? : Limits) -> Result[Array[Array[PointD]], ClipError]

    boolean_tree

    fn boolean_tree(subject : Array[Array[Point64]], clip : Array[Array[Point64]], operation : ClipType, fill_rule? : FillRule, limits? : Limits) -> Result[PolyTree64, ClipError]

    Closed integer Boolean result with geometric parentage. Empty is Ok(empty).

    boolean_tree_d

    fn boolean_tree_d(subject : Array[Array[PointD]], clip : Array[Array[PointD]], operation : ClipType, precision? : Precision, fill_rule? : FillRule, limits? : Limits) -> Result[PolyTreeD, ClipError]

    Boolean hierarchy of the quantized region, with decoded Double contours. Introduced in 0.2.0; not included in the published 0.1.0 package.

    bounds

    fn bounds(path : Array[Point64], limits? : Limits) -> Result[Bounds64?, ClipError]

    copy_path

    fn copy_path(path : Array[Point64]) -> Array[Point64]

    Copy the point array. Immutable points may be shared safely. Time and additional space are O(number of vertices).

    copy_path_d

    fn copy_path_d(path : Array[PointD]) -> Array[PointD]

    copy_paths

    fn copy_paths(paths : Array[Array[Point64]]) -> Array[Array[Point64]]

    Copy the outer array and each path separately, even for aliased input paths. Time and additional space are O(number of paths + total number of vertices).

    copy_paths_d

    fn copy_paths_d(paths : Array[Array[PointD]]) -> Array[Array[PointD]]

    cross

    fn cross(a : Point64, b : Point64, c : Point64, limits? : Limits) -> Result[Int64, ClipError]

    minkowski_sum

    fn minkowski_sum(pattern : Array[Point64], path : Array[Point64], path_is_closed : Bool, limits? : Limits) -> Result[Array[Array[Point64]], ClipError]

    Fixed single-pattern path sweep. No endpoint pattern fill; preserves output collinearity.

    normalize_path

    fn normalize_path(path : Array[Point64], mode : PathMode, limits? : Limits) -> Result[Array[Point64], ClipError]

    normalize_paths

    fn normalize_paths(paths : Array[Array[Point64]], mode : PathMode, limits? : Limits) -> Result[Array[Array[Point64]], ClipError]

    Validate all original paths and vertices before allocating any output. Empty contours and original contour indices are retained.

    offset_polygons

    fn offset_polygons(paths : Array[Array[Point64]], delta : Double, fill_rule? : FillRule, options? : OffsetOptions, limits? : Limits) -> Result[Array[Array[Point64]], ClipError]

    Closed filled-region offset. Normalize all inputs together before offsetting. NonZero by default; positive delta expands outers and shrinks holes.

    offset_polygons_d

    fn offset_polygons_d(paths : Array[Array[PointD]], delta : Double, precision? : Precision, fill_rule? : FillRule, options? : OffsetOptions, limits? : Limits) -> Result[Array[Array[PointD]], ClipError]

    offset_tree

    fn offset_tree(paths : Array[Array[Point64]], delta : Double, fill_rule? : FillRule, options? : OffsetOptions, limits? : Limits) -> Result[PolyTree64, ClipError]

    Final offset region with geometric parentage. Introduced in 0.2.0.

    offset_tree_d

    fn offset_tree_d(paths : Array[Array[PointD]], delta : Double, precision? : Precision, fill_rule? : FillRule, options? : OffsetOptions, limits? : Limits) -> Result[PolyTreeD, ClipError]

    Offset hierarchy on the decimal grid. Introduced in 0.2.0.

    orientation

    fn orientation(a : Point64, b : Point64, c : Point64, limits? : Limits) -> Result[Orientation, ClipError]

    Mathematical axes: x right, y up; positive cross is counterclockwise.

    point_in_polygon

    fn point_in_polygon(point : Point64, path : Array[Point64], limits? : Limits) -> Result[PointLocation, ClipError]

    Single closed contour, EvenOdd rule. Half-open y intervals count a shared vertex once; horizontal edges participate only in the boundary check.

    point_on_segment

    fn point_on_segment(point : Point64, a : Point64, b : Point64, limits? : Limits) -> Result[Bool, ClipError]

    simplify_paths

    fn simplify_paths(paths : Array[Array[Point64]], options? : SimplifyOptions, limits? : Limits) -> Result[SimplifyResult, ClipError]

    simplify_paths_d

    fn simplify_paths_d(paths : Array[Array[PointD]], precision? : Precision, options? : SimplifyOptions, limits? : Limits) -> Result[SimplifyResultD, ClipError]

    stroke_polyline

    fn stroke_polyline(path : Array[Point64], width : Double, cap? : CapType, options? : OffsetOptions, limits? : Limits) -> Result[Array[Array[Point64]], ClipError]

    Stroke a single open route with full width, two sides at width / 2. Width below 1 and routes with fewer than two effective points produce no area.

    stroke_polyline_d

    fn stroke_polyline_d(path : Array[PointD], width : Double, precision? : Precision, cap? : CapType, options? : OffsetOptions, limits? : Limits) -> Result[Array[Array[PointD]], ClipError]

    stroke_tree

    fn stroke_tree(path : Array[Point64], width : Double, cap? : CapType, options? : OffsetOptions, limits? : Limits) -> Result[PolyTree64, ClipError]

    Final filled hierarchy of a single open route. Introduced in 0.2.0.

    stroke_tree_d

    fn stroke_tree_d(path : Array[PointD], width : Double, precision? : Precision, cap? : CapType, options? : OffsetOptions, limits? : Limits) -> Result[PolyTreeD, ClipError]

    Open-route stroke hierarchy on the decimal grid. Introduced in 0.2.0.

    validate_paths

    fn validate_paths(paths : Array[Array[Point64]], limits? : Limits) -> Result[Unit, ClipError]

    Validate raw inputs without copying or normalizing them.