forked from 1b-agent/opencadstudio-upstream
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:
1 parent
59651052f8
commit
1f5a7e97a1
3 files changed
+84
-18
No files matched your search
+72
-15
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user