diff --git a/src/scene/mod.rs b/src/scene/mod.rs index d6a269690..0fc58caa2 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -10660,6 +10660,44 @@ mod journal_tests { // Differential oracle: the incrementally-patched entity index must always // equal a from-scratch rebuild after any add / move / erase. #[test] + /// The partition skip turns on a membership test, and the test has to tell + /// a drawn line from a drawn circle. A line carries a tangent geom for + /// snapping and no triangles, so the cheap structural filter admits it — + /// deciding on that alone called every line a contributor and the skip + /// never fired once in a real session. + #[test] + fn the_partition_membership_test_separates_lines_from_curves() { + use acadrust::entities::{Circle, Line}; + use acadrust::types::Vector3; + use crate::scene::pipeline::wire_arena::feeds_analytical_uploads; + + let mut scene = Scene::new(); + let feeds = |scene: &Scene, handle: Handle| { + let entity = scene.document.get_entity(handle).unwrap().clone(); + scene + .tessellate_one(&entity) + .iter() + .any(feeds_analytical_uploads) + }; + + let line = scene.add_entity(EntityType::Line(Line::from_points( + Vector3::new(0.0, 0.0, 0.0), + Vector3::new(10.0, 5.0, 0.0), + ))); + assert!( + !feeds(&scene, line), + "a drawn line must not force the partition walk", + ); + + let mut circle = Circle::new(); + circle.radius = 4.0; + let circle = scene.add_entity(EntityType::Circle(circle)); + assert!( + feeds(&scene, circle), + "a drawn circle feeds the analytical uploads and must force the walk", + ); + } + /// The partition skip reuses uploads that bake a draw depth, and it is only /// sound while existing depths hold still. An incremental add must leave /// them alone and must not bump the generation; a rebuild from scratch may diff --git a/src/scene/pipeline/wire_arena.rs b/src/scene/pipeline/wire_arena.rs index 7dcd29954..322dea9f0 100644 --- a/src/scene/pipeline/wire_arena.rs +++ b/src/scene/pipeline/wire_arena.rs @@ -184,17 +184,23 @@ pub struct PartitionedWires<'a> { /// Whether a change to this wire can alter what `partition_wires` produces for /// the instanced, circle and ellipse uploads. /// -/// Deliberately conservative: a wire that looks analytical here may still fail -/// extraction and end up in `regular`, which costs an unnecessary re-partition -/// and never a stale upload. Shared with `partition_wires` so the test that -/// decides to skip cannot drift from the walk it is skipping. +/// Decided by attempting the extraction, exactly as the walk decides it. The +/// cheap structural test alone is not enough: an ordinary drawn line carries a +/// tangent geom for snapping and no triangles, so it passes that test, fails +/// extraction, and lands in `regular` — a membership test that stopped there +/// would call every line a contributor and never skip anything. +/// +/// `draw_depth` only rides along in the extracted instance, so the value passed +/// here cannot change the answer. pub fn feeds_analytical_uploads(wire: &WireModel) -> bool { - wire.display_visible - && (wire.render_instance.is_some() - || (!wire.tangent_geoms.is_empty() - && wire.fill_tris.is_empty() - && wire.pick_tris.is_empty() - && wire.text_verts.is_empty())) + if !wire.display_visible { + return false; + } + if wire.render_instance.is_some() { + return true; + } + super::circle_gpu::extract_circle_instances(wire, 0.0).is_some() + || super::ellipse_gpu::extract_ellipse_instances(wire, 0.0).is_some() } /// Single-pass classification and extraction of all viewport wire categories.