tiny-skia-path (MoonBit)
========================

本仓是 tiny-skia 的 **path 子集**在 MoonBit 下的移植。

上游
----
项目： tiny-skia
地址： https://github.com/RazrFalcon/tiny-skia
版权： Copyright (c) 2018 Yevhenii Reizner
许可： BSD-3-Clause（与 LICENSE 同文）

许可义务 → 本仓的落实
---------------------
BSD-3-Clause 要求再分发方保留版权声明、条件与免责声明，并在衍生作品中说明改动。
本仓据此承担三条：

1. LICENSE 与 NOTICE **随分发面一起走**（判据：`bash scripts/license-check.sh`，
   含「归档里必须有这两份」那一段）。
2. 本文的「改动声明」一节即条件②要求的**改动说明**。
3. 不使用上游项目名或作者名为本仓背书。

改动声明
--------
本仓**未**整体搬运上游代码，只逐步移植 path 相关实现。与原作的关系：

- 语言：Rust → MoonBit。类型与内存模型照抄不了（无生命周期、无零成本借用），
  所以数值语义按上游对齐、结构按 MoonBit 的表达能力重写。
- 范围：仅 path（`Path` / `PathBuilder` / `PathStroker` / `Path::dash` /
  `path_geometry` 及其顺带提供的几何基座），渲染管线、渐变、pattern、png 编解码
  **不在本仓范围内**。范围清单与切片顺序见 `docs/05-总览/P1-移植与对拍.md`（§2 / §3）。
- 浮点：上游混用 f32 与定长定点（fdot6/fdot16）。本仓凡 `f32` 语义面一律显式落到
  `Float` 上，不做「看起来等价」的 Double 替代。
- 移植来源：部分实现先在同组织的 `tiny-skia-moon`（同上游同版本的 MoonBit 移植，BSD-3-Clause）
  中落地，再剪枝搬迁至本仓。搬入时统一按本仓约定改写：包路径、门面分层、
  以及把该仓使用的 `Double` 全部改回 `Float`（上游是 `f32`）。
- 不移植项：上游的 `f32x2_t.rs` / `f32x4_t.rs`（`simd` feature 才用的私有垫片），
  以及 `bytemuck` 依赖（MoonBit 侧用 `reinterpret_*` 等价表达）。

搬了哪些文件（P1–P14，文件粒度）
--------------------------------
**两个检出范围**，都在这里点名：

1. `path/src`（13 个文件 7092 行，crate `tiny-skia-path` 0.12.0）；
2. **顶层 crate 的 `src/path64/`**（`mod.rs` / `cubic64.rs` / `quad64.rs` /
   `line_cubic_intersections.rs` / `point64.rs`，5 个文件 850 行）——
   `chop_mono_cubic_at_x/y` 的 f64 求根来自这里，落点是 `lib/path/path64_roots.mbt`
   的 `f64_*` 一族。这一族按上游的算式宽度落在 `f64` 上
   （`docs/05-总览/P1-移植与对拍.md` §7 `Q3`）。

两处范围的逐文件行数由 `bash scripts/upstream-ledger.sh --verify` 现算（生成表在
`docs/05-总览/P1-移植与对拍.md` §2 的账本表），**本节不抄行数**：抄一遍就会漂。
判据是 `bash scripts/license-check.sh` ④ 段——NOTICE 点名的每个上游目录，账本表里
必须真的有对应行。

数值助手并回 `lib/path/`：`scalar.mbt` ← `scalar.rs`；`floating_point.mbt` ←
`floating_point.rs`（有限性、`SaturateCast` / `SaturateRound`、`NonZeroPositiveF32`）；
`math.mbt` ← `tiny-skia/src/math.rs` 的 `left_shift` / `bound`。

`lib/path/`：`point.mbt` ← `lib.rs` 的 `Point`；`size.mbt` ← `size.rs`；
`rect.mbt` ← `rect.rs`（`IntRect` / `Rect` / `NonZeroRect`）；`transform.mbt` ← `transform.rs`；
`path.mbt` ← `path.rs`（`Path` / `PathVerb` / `PathSegment` / `PathSegmentsIter`）；
`path_builder.mbt` ← `path_builder.rs`（`from_circle` / `from_oval` 两个自由函数形入口也在）；
`path_geometry.mbt` ← `path_geometry.rs`（切分/极值 + 求值/求根 + `Conic`）；`path64.mbt` ←
顶层 `src/path64/*`；`stroker.mbt` ← `stroker.rs`（`Stroke` / `LineCap` / `LineJoin` /
`PathStroker` + 全部算法）；`dash.mbt` ← `dash.rs`（`StrokeDash` /
`ContourMeasure` / `Path::dash`）。文件边界与上游 module 一一对应，拆法见
`docs/05-总览/P0-现状与结构.md` 的「§3.1 文件归属」。
上游 `pub(crate)` 助手在本仓写成 `priv`（同包内可见）；只有上游 `lib.rs` 真正
`pub use` 的类型与方法才是对外面，**不替上游扩大契约面**。

剪掉没搬的：`tiny-skia/src/geom.rs` 的 `ScreenIntRect`（属光栅侧）、
`scan/hairline` 相关测试、`color.rs` / `fixed_point.rs` 的通道与定点算术（`lib/path` 零调用点）、
`math.rs` 的 `approx_powf`（唯一调用方是渐变，本仓没有渐变）。

形态改动（逐条）
----------------
1. **门面分层**：上游 `tiny-skia-path/src/lib.rs` 的 re-export 表 → 本仓 `lib/rexport.mbt`
   的 `pub using`（类型 + 18 项）+ `lib/facade.mbt` 的自由函数转发（12 项）。
   MoonBit 的 `pub using` 不带方法、且不能给外来类型补 `impl` 方法，所以方法形 API
   的出口是「消费者直接 `import lib/path`」。
2. **`Stroke` 从「`pub` 字段 + `&mut`」→「不可变 + `with_*` builder + 访问器」**。
   上游是五个可变公开字段；MoonBit 没有借用，改成 builder，并补了同门仓缺的读取器
   （`#valtype` 字段不跨包，访问器是唯一出口）。
3. **`StrokeDash::new` 保持 `Option`**（同门仓曾改成 `Result`）。上游本来就是 `Option`。
4. **`Point::length` 少一层 f64 兜底**：上游在 f32 溢出时退 f64 重算，本仓没有更宽的
   浮点型可用（`AGENTS.md §二.4`），所以坐标绝对值 > ~1.8e19 时直接给 `inf`。
   这是**已知且有意接受**的差异，`lib/path/geom_test.mbt` 有一条断言把它锁成文档。
5. **`scalar_cube_root` 比上游多做一步 `abs`**：上游 `x.powf(1/3)` 在 `x < 0` 时给 `NaN`，
   而卡丹式的调用点入参恒非负——那一支永远碰不到负数。本仓取 `abs` 再补符号，
   让「立方根」这个函数在自己的名字上成立。`lib/path/scalar_f32.mbt` 的注释写明了取舍。
6. **`find_unit_quad_roots_f32` 的判别式在 f64 上算**：上游原文这样写并带注释
   （`use doubles so we don't overflow temporarily trying to compute R`）。
   这是本仓唯一一处「算式必须在更宽的通道里算」，登记在 `scripts/float-gate.sh` 的例外表。

真值出处（三层）
----------------
1. **上游源码里的字面量**：`lib/path/stroker_upstream_test.mbt`（上游
   `path/src/stroker.rs::tests` 6 条）与 `lib/path/dash_upstream_test.mbt`（`path/src/dash.rs::tests`
   2 条）是**逐条抄上游的 `assert`**，不含本仓任何输出。
2. **上游现跑**：`tests/baselines/upstream_golden.mbt` 是**生成物**，每一行都是上游
   `tiny-skia-path` 0.12.0 的 `cargo run` 输出，由 `scripts/upstream-golden.sh --write`
   维护、`--verify` 校验（门禁 `upstream-golden` 段）。本仓的任何数字都不在这个文件里。
   出处说明见同目录的 `upstream_golden.provenance`。
3. **上游的实现（逐行推导）**：`geom_test.mbt` / `path_test.mbt` / `path_geometry_test.mbt`
   里的几何与拓扑断言，真值来自上游算法本身与 IEEE-754 定义。

**没有第四层**：任何「跑一遍本仓实现、把输出抄进测试」的形态都是自证，`AGENTS.md §五.1` 明令禁止。

当前状态
--------
P1–P14 已落地：`lib/path` 一个实现包齐了（数值助手已并回），语义面一律 `Float`
（`Double` 逐文件计数为 0，例外七处各写了理由，见 `scripts/float-gate.sh`）。
**具体数字一律现算，本节不抄**：测试条数看 `bash scripts/test-scale.sh`，
门禁看 `bash scripts/gates.sh`，上游读数看 `bash scripts/upstream-golden.sh --verify`。

真值面不再只有「上游写下来的字面量」：另有一批上游现跑读数（描边 / 虚线 / 包围盒 /
构造器 / `path_geometry` 的公开入口 / 曲线上的点 / `Transform` 的 `invert` 与 `concat`），
全部逐字相等，没有已知偏差。

本仓包含上游与 `tiny-skia-moon` 的代码衍生副本——上面两节逐文件列了搬了什么、改了什么。
许可与归属见 `LICENSE` / 本文件。
