XLine/Ray tessellate as +/-1e6 display segments (entities/ray.rs), and both fit_all outlier defenses miss them: the IQR reject passes because a construction line through the drawing has its centroid at its base point, inside the consensus cluster; and the per-point lim filter passes because local_extent_max is computed once at document load and stays at the 1e9 default for drawings created fresh in-app. The far endpoints then poison the bounds and the view fits +/-1e6, shrinking real geometry to a dot. Exclude XLine/Ray wires from the extents up front (AutoCAD likewise ignores infinite lines in ZOOM Extents), falling back to fitting their base points when the drawing holds nothing else. Regression tests verified to fail without the fix (camera distance 3,000,000) and pass with it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
71 lines
2.3 KiB
Rust
71 lines
2.3 KiB
Rust
// Regression for #284: ZOOM Extents must ignore the ±1e6 display segments
|
|
// that XLine/Ray tessellate into, instead of fitting the camera to them.
|
|
use acadrust::entities::{Line, XLine};
|
|
use acadrust::types::Vector3;
|
|
use acadrust::EntityType;
|
|
use OpenCADStudio::scene::Scene;
|
|
|
|
fn add_rect(scene: &mut Scene) {
|
|
let corners = [
|
|
(0.0, 0.0, 10.0, 0.0),
|
|
(10.0, 0.0, 10.0, 10.0),
|
|
(10.0, 10.0, 0.0, 10.0),
|
|
(0.0, 10.0, 0.0, 0.0),
|
|
];
|
|
for (x1, y1, x2, y2) in corners {
|
|
let mut line = Line::new();
|
|
line.start = Vector3::new(x1, y1, 0.0);
|
|
line.end = Vector3::new(x2, y2, 0.0);
|
|
scene.add_entity(EntityType::Line(line));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn fit_all_ignores_xline_display_extent() {
|
|
let mut scene = Scene::new();
|
|
add_rect(&mut scene);
|
|
|
|
// Construction line through the rectangle centre at 45° — its centroid
|
|
// sits inside the drawing cluster, and in a fresh document
|
|
// `local_extent_max` is still the 1e9 default, so before the fix both
|
|
// outlier rejects passed its ±1e6 display endpoints into the bounds.
|
|
let xl = XLine::new(Vector3::new(5.0, 5.0, 0.0), Vector3::new(1.0, 1.0, 0.0));
|
|
scene.add_entity(EntityType::XLine(xl));
|
|
|
|
scene.fit_all();
|
|
|
|
let cam = scene.camera.borrow();
|
|
assert!(
|
|
cam.distance.is_finite() && cam.distance < 1000.0,
|
|
"camera must fit the 10x10 rectangle, not the xline's 1e6 display \
|
|
segment (distance = {})",
|
|
cam.distance
|
|
);
|
|
assert!(
|
|
(cam.target.x - 5.0).abs() < 5.0 && (cam.target.y - 5.0).abs() < 5.0,
|
|
"camera target must stay on the rectangle (target = {:?})",
|
|
cam.target
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn fit_all_with_only_xline_fits_base_point() {
|
|
let mut scene = Scene::new();
|
|
let xl = XLine::new(Vector3::new(100.0, 200.0, 0.0), Vector3::new(0.0, 1.0, 0.0));
|
|
scene.add_entity(EntityType::XLine(xl));
|
|
|
|
scene.fit_all();
|
|
|
|
let cam = scene.camera.borrow();
|
|
assert!(
|
|
cam.distance.is_finite() && cam.distance < 1000.0,
|
|
"xline-only drawing must fit near the base point, not ±1e6 \
|
|
(distance = {})",
|
|
cam.distance
|
|
);
|
|
assert!(
|
|
(cam.target.x - 100.0).abs() < 5.0 && (cam.target.y - 200.0).abs() < 5.0,
|
|
"camera target must be the xline base point (target = {:?})",
|
|
cam.target
|
|
);
|
|
}
|