fix(paste): UTM-precise paste/block base via f64 centroid

entities_centroid summed the f32 points (double-single HIGH half) alone, so
at UTM scale (~5.7e6) it both quantized each term ~0.5 m and lost low bits
across the running total — drifting the paste base and PASTEBLOCK's block
base metres off, which placed the wrapped block at the wrong spot/scale.

It now reconstructs each vertex's absolute f64 from points + points_low and
accumulates in f64. clipboard_centroid is DVec3; the PASTECLIP commit delta
is fully f64; define_block_from_owned_entities takes an f64 base so the
block-local fold (Translate(-base)) stays precise. The interactive ghost
anchors (PasteCommand, InsertBlockCommand) still take the f32 cast — display
only; the committed geometry is f64.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-06-24 10:42:58 +03:00
commit 60d4712179
5 changed files with 31 additions and 21 deletions

View file

@ -254,25 +254,34 @@ pub(super) fn polar_constrain_near(
// ── Clipboard / selection helpers ──────────────────────────────────────────
/// Compute the centroid of a set of wire models (average of all points).
pub(super) fn entities_centroid(wires: &[WireModel]) -> glam::Vec3 {
let mut sum = glam::Vec3::ZERO;
pub(super) fn entities_centroid(wires: &[WireModel]) -> glam::DVec3 {
// Reconstruct each vertex's absolute f64 from the double-single high/low
// pair and accumulate in f64: summing the f32 `points` alone at UTM scale
// (~5.7e6) both quantizes each term ~0.5 m and loses low bits across the
// running total, drifting the paste base / block base metres off.
let mut sum = glam::DVec3::ZERO;
let mut count = 0usize;
for w in wires {
for p in &w.points {
let v = glam::Vec3::from(*p);
for (i, p) in w.points.iter().enumerate() {
// Wire models carry NaN points as separators between disjoint
// segments; summing them poisons the whole centroid into NaN,
// which then makes every paste base point NaN. (#129)
if v.is_finite() {
sum += v;
count += 1;
if !p[0].is_finite() || !p[1].is_finite() || !p[2].is_finite() {
continue;
}
let l = w.points_low.get(i).copied().unwrap_or([0.0; 3]);
sum += glam::DVec3::new(
p[0] as f64 + l[0] as f64,
p[1] as f64 + l[1] as f64,
p[2] as f64 + l[2] as f64,
);
count += 1;
}
}
if count > 0 {
sum / count as f32
sum / count as f64
} else {
glam::Vec3::ZERO
glam::DVec3::ZERO
}
}