perf(clipboard): derive the paste anchor from bounding boxes

COPYCLIP/CUTCLIP computed the clipboard anchor by tessellating every
selected entity and averaging all wire vertices — O(total geometry),
~250 ms when copying a whole drawing. The anchor only fixes which point
of the selection sits under the cursor at paste, so the mean of each
entity's bounding-box centre is an equally valid, far cheaper origin.
This commit is contained in:
sLuCHa committed 2026-07-08 18:57:11 +02:00
1 parent 4d7588e1e0
commit 56835791af
2 files changed
+25 -25

No files matched your search

+6 -4
View File
@@ -70,8 +70,9 @@ impl OpenCADStudio {
.iter()
.filter_map(|&h| self.tabs[i].scene.document.get_entity(h).cloned())
.collect();
self.clipboard_centroid = super::super::helpers::entities_centroid(
&self.tabs[i].scene.wire_models_for(&handles),
self.clipboard_centroid = super::super::helpers::entities_centroid_by_bbox(
&self.tabs[i].scene.document,
&handles,
);
self.clipboard = entities;
self.clipboard_deps = super::super::ClipboardDeps::capture(
@@ -158,8 +159,9 @@ impl OpenCADStudio {
.iter()
.filter_map(|&h| self.tabs[i].scene.document.get_entity(h).cloned())
.collect();
self.clipboard_centroid = super::super::helpers::entities_centroid(
&self.tabs[i].scene.wire_models_for(&handles),
self.clipboard_centroid = super::super::helpers::entities_centroid_by_bbox(
&self.tabs[i].scene.document,
&handles,
);
let count = entities.len();
self.clipboard = entities;
+19 -21
View File
@@ -1,4 +1,3 @@
use crate::scene::WireModel;
use crate::ui::overlay::GridPlane;
use acadrust::tables::Ucs;
@@ -253,28 +252,27 @@ 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::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.
/// Cheap copy/paste anchor: the mean of each entity's bounding-box centre.
/// Replaces averaging every tessellated wire vertex, which cost O(total
/// geometry) and stalled a whole-drawing copy. The anchor only sets which point
/// of the selection sits under the cursor, so a per-entity bbox centre is an
/// equally valid, far cheaper origin.
pub(super) fn entities_centroid_by_bbox(
doc: &acadrust::CadDocument,
handles: &[acadrust::Handle],
) -> glam::DVec3 {
let mut sum = glam::DVec3::ZERO;
let mut count = 0usize;
for w in wires {
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 !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,
);
for &h in handles {
let Some(e) = doc.get_entity(h) else { continue };
let bb = e.as_entity().bounding_box();
let c = glam::DVec3::new(
(bb.min.x + bb.max.x) * 0.5,
(bb.min.y + bb.max.y) * 0.5,
(bb.min.z + bb.max.z) * 0.5,
);
if c.x.is_finite() && c.y.is_finite() && c.z.is_finite() {
sum += c;
count += 1;
}
}