// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
/// Path data abstraction.
///
/// Ported from upstream `zeno/src/path_data.rs` (Apache-2.0 OR MIT).
pub(open) trait PathData {
commands(Self) -> Iter[Command]
copy_to(Self, &PathBuilder) -> Unit
}
///|
/// PathData implementation for point/verb list structures.
///
/// Mirrors upstream `impl PathData for (&[Point], &[Verb])`.
pub impl PathData for (Array[Point], Array[Verb]) with commands(self) {
let (points, verbs) = self
let it = PointsCommands(points, verbs)
Iter::new(fn() { it.next() })
}
///|
pub impl PathData for (Array[Point], Array[Verb]) with copy_to(self, sink) {
let it = PathData::commands(self)
while it.next() is Some(cmd) {
match cmd {
MoveTo(p) => sink.move_to(p)
LineTo(p) => sink.line_to(p)
QuadTo(c, p) => sink.quad_to(c, p)
CurveTo(c1, c2, p) => sink.curve_to(c1, c2, p)
Close => sink.close()
}
}
}
///|
/// PathData implementation for SVG path strings.
pub impl PathData for String with commands(self) {
let it = SvgCommands(self)
Iter::new(fn() { it.next() })
}
///|
pub impl PathData for String with copy_to(self, sink) {
let it = PathData::commands(self)
while it.next() is Some(cmd) {
match cmd {
MoveTo(p) => sink.move_to(p)
LineTo(p) => sink.line_to(p)
QuadTo(c, p) => sink.quad_to(c, p)
CurveTo(c1, c2, p) => sink.curve_to(c1, c2, p)
Close => sink.close()
}
}
}
///|
/// PathData implementation for pre-parsed command buffers.
pub impl PathData for Array[Command] with commands(self) {
let mut i = 0
let n = self.length()
Iter::new(fn() {
if i >= n {
None
} else {
let v = self[i]
i = i + 1
Some(v)
}
})
}
///|
pub impl PathData for Array[Command] with copy_to(self, sink) {
let it = PathData::commands(self)
while it.next() is Some(cmd) {
match cmd {
MoveTo(p) => sink.move_to(p)
LineTo(p) => sink.line_to(p)
QuadTo(c, p) => sink.quad_to(c, p)
CurveTo(c1, c2, p) => sink.curve_to(c1, c2, p)
Close => sink.close()
}
}
}
///|
/// Computes the total length of the path.
///
/// Ported from upstream `zeno/src/path_data.rs::length`.
pub fn length(data : &PathData, transform : Transform?) -> Double {
let mut sum = 0.0
let cmds = data.commands()
if transform is Some(t) {
let it = cmds
let mapped = Iter::new(fn() {
match it.next() {
Some(cmd) => Some(cmd.transform(t))
None => None
}
})
let segs = segments(mapped, false)
let sit = segs
while sit.next() is Some(seg) {
sum = sum + seg.length()
}
} else {
let segs = segments(cmds, false)
let sit = segs
while sit.next() is Some(seg) {
sum = sum + seg.length()
}
}
sum
}