// 带错误结果的离散入口,供检查和块展开使用。
///|
fn flat_is_finite(flat : Flat) -> Bool {
match flat {
Outline(points~, ..) => {
for p in points {
if !finite_number(p.x) || !finite_number(p.y) {
return false
}
}
true
}
Dot(at~) => finite_number(at.x) && finite_number(at.y)
Label(at~, height~, ..) =>
finite_number(at.x) && finite_number(at.y) && finite_number(height)
Empty => true
}
}
///|
fn arc_cap_exceeded(radius : Double, sweep : Double, tol : Double) -> Bool {
let s = @math.sin(sweep / 16384.0)
// 4096段封顶时的弦高;用 sin² 形式避免极小角度时 1-cos 的相消。
2.0 * radius.abs() * s * s > tol * (1.0 + 1.0e-12)
}
///|
fn ellipse_points_checked(
center : Vec3,
major : Vec3,
ratio : Double,
start_param : Double,
end_param : Double,
tol : Double,
) -> Result[Array[Vec2], String] {
if !positive_tolerance(tol) ||
!finite_number(start_param) ||
!finite_number(end_param) ||
!finite_number(ratio) ||
ratio <= 0.0 ||
ratio > 1.0 {
return Err(
"椭圆需要有限参数、正容差;短长轴比必须大于0且不超过1",
)
}
let c = center.xy()
let m = major.xy()
let n = Vec2::new(-m.y * ratio, m.x * ratio)
if m.length() == 0.0 {
return Err("椭圆长轴的 XY 投影长度为零")
}
let span = ellipse_span(start_param, end_param)
if !finite_number(span) || span > 6.283185307179586 + 1.0e-12 {
return Err("椭圆参数跨度超出一周")
}
let mut segments = (span / 1.5707963267948966).to_int()
if segments.to_double() * 1.5707963267948966 < span {
segments = segments + 1
}
if segments < 1 {
segments = 1
}
let out : Array[Vec2] = []
for i = 0; i < segments; i = i + 1 {
let a = start_param + span * i.to_double() / segments.to_double()
let b = start_param + span * (i + 1).to_double() / segments.to_double()
let mid = 0.5 * a + 0.5 * b
let w = @math.cos(0.5 * (b - a))
let p0 = c.add(m.scale(@math.cos(a))).add(n.scale(@math.sin(a)))
let p1 = c.add(m.scale(@math.cos(mid) / w)).add(n.scale(@math.sin(mid) / w))
let p2 = c.add(m.scale(@math.cos(b))).add(n.scale(@math.sin(b)))
let points = match
spline_points_checked(
2,
[0.0, 0.0, 0.0, 1.0, 1.0, 1.0],
[
Vec3::new(p0.x, p0.y, 0.0),
Vec3::new(p1.x, p1.y, 0.0),
Vec3::new(p2.x, p2.y, 0.0),
],
[1.0, w, 1.0],
tol,
) {
Ok(points) => points
Err(msg) => return Err(msg)
}
for j = (if i == 0 { 0 } else { 1 }); j < points.length(); j = j + 1 {
out.push(points[j])
}
if out.length() > 65536 {
return Err("椭圆离散超过65536点上限")
}
}
if span >= 6.283185307179586 - 1.0e-12 {
let closed : Array[Vec2] = []
for i = 0; i + 1 < out.length(); i = i + 1 {
closed.push(out[i])
}
Ok(closed)
} else {
Ok(out)
}
}
///|
/// 检查和展开使用此入口:达到上限或无法处理时返回错误,不把占位点当成正确曲线。
pub fn Geometry::flatten_checked(
self : Geometry,
tol : Double,
) -> Result[Flat, String] {
if !positive_tolerance(tol) {
return Err("离散容差必须是正的有限数字")
}
match self {
Circle(radius~, ..) =>
if !finite_number(radius) ||
arc_cap_exceeded(radius, 6.283185307179586, tol) {
return Err(
"圆离散需要超过4096段,或半径非有限;未满足指定容差",
)
}
Arc(radius~, start_angle~, end_angle~, ..) => {
if !finite_number(radius) ||
!finite_number(start_angle) ||
!finite_number(end_angle) {
return Err("圆弧参数必须有限")
}
if arc_cap_exceeded(radius, arc_sweep(start_angle, end_angle), tol) {
return Err("圆弧离散超过4096段上限;未满足指定容差")
}
}
Polyline(vertices~, closed~) => {
let count = if closed { vertices.length() } else { vertices.length() - 1 }
for i = 0; i < count; i = i + 1 {
let a = vertices[i]
let b = vertices[(i + 1) % vertices.length()]
if !finite_number(a.bulge) {
return Err("多段线 bulge 必须有限")
}
if a.bulge != 0.0 && a.at.xy().distance(b.at.xy()) > 0.0 {
let arc = arc_from_bulge(a.at.xy(), b.at.xy(), a.bulge)
if !finite_number(arc.radius) ||
arc_cap_exceeded(
arc.radius,
angle_span(arc.start_angle, arc.end_angle),
tol,
) {
return Err(
"多段线圆弧超出数值或4096段上限;未满足指定容差",
)
}
}
}
}
_ => ()
}
let flat = match self {
Spline(degree~, knots~, control_points~, weights~) =>
match spline_points_checked(degree, knots, control_points, weights, tol) {
Ok(points) => Outline(points~, closed=false)
Err(msg) => return Err(msg)
}
Ellipse(center~, major~, ratio~, start_param~, end_param~) =>
match
ellipse_points_checked(
center, major, ratio, start_param, end_param, tol,
) {
Ok(points) =>
Outline(
points~,
closed=ellipse_span(start_param, end_param) >=
6.283185307179586 - 1.0e-12,
)
Err(msg) => return Err(msg)
}
_ => self.flatten(tol)
}
if !flat_is_finite(flat) {
return Err("离散结果含非有限坐标或字高")
}
Ok(flat)
}