From d46c00281f85263422b24d0f12c016db8d12aa5c Mon Sep 17 00:00:00 2001 From: Panos <25664832+mechpanos@users.noreply.github.com> Date: Tue, 8 Sep 2026 14:45:16 +0300 Subject: [PATCH] fix: decide partition membership by extraction, not by shape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `skipped=false` on every frame of a real session — the skip added in the previous commit never fired once. The membership test used the same cheap structural filter that `partition_wires` uses before attempting extraction: a tangent geom, no fill, pick or text triangles. That filter is a pre-check, not a decision. An ordinary drawn line carries a tangent geom for snapping and no triangles, so it passed, and every drawn line was counted as feeding the analytical uploads. Nothing was ever skippable. It now decides the way the walk decides, by attempting the extraction. `draw_depth` only rides along in the extracted instance, so the value passed for the test cannot change the answer, and it runs only for the handful of wires a patch changed. Drawing a circle or an ellipse still forces the walk, correctly — those are contributors, and the session that exposed this contained both. What changes is that the lines drawn afterwards no longer do. Tested on real tessellated geometry: a line must not force the walk, a circle must. Co-Authored-By: Claude Opus 5 --- src/scene/mod.rs | 38 ++++++++++++++++++++++++++++++++ src/scene/pipeline/wire_arena.rs | 26 +++++++++++++--------- 2 files changed, 54 insertions(+), 10 deletions(-) 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.