diff --git a/src/app/cmd_result.rs b/src/app/cmd_result.rs index 9250e7def..adaa386b8 100644 --- a/src/app/cmd_result.rs +++ b/src/app/cmd_result.rs @@ -616,8 +616,7 @@ impl OpenCADStudio { tgt_wo[1] - src_wo[1], tgt_wo[2] - src_wo[2], ); - let delta = - base_pt - self.clipboard_centroid.as_dvec3() + wo_corr; + let delta = base_pt - self.clipboard_centroid + wo_corr; let translate = crate::command::EntityTransform::Translate(delta); self.push_undo_snapshot(i, "PASTECLIP"); let count = self.clipboard.len(); diff --git a/src/app/commands.rs b/src/app/commands.rs index 02ece38f3..032a5e464 100644 --- a/src/app/commands.rs +++ b/src/app/commands.rs @@ -603,13 +603,15 @@ impl OpenCADStudio { let src_wo = self.clipboard_world_offset; let tgt_wo = [0.0_f64; 3]; let centroid = self.clipboard_centroid - + glam::Vec3::new( - (src_wo[0] - tgt_wo[0]) as f32, - (src_wo[1] - tgt_wo[1]) as f32, - (src_wo[2] - tgt_wo[2]) as f32, + + glam::DVec3::new( + src_wo[0] - tgt_wo[0], + src_wo[1] - tgt_wo[1], + src_wo[2] - tgt_wo[2], ); use crate::modules::draw::clipboard::paste::PasteCommand; - let cmd = PasteCommand::new(wires, centroid); + // The ghost anchor is a display-only offset; the precise + // paste delta is computed in f64 at commit time. + let cmd = PasteCommand::new(wires, centroid.as_vec3()); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } @@ -674,7 +676,7 @@ impl OpenCADStudio { self.tabs[i].dirty = true; let wires = self.tabs[i].scene.wires_for_entities(&self.clipboard); use crate::modules::insert::insert_block::InsertBlockCommand; - let cmd = InsertBlockCommand::new_for_block(name, wires, base); + let cmd = InsertBlockCommand::new_for_block(name, wires, base.as_vec3()); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } diff --git a/src/app/helpers.rs b/src/app/helpers.rs index a4e0a2e89..46ca4ad22 100644 --- a/src/app/helpers.rs +++ b/src/app/helpers.rs @@ -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 } } diff --git a/src/app/mod.rs b/src/app/mod.rs index 2269a1837..e6a3f7680 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -371,7 +371,7 @@ pub(super) struct OpenCADStudio { /// In-memory clipboard: cloned entities waiting to be pasted. clipboard: Vec, /// Centroid of the clipboard entities (world XY plane). - clipboard_centroid: glam::Vec3, + clipboard_centroid: glam::DVec3, /// The source drawing's `world_offset` at copy time. The centroid above is /// measured in that drawing's offset-relative frame; pasting into a drawing /// with a different offset (e.g. a large-coordinate file → a fresh one) @@ -1808,7 +1808,7 @@ impl OpenCADStudio { update_notice_version: None, update_notice_body: None, clipboard: Vec::new(), - clipboard_centroid: glam::Vec3::ZERO, + clipboard_centroid: glam::DVec3::ZERO, clipboard_world_offset: [0.0; 3], clipboard_deps: ClipboardDeps::default(), shift_down: false, diff --git a/src/scene/mod.rs b/src/scene/mod.rs index be582d3f9..6391d4f75 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -4204,7 +4204,7 @@ impl Scene { &mut self, entities: Vec, name: &str, - base: glam::Vec3, + base: glam::DVec3, ) -> Result<(), String> { let name = name.trim(); if name.is_empty() { @@ -4248,7 +4248,7 @@ impl Scene { .add_entity(EntityType::BlockEnd(block_end)) .map_err(|e| e.to_string())?; - let local = EntityTransform::Translate((-base).as_dvec3()); + let local = EntityTransform::Translate(-base); for mut entity in entities { view::dispatch::apply_transform(&mut entity, &local); entity = crate::modules::draw::modify::explode::normalize_entity_for_block(entity);