forked from 1b-agent/opencadstudio-upstream
fix(pdf): per-piece snap bounds, linear image layering, snap op checks
Review of #1484: - The underlay's snap geometry became one hidden wire per segment, but every piece took the whole page's bounding box, so each cursor move visited every piece. Each piece now gets its own box. - Assigning unlayered images to the last layer compared Debug strings of every layered path and text per drawn item (quadratic, formatted anew each time); the owners are keyed once in a map. - The `snap` automation op used (0, 0, 0) for a missing point; it now answers invalid_point. It also left the live snapper's base point set to the query's; the base point is restored. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
fc48087e50
commit
7e90d4d1a1
4 files changed
+23
-15
No files matched your search
@@ -708,13 +708,12 @@ impl OpenCADStudio {
|
||||
"measure" => self.control_measure(&req),
|
||||
// The object snap a cursor over `point` (world) would get;
|
||||
// `from` is the base for perpendicular and tangent.
|
||||
"snap" => {
|
||||
"snap" => 'snap: {
|
||||
let p = &req["point"];
|
||||
let world = glam::DVec3::new(
|
||||
p[0].as_f64().unwrap_or(0.0),
|
||||
p[1].as_f64().unwrap_or(0.0),
|
||||
p[2].as_f64().unwrap_or(0.0),
|
||||
);
|
||||
let (Some(x), Some(y)) = (p[0].as_f64(), p[1].as_f64()) else {
|
||||
break 'snap failure("invalid_point", "point must be [x, y] or [x, y, z]");
|
||||
};
|
||||
let world = glam::DVec3::new(x, y, p[2].as_f64().unwrap_or(0.0));
|
||||
let from = req["from"].as_array().map(|f| {
|
||||
glam::DVec3::new(
|
||||
f.first().and_then(|v| v.as_f64()).unwrap_or(0.0),
|
||||
|
||||
@@ -2908,11 +2908,15 @@ impl OpenCADStudio {
|
||||
self.snapper.osnap_radius_px,
|
||||
);
|
||||
// Perpendicular and tangent measure from `from`, else the running
|
||||
// command's last point.
|
||||
// command's last point; the live cursor's base point is restored.
|
||||
let live_from = self.snapper.from_point;
|
||||
self.snapper.from_point = from.or(self.last_point).map(|p| p.as_vec3());
|
||||
let (go, gr) = self.drafting_grid_basis(i);
|
||||
self.snapper
|
||||
.snap(raw, p, &candidates, view_rot, eye, bounds, go, gr, None)
|
||||
let hit = self
|
||||
.snapper
|
||||
.snap(raw, p, &candidates, view_rot, eye, bounds, go, gr, None);
|
||||
self.snapper.from_point = live_from;
|
||||
hit
|
||||
}
|
||||
|
||||
/// Apply one frame of a UCS-icon grip drag: map the cursor onto the active
|
||||
|
||||
@@ -1308,7 +1308,9 @@ fn tessellate_entity_inner(
|
||||
crate::scene::model::wire_model::SnapHint::Center,
|
||||
)];
|
||||
}
|
||||
set_wire_aabb(&mut wire, aabb);
|
||||
// Each piece gets its own box (not the page's), so candidate
|
||||
// gathering only visits the pieces near the cursor.
|
||||
set_wire_aabb(&mut wire, WireModel::UNBOUNDED_AABB);
|
||||
bases.push(wire);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,11 +200,14 @@ fn images_to_last_layer(
|
||||
return;
|
||||
};
|
||||
let key = |s: &dyn std::fmt::Debug| format!("{s:?}");
|
||||
let owner = |item: String| {
|
||||
layered.iter().position(|(_, pv)| {
|
||||
pv.paths.iter().any(|p| key(p) == item) || pv.texts.iter().any(|t| key(t) == item)
|
||||
})
|
||||
};
|
||||
// Each layered path and text keyed once; the first layer holding it wins.
|
||||
let mut owners: std::collections::HashMap<String, usize> = std::collections::HashMap::new();
|
||||
for (n, (_, pv)) in layered.iter().enumerate() {
|
||||
for item in pv.paths.iter().map(|p| key(p)).chain(pv.texts.iter().map(|t| key(t))) {
|
||||
owners.entry(item).or_insert(n);
|
||||
}
|
||||
}
|
||||
let owner = |item: String| owners.get(&item).copied();
|
||||
let mut last: Option<usize> = None;
|
||||
let mut moves: Vec<(usize, PdfImage)> = Vec::new();
|
||||
for drawn in &full.order {
|
||||
|
||||
Reference in new issue
Block a user