refactor: drop the discarded offset from world_offset_from_centers

The function computed a median-centroid offset that is now always
discarded (geometry is absolute + double-single). Rename it to
cluster_extent_from_centers, return only the cluster span used for camera
fit / cull, and drop the dead centroid computation. Rename the companion
WORLD_OFFSET_SANE_EXTENT const to CLUSTER_SANE_EXTENT.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-06-24 20:22:54 +03:00
commit 62c7e20f42

View file

@ -183,7 +183,7 @@ pub fn build_derived_caches(doc: &CadDocument) -> DerivedCaches {
centers.push(c); centers.push(c);
} }
} }
let (_world_offset, local_extent_max) = world_offset_from_centers(centers, &doc.header); let local_extent_max = cluster_extent_from_centers(centers, &doc.header);
// Default bg adaptation target at load: the model background (paper // Default bg adaptation target at load: the model background (paper
// bg is only relevant after the user enters a paper layout, and // bg is only relevant after the user enters a paper layout, and
@ -273,7 +273,7 @@ pub fn build_derived_caches(doc: &CadDocument) -> DerivedCaches {
/// Mirrors `cache::block_cache::SANE_EXTENT` — wire coords past this magnitude /// Mirrors `cache::block_cache::SANE_EXTENT` — wire coords past this magnitude
/// are treated as corruption rather than precision-relevant geometry. /// are treated as corruption rather than precision-relevant geometry.
const WORLD_OFFSET_SANE_EXTENT: f64 = 1.0e8; const CLUSTER_SANE_EXTENT: f64 = 1.0e8;
/// MSPACE-membership prep shared by the world-offset centroid scan. /// MSPACE-membership prep shared by the world-offset centroid scan.
/// ///
@ -378,7 +378,7 @@ fn offset_centroid(
if !cx.is_finite() || !cy.is_finite() || !cz.is_finite() { if !cx.is_finite() || !cy.is_finite() || !cz.is_finite() {
return None; return None;
} }
if cx.abs() > WORLD_OFFSET_SANE_EXTENT || cy.abs() > WORLD_OFFSET_SANE_EXTENT { if cx.abs() > CLUSTER_SANE_EXTENT || cy.abs() > CLUSTER_SANE_EXTENT {
return None; return None;
} }
Some([cx, cy, cz]) Some([cx, cy, cz])
@ -391,17 +391,16 @@ fn offset_centroid(
/// `$EXTMIN/$EXTMAX` only as a fallback when the entity scan found nothing. /// `$EXTMIN/$EXTMAX` only as a fallback when the entity scan found nothing.
/// `centers` is gathered by the caller's single entity walk (see /// `centers` is gathered by the caller's single entity walk (see
/// [`build_derived_caches`]) so no separate AABB pass is needed. /// [`build_derived_caches`]) so no separate AABB pass is needed.
fn world_offset_from_centers( fn cluster_extent_from_centers(
centers: Vec<[f64; 3]>, centers: Vec<[f64; 3]>,
header: &acadrust::document::HeaderVariables, header: &acadrust::document::HeaderVariables,
) -> ([f64; 3], f32) { ) -> f32 {
const SANE_EXTENT: f64 = WORLD_OFFSET_SANE_EXTENT; const SANE_EXTENT: f64 = CLUSTER_SANE_EXTENT;
let entity_ok = !centers.is_empty(); let entity_ok = !centers.is_empty();
// Median of per-entity centroids → robust drawing center. // 95th-percentile distance from the median × 2 gives the half-span of the
// For local_extent_max: 95th-percentile distance from the median × 2 // dense cluster while leaving room for legitimate outliers (sparse leaders,
// gives the half-span of the dense cluster while leaving room for // dimensions, scattered annotations).
// legitimate outliers (sparse leaders, dimensions, scattered annotations).
let median = |v: &mut Vec<f64>| -> f64 { let median = |v: &mut Vec<f64>| -> f64 {
v.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)); v.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
v[v.len() / 2] v[v.len() / 2]
@ -411,19 +410,17 @@ fn world_offset_from_centers(
let i = ((v.len() as f64 - 1.0) * frac).round() as usize; let i = ((v.len() as f64 - 1.0) * frac).round() as usize;
v[i] v[i]
}; };
let (ecx, ecy, ecz, espan_max) = if entity_ok { let espan_max = if entity_ok {
let mut xs: Vec<f64> = centers.iter().map(|c| c[0]).collect(); let mut xs: Vec<f64> = centers.iter().map(|c| c[0]).collect();
let mut ys: Vec<f64> = centers.iter().map(|c| c[1]).collect(); let mut ys: Vec<f64> = centers.iter().map(|c| c[1]).collect();
let mut zs: Vec<f64> = centers.iter().map(|c| c[2]).collect();
let mx = median(&mut xs); let mx = median(&mut xs);
let my = median(&mut ys); let my = median(&mut ys);
let mz = median(&mut zs);
let mut dx: Vec<f64> = centers.iter().map(|c| (c[0] - mx).abs()).collect(); let mut dx: Vec<f64> = centers.iter().map(|c| (c[0] - mx).abs()).collect();
let mut dy: Vec<f64> = centers.iter().map(|c| (c[1] - my).abs()).collect(); let mut dy: Vec<f64> = centers.iter().map(|c| (c[1] - my).abs()).collect();
let p95 = percentile(&mut dx, 0.95).max(percentile(&mut dy, 0.95)); let p95 = percentile(&mut dx, 0.95).max(percentile(&mut dy, 0.95));
(mx, my, mz, (p95 * 2.0).max(1.0) as f32) (p95 * 2.0).max(1.0) as f32
} else { } else {
(0.0, 0.0, 0.0, 0.0) 0.0
}; };
// ── Header extents (fallback only) ─────────────────────────────────── // ── Header extents (fallback only) ───────────────────────────────────
@ -436,25 +433,18 @@ fn world_offset_from_centers(
&& hmin.y.abs() < SANE_EXTENT && hmin.y.abs() < SANE_EXTENT
&& hmax.y.abs() < SANE_EXTENT; && hmax.y.abs() < SANE_EXTENT;
// Entity-derived offset is preferred whenever it's available — the // Geometry reaches the GPU as absolute coordinates (the double-single
// median-of-centroids ignores Ray/orphan/duplicate-block-defn outliers // relative-to-eye path keeps it precise at UTM scale), so only the
// that the header EXTMIN/EXTMAX (a min/max midpoint) bakes in. Header // cluster span — for camera fit and cull — is derived from the content.
// is the fallback only when the entity scan found nothing.
// world_offset is being retired: geometry now reaches the GPU as absolute
// coordinates and the double-single relative-to-eye path keeps it precise
// even at UTM scale, so there's no longer a coarse origin to subtract.
// Always return a zero offset; only `local_extent_max` (camera/cull span)
// is still derived from the content.
let _ = (ecx, ecy, ecz);
if entity_ok { if entity_ok {
([0.0; 3], espan_max) espan_max
} else if header_ok { } else if header_ok {
let hw = ((hmax.x - hmin.x) * 0.5) as f32; let hw = ((hmax.x - hmin.x) * 0.5) as f32;
let hh = ((hmax.y - hmin.y) * 0.5) as f32; let hh = ((hmax.y - hmin.y) * 0.5) as f32;
let hz = ((hmax.z - hmin.z) * 0.5).max(1.0) as f32; let hz = ((hmax.z - hmin.z) * 0.5).max(1.0) as f32;
([0.0; 3], hw.max(hh).max(hz) * 10.0) hw.max(hh).max(hz) * 10.0
} else { } else {
([0.0; 3], 1e9_f32) 1e9_f32
} }
} }