forked from 1b-agent/opencadstudio-upstream
STRETCH: arcs keep their height; splines stretch; real preview
An arc moved only when its centre was in the window, so stretching a rounded end left the arc where it was or blew it up. Its ends now decide: both in move it, one in moves that end while the arc keeps its height (sagitta) over the new chord. Polyline arc segments keep their height the same way instead of their bulge, spline control and fit points in the window move, and 3D polylines and points stretch too. Old-style polylines were tested in X/Z. The preview moved the tessellated points that happened to be in the window, which bent arcs out of shape; it now draws the stretched entities through one shared function the commit also uses, via a new preview_entities hook, and hides the originals while it shows. Closes #1851 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
519d6605f9
commit
907fa8d760
6 files changed
+372
-288
No files matched your search
@@ -293,10 +293,6 @@ impl OpenCADStudio {
|
||||
})
|
||||
};
|
||||
|
||||
let dx = delta.x as f64;
|
||||
let dy = delta.y as f64; // drawing plane is world XY
|
||||
let dz = delta.z as f64;
|
||||
|
||||
// Dimensions whose points moved — their baked *D block is
|
||||
// stale afterwards and must be dropped (see #398 / #372).
|
||||
let mut stretched_dims: Vec<codec::Handle> = Vec::new();
|
||||
@@ -343,198 +339,10 @@ impl OpenCADStudio {
|
||||
let Some(entity) = self.tabs[i].scene.document.get_entity_mut(*handle) else {
|
||||
continue;
|
||||
};
|
||||
let mut stretched = false;
|
||||
match entity {
|
||||
codec::EntityType::Line(l) => {
|
||||
let s_in = in_win(l.start.x, l.start.y);
|
||||
let e_in = in_win(l.end.x, l.end.y);
|
||||
if s_in {
|
||||
l.start.x += dx;
|
||||
l.start.y += dy;
|
||||
l.start.z += dz;
|
||||
stretched = true;
|
||||
}
|
||||
if e_in {
|
||||
l.end.x += dx;
|
||||
l.end.y += dy;
|
||||
l.end.z += dz;
|
||||
stretched = true;
|
||||
}
|
||||
}
|
||||
codec::EntityType::LwPolyline(p) => {
|
||||
let Some(mut world) = crate::entities::curve::lwpolyline_world_xy(p) else {
|
||||
continue;
|
||||
};
|
||||
for v in &mut world.vertices {
|
||||
if in_win(v.location.x, v.location.y) {
|
||||
v.location.x += dx;
|
||||
v.location.y += dy;
|
||||
stretched = true;
|
||||
}
|
||||
}
|
||||
if stretched {
|
||||
*p = world;
|
||||
}
|
||||
}
|
||||
codec::EntityType::Polyline2D(p) => {
|
||||
for v in &mut p.vertices {
|
||||
if in_win(v.location.x, v.location.y) {
|
||||
v.location.x += dx;
|
||||
v.location.y += dy;
|
||||
stretched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
codec::EntityType::Polyline(p) => {
|
||||
for v in &mut p.vertices {
|
||||
if in_win(v.location.x, v.location.z) {
|
||||
v.location.x += dx;
|
||||
v.location.z += dy;
|
||||
stretched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
codec::EntityType::Arc(a) => {
|
||||
if in_win(a.center.x, a.center.y) {
|
||||
a.center.x += dx;
|
||||
a.center.y += dy;
|
||||
a.center.z += dz;
|
||||
stretched = true;
|
||||
}
|
||||
}
|
||||
codec::EntityType::Circle(c) => {
|
||||
if in_win(c.center.x, c.center.y) {
|
||||
c.center.x += dx;
|
||||
c.center.y += dy;
|
||||
c.center.z += dz;
|
||||
stretched = true;
|
||||
}
|
||||
}
|
||||
codec::EntityType::Ellipse(e) => {
|
||||
if in_win(e.center.x, e.center.y) {
|
||||
e.center.x += dx;
|
||||
e.center.y += dy;
|
||||
e.center.z += dz;
|
||||
stretched = true;
|
||||
}
|
||||
}
|
||||
codec::EntityType::Insert(ins) => {
|
||||
if in_win(ins.insert_point.x, ins.insert_point.y) {
|
||||
ins.insert_point.x += dx;
|
||||
ins.insert_point.y += dy;
|
||||
ins.insert_point.z += dz;
|
||||
stretched = true;
|
||||
}
|
||||
}
|
||||
codec::EntityType::Text(t) => {
|
||||
if in_win(t.insertion_point.x, t.insertion_point.y) {
|
||||
t.insertion_point.x += dx;
|
||||
t.insertion_point.y += dy;
|
||||
t.insertion_point.z += dz;
|
||||
stretched = true;
|
||||
}
|
||||
}
|
||||
codec::EntityType::MText(t) => {
|
||||
if in_win(t.insertion_point.x, t.insertion_point.y) {
|
||||
t.insertion_point.x += dx;
|
||||
t.insertion_point.y += dy;
|
||||
t.insertion_point.z += dz;
|
||||
stretched = true;
|
||||
}
|
||||
}
|
||||
codec::EntityType::Viewport(vp) => {
|
||||
stretched = windows.iter().any(|(win_min, win_max)| {
|
||||
crate::entities::viewport::stretch(vp, *win_min, *win_max, delta)
|
||||
});
|
||||
}
|
||||
codec::EntityType::Dimension(dim) => {
|
||||
use codec::entities::Dimension;
|
||||
// Move every definition point that falls inside the
|
||||
// window — the same points the grips expose — then
|
||||
// refresh the stored measurement so the value tracks
|
||||
// the stretched geometry.
|
||||
let mv = |p: &mut codec::types::Vector3| {
|
||||
if in_win(p.x, p.y) {
|
||||
p.x += dx;
|
||||
p.y += dy;
|
||||
p.z += dz;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
match &mut **dim {
|
||||
Dimension::Linear(d) => {
|
||||
stretched |= mv(&mut d.first_point);
|
||||
stretched |= mv(&mut d.second_point);
|
||||
stretched |= mv(&mut d.definition_point);
|
||||
}
|
||||
Dimension::Aligned(d) => {
|
||||
stretched |= mv(&mut d.first_point);
|
||||
stretched |= mv(&mut d.second_point);
|
||||
stretched |= mv(&mut d.definition_point);
|
||||
}
|
||||
Dimension::Radius(d) => {
|
||||
stretched |= mv(&mut d.angle_vertex);
|
||||
stretched |= mv(&mut d.definition_point);
|
||||
}
|
||||
Dimension::Diameter(d) => {
|
||||
stretched |= mv(&mut d.angle_vertex);
|
||||
stretched |= mv(&mut d.definition_point);
|
||||
}
|
||||
Dimension::Angular2Ln(d) => {
|
||||
stretched |= mv(&mut d.angle_vertex);
|
||||
stretched |= mv(&mut d.first_point);
|
||||
stretched |= mv(&mut d.second_point);
|
||||
stretched |= mv(&mut d.definition_point);
|
||||
}
|
||||
Dimension::Angular3Pt(d) => {
|
||||
stretched |= mv(&mut d.angle_vertex);
|
||||
stretched |= mv(&mut d.first_point);
|
||||
stretched |= mv(&mut d.second_point);
|
||||
stretched |= mv(&mut d.definition_point);
|
||||
}
|
||||
Dimension::Ordinate(d) => {
|
||||
stretched |= mv(&mut d.feature_location);
|
||||
stretched |= mv(&mut d.leader_endpoint);
|
||||
}
|
||||
Dimension::Arc(d) => {
|
||||
stretched |= mv(&mut d.definition_point);
|
||||
stretched |= mv(&mut d.first_extension_point);
|
||||
stretched |= mv(&mut d.second_extension_point);
|
||||
stretched |= mv(&mut d.center_point);
|
||||
if d.has_leader {
|
||||
stretched |= mv(&mut d.first_leader_point);
|
||||
stretched |= mv(&mut d.second_leader_point);
|
||||
}
|
||||
}
|
||||
Dimension::LargeRadial(d) => {
|
||||
stretched |= mv(&mut d.definition_point);
|
||||
stretched |= mv(&mut d.chord_point);
|
||||
stretched |= mv(&mut d.override_center);
|
||||
stretched |= mv(&mut d.jog_point);
|
||||
}
|
||||
}
|
||||
// Pinned text follows too; the zero sentinel means
|
||||
// "auto placement" and must not be captured by a
|
||||
// window that happens to cover the origin.
|
||||
let t = dim.base().text_middle_point;
|
||||
if t.x * t.x + t.y * t.y + t.z * t.z > 1e-16 {
|
||||
let mut t = t;
|
||||
if mv(&mut t) {
|
||||
dim.base_mut().text_middle_point = t;
|
||||
stretched = true;
|
||||
}
|
||||
}
|
||||
if stretched {
|
||||
dim.base_mut().actual_measurement = dim.measurement();
|
||||
stretched_dims.push(*handle);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Generic: move entire entity (treat as block-level)
|
||||
stretched = false; // skip generic types
|
||||
}
|
||||
let stretched =
|
||||
crate::modules::draw::modify::stretch::stretch_entity(entity, &windows, delta);
|
||||
if stretched && matches!(entity, codec::EntityType::Dimension(_)) {
|
||||
stretched_dims.push(*handle);
|
||||
}
|
||||
if stretched {
|
||||
if let Some(before) = before {
|
||||
|
||||
Reference in new issue
Block a user