forked from 1b-agent/opencadstudio-upstream
fix(io): keep zero-radius circles and arcs on open
Problem The open guard rejects circles and arcs with a radius below 1e-10. A normal open fails when a drawing contains one. The user gets the recovery prompt. AutoCAD and nanoCAD open these drawings. The check came fromace99541. Its comment says the tessellator cannot sample a degenerate curve. That was true of truck's parameter_division. Commit8c7ebdc9removed truck. Commit96be8214made the kernel sample circles and arcs by sweep angle with a floored segment count. The reason for the check no longer exists. Change - Remove the near-zero radius check for circles and arcs. - Keep the checks for non-finite values and radii above 1e10. - Update the guard comments. The purge rejects parser desync. It does not reject degenerate geometry. Be generous in what you accept and strict in what you produce. - Add tests: a zero radius passes the guard, an absurd radius does not. - Change the automation purge test sample from radius 0 to radius 1e11. Behavior - Zero-radius circles and arcs load. They render as dots. - Tessellation, hit testing, snapping, and hatch boundaries accept a zero radius. - The DXF writer does not change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
7aff9fa046
commit
8e14b10104
2 files changed
+49
-9
No files matched your search
@@ -1414,7 +1414,9 @@ mod tests {
|
||||
doc.add_entity(acadrust::EntityType::Circle(good)).unwrap();
|
||||
let mut corrupt = acadrust::entities::Circle::new();
|
||||
corrupt.center = acadrust::types::Vector3::new(1.0, 1.0, 0.0);
|
||||
corrupt.radius = 0.0; // io::is_entity_corrupt rejects a zero-radius circle
|
||||
// Past any drawing's extent: io::is_entity_corrupt treats it as parser
|
||||
// desync. (A zero radius is legal geometry and survives the purge.)
|
||||
corrupt.radius = 1.0e11;
|
||||
doc.add_entity(acadrust::EntityType::Circle(corrupt)).unwrap();
|
||||
let bytes = crate::io::save_to_bytes(&doc, "dxf", doc.version)
|
||||
.expect("save a document containing a corrupt entity");
|
||||
|
||||
+46
-8
@@ -2022,6 +2022,12 @@ fn sync_current_styles_on_save(doc: &mut CadDocument) {
|
||||
// `purge_corrupt_entities` scans the document and removes any entity that
|
||||
// fails a cheap sanity check, returning the number dropped so the caller can
|
||||
// surface it to the UI / log.
|
||||
//
|
||||
// The checks target what a desynchronised parser produces, not what a CAD
|
||||
// package might legitimately write. Degenerate-but-legal geometry — a
|
||||
// zero-radius circle, a zero-sweep arc, a zero-length line — is kept, since
|
||||
// the strict open path treats every drop as a corrupt file and sends the
|
||||
// user through recovery for it.
|
||||
|
||||
fn finite_unit_normal(n: &acadrust::types::Vector3) -> bool {
|
||||
let (x, y, z) = (n.x, n.y, n.z);
|
||||
@@ -2079,20 +2085,23 @@ pub(crate) fn is_entity_corrupt(e: &EntityType) -> bool {
|
||||
|| p.vertices.iter().any(|v| !finite_vec3(&v.location))
|
||||
}
|
||||
E::Line(l) => !finite_vec3(&l.start) || !finite_vec3(&l.end),
|
||||
// A zero or near-zero radius is legal, if useless, geometry: AutoCAD
|
||||
// and nanoCAD write and open such circles and arcs, and real
|
||||
// drawings carry them (collapsed dimension arcs, point markers).
|
||||
// Every consumer here copes with it — the kernel tessellates by
|
||||
// sweep angle and floors the segment count, the circle shader draws
|
||||
// the distance to the radius, and the tangent snap solver returns
|
||||
// nothing when the cursor sits inside the radius — so only the
|
||||
// desync signatures are rejected: non-finite values and radii past
|
||||
// any drawing's extent.
|
||||
E::Circle(c) => {
|
||||
!finite_vec3(&c.center)
|
||||
|| !finite_coord(c.radius)
|
||||
// Reject zero- or near-zero circles: they tessellate into a
|
||||
// degenerate curve the tessellator cannot sample.
|
||||
|| c.radius.abs() < 1.0e-10
|
||||
|| c.radius.abs() > 1.0e10
|
||||
!finite_vec3(&c.center) || !finite_coord(c.radius) || c.radius.abs() > 1.0e10
|
||||
}
|
||||
E::Arc(a) => {
|
||||
!finite_vec3(&a.center)
|
||||
|| !finite_coord(a.radius)
|
||||
|| !a.start_angle.is_finite()
|
||||
|| !a.end_angle.is_finite()
|
||||
|| a.radius.abs() < 1.0e-10
|
||||
|| a.radius.abs() > 1.0e10
|
||||
|| !finite_unit_normal(&a.normal)
|
||||
}
|
||||
@@ -2342,7 +2351,7 @@ mod layer_roundtrip_tests {
|
||||
#[cfg(test)]
|
||||
mod corrupt_guard_tests {
|
||||
use super::*;
|
||||
use acadrust::entities::{Arc, EntityType, Spline};
|
||||
use acadrust::entities::{Arc, Circle, EntityType, Spline};
|
||||
use acadrust::types::Vector3;
|
||||
|
||||
// Small but finite arcs are valid records. Kernel tessellation is bounded,
|
||||
@@ -2382,6 +2391,35 @@ mod corrupt_guard_tests {
|
||||
assert!(!is_entity_corrupt(&EntityType::Arc(a)));
|
||||
}
|
||||
|
||||
// Zero-radius circles and arcs are legal DXF that AutoCAD and nanoCAD
|
||||
// open without complaint. A strict open that drops them fails the whole
|
||||
// file, so they must pass the guard; only absurd radii are desync.
|
||||
#[test]
|
||||
fn keeps_zero_radius_circle_and_arc() {
|
||||
let mut c = Circle::new();
|
||||
c.center = Vector3::new(206.2, 150.7, 0.0);
|
||||
c.radius = 0.0;
|
||||
assert!(!is_entity_corrupt(&EntityType::Circle(c)));
|
||||
|
||||
let mut a = Arc::new();
|
||||
a.center = Vector3::new(223.5, 174.5, 0.0);
|
||||
a.radius = 0.0;
|
||||
a.start_angle = 0.0;
|
||||
a.end_angle = 0.0;
|
||||
a.normal = Vector3::new(0.0, 0.0, 1.0);
|
||||
assert!(!is_entity_corrupt(&EntityType::Arc(a)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drops_absurd_radius_circle() {
|
||||
let mut c = Circle::new();
|
||||
c.radius = 1.0e11;
|
||||
assert!(is_entity_corrupt(&EntityType::Circle(c)));
|
||||
let mut c = Circle::new();
|
||||
c.radius = f64::NAN;
|
||||
assert!(is_entity_corrupt(&EntityType::Circle(c)));
|
||||
}
|
||||
|
||||
// Parser desync emits 100_000-control-point splines; building a kernel
|
||||
// NURBS from one and tessellating it OOMs. The control-point cap rejects it.
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user