forked from 1b-agent/opencadstudio-upstream
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:
commit
889a19c362
9 files changed
+706
-415
No files matched your search
+155
-56
@@ -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, ¶ms);
|
||||
}
|
||||
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, ¶ms);
|
||||
}
|
||||
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, ¶ms);
|
||||
}
|
||||
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, ¶ms);
|
||||
if !suppressed && params.text_movement != 2 {
|
||||
append_radial_leader(g, tip, tip - center, params.text_position, ¶ms);
|
||||
}
|
||||
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),
|
||||
|
||||
Reference in new issue
Block a user