forked from 1b-agent/opencadstudio-upstream
fix(dimension): bake↔live consistency, edits, baseline/continue, grips
Batch 1-3 of the dimension audit (19 issues). The dominant class was the baked *D block diverging from the live render: a reloaded dimension renders from that block, so on save+reopen it silently changed. Bake now matches the live render: - text value/format via the shared style-resolved path (DIMDEC/DIMLFAC/ DIMPOST/units/<>), correct height + rotation ladder, suppression - angular dims bake their swept arc (no longer collapse to a V) - diameter draws edge-to-edge through the centre (live + bake), centre mark - radius/linear/aligned bake arrowheads - group-10 (base.definition_point) synced from the edited geometry so an edited dim no longer shifts on reload Editing / properties: - aligned no longer corrupts ext_line_rotation when a coordinate is edited - DIMTEDIT pins the moved text (text_user_positioned) - angle properties shown/edited in degrees - non-linear text respects the user-positioned gate Commands / geometry: - DIMBASELINE stacks outward (not toward the points) and its preview no longer tilts over non-level points - DIMCONTINUE/DIMBASELINE inherit the base dim's text rotation (UCS chains) - angular extension lines reach the arc (no gap) - text grip sits on the text for auto-placed dims instead of the origin Refs #181. (acadrust DXF round-trip + QDIM/DIMBREAK/DIMJOGLINE/DIMSPACE/ DIMEDIT to follow.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
b691854e7a
commit
3c5aa4453a
7 files changed
+591
-131
No files matched your search
@@ -34,6 +34,9 @@ pub struct DimBaselineCommand {
|
||||
base_p1: Vec3,
|
||||
/// Direction along the measurement direction (0.0 = horizontal, PI/2 = vertical).
|
||||
rotation: f64,
|
||||
/// Text reading rotation inherited from the base dim (keeps a UCS-aligned
|
||||
/// chain's text consistent with the originating DIMLINEAR). 0 = natural.
|
||||
text_rotation: f64,
|
||||
/// Unit vector perpendicular to the dimension axis, pointing toward the dim line side.
|
||||
perp: Vec3,
|
||||
/// Perpendicular distance of the NEXT dimension line from the extension-line axis.
|
||||
@@ -50,6 +53,7 @@ impl DimBaselineCommand {
|
||||
Self {
|
||||
base_p1: Vec3::ZERO,
|
||||
rotation: 0.0,
|
||||
text_rotation: 0.0,
|
||||
perp: Vec3::Y,
|
||||
next_offset: DEFAULT_DIMDLI,
|
||||
dimdli: DEFAULT_DIMDLI,
|
||||
@@ -69,6 +73,7 @@ impl DimBaselineCommand {
|
||||
_p2: Vec3,
|
||||
definition_point: Vec3,
|
||||
rotation: f64,
|
||||
text_rotation: f64,
|
||||
dimdli: f32,
|
||||
) -> Self {
|
||||
// Measurement axis = the base dim's rotation angle (any angle, incl. a
|
||||
@@ -79,13 +84,19 @@ impl DimBaselineCommand {
|
||||
let dimdli = if dimdli.abs() < 1e-6 {
|
||||
DEFAULT_DIMDLI
|
||||
} else {
|
||||
dimdli
|
||||
dimdli.abs()
|
||||
};
|
||||
// Next baseline dim goes one DIMDLI further from the baseline.
|
||||
// Stack each baseline outward — in the SIGN direction of the base dim's
|
||||
// own offset. When the base dim sits on the -perp side (below / left of
|
||||
// the points) a positive increment would march the stack back toward and
|
||||
// across the points; the signed increment keeps it moving away. (#181)
|
||||
let dir = if base_offset >= 0.0 { 1.0 } else { -1.0 };
|
||||
let dimdli = dimdli * dir;
|
||||
let next_offset = base_offset + dimdli;
|
||||
Self {
|
||||
base_p1: p1,
|
||||
rotation,
|
||||
text_rotation,
|
||||
perp,
|
||||
next_offset,
|
||||
dimdli,
|
||||
@@ -94,6 +105,19 @@ impl DimBaselineCommand {
|
||||
}
|
||||
}
|
||||
|
||||
impl DimBaselineCommand {
|
||||
/// The two dimension-line endpoints for the new baseline at `p2`: the fixed
|
||||
/// base origin and `p2` each projected INDEPENDENTLY onto the dim line (at
|
||||
/// the current stacking offset). Projecting both keeps the line straight
|
||||
/// even when the two origins aren't level — a shared offset tilts it. (#181)
|
||||
fn dim_line_pts(&self, p2: Vec3) -> (Vec3, Vec3) {
|
||||
let target = self.base_p1.dot(self.perp) + self.next_offset;
|
||||
let d1 = self.base_p1 + self.perp * (target - self.base_p1.dot(self.perp));
|
||||
let d2 = p2 + self.perp * (target - p2.dot(self.perp));
|
||||
(d1, d2)
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for DimBaselineCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"DIMBASELINE"
|
||||
@@ -117,9 +141,11 @@ impl CadCommand for DimBaselineCommand {
|
||||
// Build a new linear dimension.
|
||||
let mut dim = DimensionLinear::new(v3(p1), v3(p2));
|
||||
dim.rotation = self.rotation;
|
||||
if self.text_rotation.abs() > 1e-9 {
|
||||
dim.base.text_rotation = self.text_rotation;
|
||||
}
|
||||
|
||||
let dim_line_pt = p1 + self.perp * self.next_offset;
|
||||
let dim_line_pt2 = p2 + self.perp * self.next_offset;
|
||||
let (dim_line_pt, dim_line_pt2) = self.dim_line_pts(p2);
|
||||
dim.definition_point = v3(dim_line_pt);
|
||||
dim.base.definition_point = v3(dim_line_pt);
|
||||
dim.base.text_middle_point = v3((dim_line_pt + dim_line_pt2) * 0.5);
|
||||
@@ -141,8 +167,7 @@ impl CadCommand for DimBaselineCommand {
|
||||
return None;
|
||||
}
|
||||
let p1 = self.base_p1;
|
||||
let dim_line_pt = p1 + self.perp * self.next_offset;
|
||||
let dim_line_pt2 = pt + self.perp * self.next_offset;
|
||||
let (dim_line_pt, dim_line_pt2) = self.dim_line_pts(pt);
|
||||
Some(WireModel {
|
||||
name: "dimbase_preview".into(),
|
||||
points: vec![
|
||||
|
||||
@@ -31,6 +31,9 @@ pub struct DimContinueCommand {
|
||||
chain_p1: Vec3,
|
||||
/// Direction along the dimension axis (0.0 = horizontal, PI/2 = vertical).
|
||||
rotation: f64,
|
||||
/// Text reading rotation inherited from the base dim so a UCS-aligned chain
|
||||
/// keeps its text consistent with the originating DIMLINEAR. 0 = natural.
|
||||
text_rotation: f64,
|
||||
/// Absolute perpendicular coordinate (dot with `perp`) of the base
|
||||
/// dimension line. Each continued dim line is projected onto this exact
|
||||
/// coordinate so the whole chain stays collinear — even when the extension
|
||||
@@ -49,6 +52,7 @@ impl DimContinueCommand {
|
||||
Self {
|
||||
chain_p1: Vec3::ZERO,
|
||||
rotation: 0.0,
|
||||
text_rotation: 0.0,
|
||||
dim_line_perp: 0.0,
|
||||
perp: Vec3::Y,
|
||||
ready: false,
|
||||
@@ -64,7 +68,13 @@ impl DimContinueCommand {
|
||||
/// `definition_point` — where the dim line was placed; its perpendicular
|
||||
/// coordinate is the line the whole chain stays collinear with.
|
||||
/// `rotation` — 0.0 = horizontal dim, PI/2 = vertical dim.
|
||||
pub fn from_base(_p1: Vec3, p2: Vec3, definition_point: Vec3, rotation: f64) -> Self {
|
||||
pub fn from_base(
|
||||
_p1: Vec3,
|
||||
p2: Vec3,
|
||||
definition_point: Vec3,
|
||||
rotation: f64,
|
||||
text_rotation: f64,
|
||||
) -> Self {
|
||||
// Axis unit vector along the measurement direction — the base dim's
|
||||
// rotation angle (any angle, incl. a UCS-aligned one), not a world H/V.
|
||||
let axis = Vec3::new(rotation.cos() as f32, rotation.sin() as f32, 0.0);
|
||||
@@ -74,6 +84,7 @@ impl DimContinueCommand {
|
||||
Self {
|
||||
chain_p1: p2,
|
||||
rotation,
|
||||
text_rotation,
|
||||
dim_line_perp,
|
||||
perp,
|
||||
ready: true,
|
||||
@@ -104,6 +115,9 @@ impl CadCommand for DimContinueCommand {
|
||||
// Build a new linear dimension.
|
||||
let mut dim = DimensionLinear::new(v3(p1), v3(p2));
|
||||
dim.rotation = self.rotation;
|
||||
if self.text_rotation.abs() > 1e-9 {
|
||||
dim.base.text_rotation = self.text_rotation;
|
||||
}
|
||||
|
||||
// Dim line stays collinear with the base: project both extension
|
||||
// origins onto the base dim line's absolute perpendicular coordinate.
|
||||
|
||||
@@ -78,6 +78,9 @@ impl CadCommand for DimTeditCommand {
|
||||
acadrust::types::Vector3::new(pt.x as f64, pt.y as f64, pt.z as f64);
|
||||
d.base_mut().text_middle_point = new_pt;
|
||||
d.base_mut().insertion_point = new_pt;
|
||||
// Pin the text to this location, else the renderer recomputes
|
||||
// the style-default placement and the move is discarded.
|
||||
d.base_mut().text_user_positioned = true;
|
||||
}
|
||||
return CmdResult::ReplaceEntity(h, vec![ent]);
|
||||
}
|
||||
|
||||
@@ -151,28 +151,28 @@ fn preview_wire(points: Vec<Vec3>) -> WireModel {
|
||||
}
|
||||
}
|
||||
|
||||
fn linear_dimension_preview(first: Vec3, second: Vec3, def: Vec3, axis: Vec3) -> Vec<Vec3> {
|
||||
/// Project the two extension origins onto the dimension line, which passes
|
||||
/// through `def` along `axis`. Each origin is projected *independently*: a
|
||||
/// single shared offset only lands both on the line when they are level, and
|
||||
/// tilts the dimension line when they are not (e.g. measuring across sloped
|
||||
/// points). See #181.
|
||||
fn dim_line_endpoints(first: Vec3, second: Vec3, def: Vec3, axis: Vec3) -> (Vec3, Vec3) {
|
||||
let perp = Vec3::new(-axis.y, axis.x, 0.0);
|
||||
let offset = (def - first).dot(perp);
|
||||
let d1 = first + perp * offset;
|
||||
let d2 = second + perp * offset;
|
||||
vec![
|
||||
first,
|
||||
d1,
|
||||
Vec3::new(f32::NAN, f32::NAN, f32::NAN),
|
||||
second,
|
||||
d2,
|
||||
Vec3::new(f32::NAN, f32::NAN, f32::NAN),
|
||||
d1,
|
||||
d2,
|
||||
]
|
||||
let dperp = def.dot(perp);
|
||||
let d1 = first + perp * (dperp - first.dot(perp));
|
||||
let d2 = second + perp * (dperp - second.dot(perp));
|
||||
(d1, d2)
|
||||
}
|
||||
|
||||
fn linear_dimension_preview(first: Vec3, second: Vec3, def: Vec3, axis: Vec3) -> Vec<Vec3> {
|
||||
let (d1, d2) = dim_line_endpoints(first, second, def, axis);
|
||||
let nan = Vec3::new(f32::NAN, f32::NAN, f32::NAN);
|
||||
vec![first, d1, nan, second, d2, nan, d1, d2]
|
||||
}
|
||||
|
||||
fn linear_text_pos(first: Vec3, second: Vec3, def: Vec3, axis: Vec3) -> Vec3 {
|
||||
let (d1, d2) = dim_line_endpoints(first, second, def, axis);
|
||||
let perp = Vec3::new(-axis.y, axis.x, 0.0);
|
||||
let offset = (def - first).dot(perp);
|
||||
let d1 = first + perp * offset;
|
||||
let d2 = second + perp * offset;
|
||||
(d1 + d2) * 0.5 + perp * 0.15
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user