Merge pull request #1447 from ramox81:codex/diameter-constraint-parity

#1447 branched from the same commit as #1443 and rewrote the radial
dimensional constraint a second time, so the two designs had to be
reconciled rather than stacked. #1447's is the one kept: it covers
DCRADIUS and DCDIAMETER through one command, places the dimension by the
direction the location was picked in rather than storing a text point,
and names the parameters `radN` / `diaN`. #1443's radial command result,
its entity builder call and its follow-up code are gone; its own
independent work stays — the ribbon's small-button grid and tool
descriptions, the magnitude reading of a negative radius, and the centre
anchor a parameter edit resizes a circle about.

The DIMCONSTRAINT default follows #1443: running DCRADIUS or DCDIAMETER
directly leaves it alone, and the Dispatch arm sets it when DIMCONSTRAINT
is what asked for the command.

The second commit's radial drawing — the dimension line broken around the
text, the elbowed leader, the centre-mark and arrowhead rules — applied to
every radius and diameter dimension, not just a constraint's, and three
layout-parity tests said the old layout was the measured one. It is now
scoped to a dimensional constraint's own dimension, as #1443 scoped its
version, and ordinary radial dimensions draw as they did.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 5 committed 2026-09-22 17:51:16 +03:00
commit 889a19c362
9 files changed
+706 -415

No files matched your search

+155 -56
View File
@@ -1134,11 +1134,7 @@ impl Grippable for Dimension {
// of the world origin, so it stays on the visible text and grabbable.
let text = {
let p = self.base().text_middle_point;
// A dynamic radius constraint draws its text at mid-radius, not
// at the stored point; its grip sits on the drawn text.
let drawn_elsewhere =
matches!(self, Dimension::Radius(_)) && dynamic_constraint_dimension(self);
if p.x * p.x + p.y * p.y + p.z * p.z > 1e-16 && !drawn_elsewhere {
if p.x * p.x + p.y * p.y + p.z * p.z > 1e-16 {
dv3(&p)
} else {
dv3(&dimension_text_pos_f64(self, None, 2.5, 1.0))
@@ -3864,6 +3860,8 @@ fn tessellate_dimension_inner(
ticks: dimtsz_raw > 1e-9,
arrow_len: dimasz,
text_width: text_layout.width,
text_height: dim_txt as f32,
constraint: dynamic_constraint_dimension(dim),
dimatfit: style.map(|s| s.dimatfit).unwrap_or(3),
dimtix: style.is_some_and(|s| s.dimtix),
dimtofl: style.map(|s| s.dimtofl).unwrap_or(false),
@@ -4695,6 +4693,11 @@ struct DimLineParams {
/// Arrowhead length (DIMASZ, scaled) — used to decide arrow-outside fit.
arrow_len: f32,
text_width: f32,
/// Text height (DIMTXT, scaled) — a radial leader is sized by it.
text_height: f32,
/// This dimension is a dimensional constraint's dynamic dimension, which
/// the reference draws by its own radial rules.
constraint: bool,
dimatfit: i16,
dimtix: bool,
dimtofl: bool,
@@ -4780,27 +4783,46 @@ fn dimension_geometry(
}
let radius = (point - center).length();
let text_is_outside = text.distance(center) > radius + 1e-5;
// Text inside the arc: the dimension line runs from the arc point
// to the centre. Text outside: only a leader from the arc point to
// the text, unless DIMTOFL asks for the inside line as well.
if !jogged && !suppress.dim2 && (!text_is_outside || params.dimtofl) {
if dynamic_constraint_dimension(dim) {
// The line passes behind the mid-radius text.
if params.constraint {
// A dimensional constraint reads on its own dimension line,
// which runs from the centre to the arc point and breaks
// where the text sits on it. Text that no longer fits inside
// is reached by a leader from the arc point, and the
// arrowhead stays on the arc either way.
if !jogged && !suppress.dim2 {
add_segment_with_text_break(&mut g.dim_lines, center, point, params.text_break);
} else {
}
if text_is_outside && !suppress.dim2 {
append_radial_leader(&mut g, point, point - center, text, &params);
}
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);
}
// The centre mark belongs to a radius drawn without its
// inside line; the line itself already marks the centre.
if jogged || suppress.dim2 {
append_center_mark(&mut g, center, params.dimcen, radius);
}
} else {
// Text inside the arc: the dimension line runs from the arc
// point to the centre. Text outside: only a leader from the
// arc point to the text, unless DIMTOFL asks for the inside
// line as well.
if !jogged && !suppress.dim2 && (!text_is_outside || params.dimtofl) {
add_segment(&mut g.dim_lines, center, point);
}
}
if text_is_outside && !suppress.dim2 {
append_radial_leader(&mut g, point, text, &params);
}
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);
}
if text_is_outside {
append_center_mark(&mut g, center, params.dimcen, radius);
if text_is_outside && !suppress.dim2 {
append_radial_leader(&mut g, point, point - center, text, &params);
}
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);
}
if text_is_outside {
append_center_mark(&mut g, center, params.dimcen, radius);
}
}
}
Dimension::Diameter(d) => {
@@ -4816,9 +4838,11 @@ fn dimension_geometry(
params,
suppress,
);
let center = (chord + far_chord) * 0.5;
let radius = chord.distance(far_chord) * 0.5;
append_center_mark(&mut g, center, params.dimcen, radius);
if !params.constraint {
let center = (chord + far_chord) * 0.5;
let radius = chord.distance(far_chord) * 0.5;
append_center_mark(&mut g, center, params.dimcen, radius);
}
}
Dimension::Angular2Ln(d) => {
// A two-line angular dimension stores two LINES, not two rays:
@@ -5287,11 +5311,36 @@ fn append_linear_dimension(
}
}
/// Leader from a point on the circle to text outside it. Horizontal text gets a
/// hook one arrow long that the text sits against; aligned text is reached by a
/// straight leader that stops a gap short of it. `text_width` already carries
/// a gap on each side.
fn append_radial_leader(g: &mut DimGeom, tip: Vec3, text: Vec3, params: &DimLineParams) {
/// Leader from a point on the circle to text outside it.
///
/// A dimensional constraint's dimension leaves the rim along its dimension
/// line, elbows level with the text and hooks under it. An ordinary radial
/// dimension keeps its measured layout: horizontal text gets a hook one arrow
/// long that the text sits against, and aligned text is reached by a straight
/// leader that stops a gap short of it. `text_width` already carries a gap on
/// each side.
fn append_radial_leader(
g: &mut DimGeom,
tip: Vec3,
direction: Vec3,
text: Vec3,
params: &DimLineParams,
) {
if params.constraint {
let side = if text.x >= tip.x { 1.0 } else { -1.0 };
let edge = match params.leader_anchor {
Some(anchor) => anchor,
None => Vec3::new(text.x - side * params.text_width * 0.5, text.y, text.z),
};
let elbow = tip
+ normalized_or(direction, Vec3::X)
* radial_leader_run(params.text_height as f64) as f32;
// The hook is horizontal at the text's height; the run reaches it.
let elbow = Vec3::new(elbow.x, edge.y, edge.z);
add_segment(&mut g.dim_lines, tip, elbow);
add_segment(&mut g.dim_lines, elbow, edge);
return;
}
if params.horizontal_text {
let side = if text.x >= tip.x { 1.0 } else { -1.0 };
let (hook_start, text_edge) = match params.leader_anchor {
@@ -5333,11 +5382,13 @@ fn append_diameter_dimension(
}
let center = (chord + far_chord) * 0.5;
let text_is_outside = params.text_position.distance(center) > diameter * 0.5 + 1e-5;
if text_is_outside && !params.dimtofl {
// Outside text without DIMTOFL: no line across the circle, just a
// leader from the near side with the arrowhead on the circle and its
// body toward the text. DIMTMOVE 2 keeps the arrowhead and drops the
// leader.
if text_is_outside && (params.constraint || !params.dimtofl) {
// A dimensional constraint carries its text on a leader from the
// nearer end of the dimension line, and the line and both arrowheads
// stay inside the circle. An ordinary diameter draws no line across
// the circle at all: just that leader, with one arrowhead on the
// circle and its body toward the text. DIMTMOVE 2 keeps the arrowhead
// and drops the leader.
let (tip, suppressed) = if params.text_position.distance_squared(chord)
<= params.text_position.distance_squared(far_chord)
{
@@ -5345,13 +5396,15 @@ fn append_diameter_dimension(
} else {
(far_chord, suppress.dim2)
};
if !suppressed {
if params.text_movement != 2 {
append_radial_leader(g, tip, params.text_position, &params);
if !suppressed && params.text_movement != 2 {
append_radial_leader(g, tip, tip - center, params.text_position, &params);
}
if !params.constraint {
if !suppressed {
append_arrow(g, tip, normalized_or(tip - center, axis), arrow1);
}
append_arrow(g, tip, normalized_or(tip - center, axis), arrow1);
return;
}
return;
}
// Text projected outside the diameter requires inward-pointing arrowheads.
let text_along = (params.text_position - chord).dot(axis);
@@ -5359,6 +5412,9 @@ fn append_diameter_dimension(
let arrows_outside = if params.ticks || params.arrow_len <= 1e-6 {
false
} else if text_is_outside {
// The leader carries the text; the arrowheads keep the circle.
false
} else if text_outside {
true
} else if diameter < 2.0 * params.arrow_len {
@@ -5401,6 +5457,10 @@ fn append_diameter_dimension(
}
}
if params.constraint && !draw_inside_line {
let center = (chord + far_chord) * 0.5;
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);
@@ -7551,15 +7611,6 @@ fn stored_text_point(dim: &Dimension) -> Option<Vector3> {
(p.x * p.x + p.y * p.y + p.z * p.z > 1e-16).then_some(p)
}
/// A dimensional constraint's dynamic dimension: the reference keeps it on
/// its own hidden layer and draws it by its own rules.
fn dynamic_constraint_dimension(dim: &Dimension) -> bool {
dim.base()
.common
.layer
.eq_ignore_ascii_case(crate::scene::parametric_constraints::DYNAMIC_DIMENSION_LAYER)
}
/// The point on the circle a radial leader leaves from: the arc point of a
/// radius, or whichever end of a diameter's chord is nearer the text.
fn radial_leader_tip(dim: &Dimension, text: Vector3) -> Vector3 {
@@ -7576,6 +7627,42 @@ fn radial_leader_tip(dim: &Dimension, text: Vector3) -> Vector3 {
}
}
/// A dimensional constraint's dynamic dimension: the reference keeps it on
/// its own hidden layer and draws it by its own rules.
fn dynamic_constraint_dimension(dim: &Dimension) -> bool {
dim.base()
.common
.layer
.eq_ignore_ascii_case(crate::scene::parametric_constraints::DYNAMIC_DIMENSION_LAYER)
}
/// How far a radial leader runs out from the rim before its hook, and how
/// long that hook is: about one text height, so the text always clears the
/// circle.
fn radial_leader_run(text_height: f64) -> f64 {
text_height
}
/// Where a radius or diameter dimension's text sits once it no longer fits
/// inside: beside the circle, level with the end of the leader's run, its
/// near edge one arrow away so the hook has room.
fn radial_outside_text(
tip: Vector3,
ux: f64,
uy: f64,
text_height: f64,
text_width: f64,
) -> Vector3 {
let run = radial_leader_run(text_height);
let elbow = Vector3::new(tip.x + ux * run, tip.y + uy * run, tip.z);
let side = if ux >= 0.0 { 1.0 } else { -1.0 };
Vector3::new(
elbow.x + side * (run + text_width * 0.5),
elbow.y,
elbow.z,
)
}
fn dimension_text_pos_f64(
dim: &Dimension,
style: Option<&DimStyle>,
@@ -7616,12 +7703,6 @@ fn dimension_text_pos_f64(
if let Some(point) = stored_text_point(dim) {
return match dim {
// A dynamic radius constraint draws its text at mid-radius on the
// dimension line, whatever text position the file stores (the
// picked location), as the reference does.
Dimension::Radius(d) if dynamic_constraint_dimension(dim) => {
(d.angle_vertex + d.definition_point) * 0.5
}
// DIMTMOVE 1 stores where the leader's hook starts: one arrow of
// hook, a gap, then the text, all running away from the arc.
// Measured on 39 such dimensions the stored point sits 2.4 text
@@ -7730,6 +7811,11 @@ fn dimension_text_pos_f64(
let ux = dx / radius;
let uy = dy / radius;
let outside = dimension_text_is_outside(dim, style);
if outside && dynamic_constraint_dimension(dim) {
// A constraint's text moves beside the circle at the end of
// its leader; an ordinary radius keeps its measured offset.
return radial_outside_text(d.definition_point, ux, uy, text_height, text_w);
}
let (mut x, mut y) = if outside {
let distance = arrow + text_w * 0.5 + dimgap;
(
@@ -7771,6 +7857,16 @@ fn dimension_text_pos_f64(
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);
if dimension_text_is_outside(dim, style) && dynamic_constraint_dimension(dim) {
// Beyond the end the dimension line was placed towards.
return radial_outside_text(
d.angle_vertex,
-dx / len,
-dy / len,
text_height,
text_w,
);
}
text_on_dim_line(
d.angle_vertex,
d.definition_point,
@@ -7878,6 +7974,9 @@ pub(crate) fn baked_large_radial_geometry(
ticks: tick_size > 1.0e-9,
arrow_len: arrow_size,
text_width: text.width,
text_height: text_height as f32,
// A LargeRadial is never a dimensional constraint's dimension.
constraint: false,
dimatfit: style.map(|style| style.dimatfit).unwrap_or(3),
dimtix: style.is_some_and(|style| style.dimtix),
dimtofl: style.is_some_and(|style| style.dimtofl),