fix(dimension): a dimension drawn live matches its baked block

Once edited, a dimension is drawn live instead of from its *D block;
on the #898 drawing that differed in three places:

- Oblique / architectural-tick strokes were DIMASZ/2 to each side, the
  block's are DIMASZ/sqrt(2) (its unit stroke runs corner to corner).
- Arrowheads and ticks shared the dimension line's wire, so a dashed
  dim line broke its ticks into stubs. They get their own solid wire
  (and explode as lines as before).
- A diameter's user-positioned text was lifted by the DIMTAD offset even
  when the stored point already was the text's middle, as a file saved
  elsewhere writes it; it is lifted only when it lies on the leader
  (#1323's case).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-01 00:27:42 +03:00
1 parent 59651052f8
commit 1f5a7e97a1
3 files changed
+84 -18

No files matched your search

+72 -15
View File
@@ -4169,6 +4169,21 @@ fn tessellate_dimension_inner(
// match emit_wire's paired fill path.
fill_tris_low: Vec::new(),
});
// Arrowheads and ticks: the dimension line's colour and weight, never
// its dashes.
if !geom.arrow_lines.is_empty() {
if let Some(line) = wires.last() {
let mut arrows = line.clone();
arrows.points = std::mem::take(&mut geom.arrow_lines);
arrows.pattern_length = 0.0;
arrows.pattern = [0.0; 8];
arrows.fill_tris = Vec::new();
arrows.snap_pts = Vec::new();
arrows.key_vertices = Vec::new();
arrows.tangent_geoms = Vec::new();
wires.push(arrows);
}
}
if let Some(symbol) = style.and_then(|style| {
arc_length_symbol_points(dim, Some(style), dim_txt, dim_scale, style.dimarcsym)
@@ -4408,6 +4423,22 @@ fn resolve_dim_lineweight_px(code: i16, fallback_px: f32) -> f32 {
/// Look up a linetype in the document's line_types table by handle and
/// resolve it to a (pattern_length, pattern) pair compatible with WireModel.
/// An arrowhead of a dimension, kept apart from its dimension line: the
/// line takes DIMLTYPE's dashes, an arrowhead or tick is always drawn solid —
/// dashed, a tick broke into two stubs once the line followed its layer's
/// linetype. (#898)
fn append_dim_arrow(g: &mut DimGeom, tip: Vec3, dir: Vec3, arrow: &ArrowKind) {
let mut head = DimGeom::new();
append_arrow(&mut head, tip, dir, arrow);
if !head.dim_lines.is_empty() {
if g.arrow_lines.last().is_some_and(|point| !point[0].is_nan()) {
g.arrow_lines.push([f32::NAN; 3]);
}
g.arrow_lines.extend(head.dim_lines);
}
g.arrow_fill.extend(head.arrow_fill);
}
/// A dimension line's dash pattern. A linetype the style names draws as
/// itself; none or ByBlock takes the dimension's own linetype (ByLayer: its
/// layer's), which is what the ByBlock lines of its baked `*D` block drew —
@@ -4818,7 +4849,7 @@ fn dimension_geometry(
}
if !suppress.dim2 {
// The arrowhead's tip is on the arc, its body inside.
append_arrow(&mut g, point, normalized_or(center - point, Vec3::X), arrow1);
append_dim_arrow(&mut g, point, normalized_or(center - point, Vec3::X), arrow1);
}
// The centre mark belongs to a radius drawn without its
// inside line; the line itself already marks the centre.
@@ -4839,7 +4870,7 @@ fn dimension_geometry(
if !suppress.dim2 {
// The arrowhead sits on the arc with its body toward the text.
let body = if text_is_outside { point - center } else { center - point };
append_arrow(&mut g, point, normalized_or(body, Vec3::X), arrow1);
append_dim_arrow(&mut g, point, normalized_or(body, Vec3::X), arrow1);
}
if text_is_outside {
append_center_mark(&mut g, center, params.dimcen, radius);
@@ -5043,7 +5074,7 @@ fn dimension_geometry(
);
}
if !suppress.dim2 {
append_arrow(
append_dim_arrow(
&mut g,
chord,
if arrows_outside { axis } else { -axis },
@@ -5298,11 +5329,11 @@ fn append_linear_dimension(
if arrows_outside {
// Tip on the ext line, body pointing outward.
append_arrow(g, d1, normalized_or(d1 - d2, -axis), arrow1);
append_arrow(g, d2, normalized_or(d2 - d1, axis), arrow2);
append_dim_arrow(g, d1, normalized_or(d1 - d2, -axis), arrow1);
append_dim_arrow(g, d2, normalized_or(d2 - d1, axis), arrow2);
} else {
append_arrow(g, d1, normalized_or(d2 - d1, axis), arrow1);
append_arrow(g, d2, normalized_or(d1 - d2, -axis), arrow2);
append_dim_arrow(g, d1, normalized_or(d2 - d1, axis), arrow1);
append_dim_arrow(g, d2, normalized_or(d1 - d2, -axis), arrow2);
}
// Horizontal text outside the extension lines of a dimension that is not
@@ -5422,7 +5453,7 @@ fn append_diameter_dimension(
}
if !params.constraint {
if !suppressed {
append_arrow(g, tip, normalized_or(tip - center, axis), arrow1);
append_dim_arrow(g, tip, normalized_or(tip - center, axis), arrow1);
}
return;
}
@@ -5483,11 +5514,11 @@ fn append_diameter_dimension(
append_center_mark(g, center, params.dimcen, diameter * 0.5);
}
if arrows_outside {
append_arrow(g, chord, -axis, arrow1);
append_arrow(g, far_chord, axis, arrow2);
append_dim_arrow(g, chord, -axis, arrow1);
append_dim_arrow(g, far_chord, axis, arrow2);
} else {
append_arrow(g, chord, axis, arrow1);
append_arrow(g, far_chord, -axis, arrow2);
append_dim_arrow(g, chord, axis, arrow1);
append_dim_arrow(g, far_chord, -axis, arrow2);
}
if params.text_movement == 0 {
@@ -6085,7 +6116,7 @@ fn append_angular_dimension(
let end_tangent = Vec3::new(-end.sin(), end.cos(), 0.0) * direction;
let draw_arrows = !arrows_outside || !params.dimsoxd;
if draw_arrows && !suppress.dim1 {
append_arrow(
append_dim_arrow(
g,
arc_start,
if arrows_outside { -start_tangent } else { start_tangent },
@@ -6093,7 +6124,7 @@ fn append_angular_dimension(
);
}
if draw_arrows && !suppress.dim2 {
append_arrow(
append_dim_arrow(
g,
arc_end,
if arrows_outside { end_tangent } else { -end_tangent },
@@ -7694,6 +7725,25 @@ fn radial_outside_text(
)
}
/// Whether `point` lies on the diameter's leader line — the line through its
/// two defining points — rather than a DIMTAD offset away from it.
fn diameter_point_on_leader(
d: &codec::entities::dimension::DimensionDiameter,
point: Vector3,
offset: f64,
) -> bool {
let (dx, dy) = (
d.definition_point.x - d.angle_vertex.x,
d.definition_point.y - d.angle_vertex.y,
);
let length = dx.hypot(dy);
if length < 1e-12 {
return true;
}
let across = ((point.x - d.angle_vertex.x) * dy - (point.y - d.angle_vertex.y) * dx) / length;
across.abs() < offset.abs() * 0.5
}
fn dimension_text_pos_f64(
dim: &Dimension,
style: Option<&DimStyle>,
@@ -7747,7 +7797,14 @@ fn dimension_text_pos_f64(
}
// A diameter whose text the user dragged keeps the grip point on
// the leader and lifts the text by the DIMTAD offset (#1323).
Dimension::Diameter(d) if base.text_user_positioned && dimtad != 0 => {
// A stored point already standing off the leader is the text's
// own middle, as a file saved elsewhere writes it; lifting that
// again floated the text a text height clear of its line (#898).
Dimension::Diameter(d)
if base.text_user_positioned
&& dimtad != 0
&& diameter_point_on_leader(d, point, perp_off) =>
{
let dx = d.definition_point.x - d.angle_vertex.x;
let dy = d.definition_point.y - d.angle_vertex.y;
let len = (dx * dx + dy * dy).sqrt().max(1e-12);
+1
View File
@@ -466,6 +466,7 @@ fn dim_geom_entities(
for (points, common) in [
(geometry.ext_lines.as_slice(), ext_common),
(geometry.dim_lines.as_slice(), dim_common),
(geometry.arrow_lines.as_slice(), dim_common),
] {
for run in points.split(|point| point[0].is_nan()) {
for pair in run.windows(2) {
+11 -3
View File
@@ -2529,9 +2529,13 @@ fn builtin_arrow_from_block_name(name: &str, dimasz: f32) -> Option<ArrowKind> {
Some(ArrowKind::Origin { size: dimasz })
}
// `ArrowKind::Tick` draws the stroke `size` to either side of the tip
// (total 2·size — its `size` is a half-length, matching DIMTSZ). For
// a block-selected tick DIMASZ is the full stroke length, so halve it.
"OBLIQUE" | "ARCHTICK" => Some(ArrowKind::Tick { size: dimasz * 0.5 }),
// (total 2·size — its `size` is a half-length, matching DIMTSZ). The
// block's stroke runs (-0.5,-0.5)→(0.5,0.5) scaled by DIMASZ, so its
// half-length is DIMASZ/√2, not DIMASZ/2 — halving drew every
// oblique stroke √2 short of the file's own. (#898)
"OBLIQUE" | "ARCHTICK" => Some(ArrowKind::Tick {
size: dimasz * std::f32::consts::FRAC_1_SQRT_2,
}),
"BOXFILLED" => Some(ArrowKind::Box_ {
size: dimasz,
filled: true,
@@ -2850,6 +2854,9 @@ fn append_custom_fill_points(
pub(crate) struct DimGeom {
pub(crate) ext_lines: Vec<[f32; 3]>,
pub(crate) dim_lines: Vec<[f32; 3]>,
/// Arrowhead / tick outlines of a dimension: drawn solid, never in the
/// dimension line's dashes.
pub(crate) arrow_lines: Vec<[f32; 3]>,
pub(crate) arrow_fill: Vec<[f32; 3]>,
}
@@ -2858,6 +2865,7 @@ impl DimGeom {
Self {
ext_lines: Vec::new(),
dim_lines: Vec::new(),
arrow_lines: Vec::new(),
arrow_fill: Vec::new(),
}
}