forked from 1b-agent/opencadstudio-upstream
Merge pull request #1235 with kernel-based dimension leaders
This commit is contained in:
commit
64e64c80a1
4 files changed
+213
-24
No files matched your search
+131
-20
@@ -3768,9 +3768,9 @@ fn tessellate_dimension_inner(
|
||||
// DIMTMOVE=1 connects the dimension line to rendered text.
|
||||
if let Some(s) = style {
|
||||
if s.dimtmove == 1 {
|
||||
if let Some((anchor, txt)) = dimtmove_leader_endpoints(dim, text_layout.position) {
|
||||
if let Some((anchor, txt, under_text)) = dimtmove_leader_endpoints(dim, text_layout) {
|
||||
let gap = dim_txt as f32 * 0.5;
|
||||
if (txt - anchor).length() > gap * 2.0 {
|
||||
if under_text || (txt - anchor).length() > gap * 2.0 {
|
||||
add_segment_with_text_break(
|
||||
&mut geom.dim_lines,
|
||||
anchor,
|
||||
@@ -4297,28 +4297,51 @@ fn split_ext_lines(points: &[[f32; 3]]) -> (Vec<[f32; 3]>, Vec<[f32; 3]>) {
|
||||
}
|
||||
|
||||
/// Endpoints for the DIMTMOVE=1 leader.
|
||||
fn dimtmove_leader_endpoints(dim: &Dimension, txt: Vec3) -> Option<(Vec3, Vec3)> {
|
||||
fn dimtmove_leader_endpoints(
|
||||
dim: &Dimension,
|
||||
text: DimensionTextLayout,
|
||||
) -> Option<(Vec3, Vec3, bool)> {
|
||||
let lv = |v| vec3_local(v);
|
||||
let anchor = match dim {
|
||||
let txt = text.position;
|
||||
let linear = |first: Vec3, second: Vec3, definition: Vec3, axis: [f64; 2]| {
|
||||
let bounds = text.break_box?;
|
||||
let xy = |point: Vec3| [point.x as f64, point.y as f64];
|
||||
let (leader, under_text) = cadkernel::geom2d::linear_dimension_leader(
|
||||
cadkernel::geom2d::Line { start: xy(first), end: xy(second) },
|
||||
xy(definition),
|
||||
axis,
|
||||
xy(txt),
|
||||
[
|
||||
(bounds.half_width + bounds.padding) as f64,
|
||||
(bounds.half_height + bounds.padding) as f64,
|
||||
],
|
||||
bounds.rotation,
|
||||
)?;
|
||||
let point = |p: [f64; 2]| Vec3::new(p[0] as f32, p[1] as f32, first.z);
|
||||
Some((point(leader.start), point(leader.end), under_text))
|
||||
};
|
||||
match dim {
|
||||
Dimension::Linear(d) => {
|
||||
let perp = Vec3::new(-(d.rotation.sin() as f32), d.rotation.cos() as f32, 0.0);
|
||||
let first = lv(d.first_point);
|
||||
let second = lv(d.second_point);
|
||||
let def = lv(d.definition_point);
|
||||
let off1 = def.dot(perp) - first.dot(perp);
|
||||
let off2 = def.dot(perp) - second.dot(perp);
|
||||
(first + perp * off1 + second + perp * off2) * 0.5
|
||||
return linear(
|
||||
lv(d.first_point),
|
||||
lv(d.second_point),
|
||||
lv(d.definition_point),
|
||||
[d.rotation.cos(), d.rotation.sin()],
|
||||
);
|
||||
}
|
||||
Dimension::Aligned(d) => {
|
||||
let first = lv(d.first_point);
|
||||
let second = lv(d.second_point);
|
||||
let axis = normalized_or(second - first, Vec3::X);
|
||||
let perp = Vec3::new(-axis.y, axis.x, 0.0);
|
||||
let def = lv(d.definition_point);
|
||||
let off1 = def.dot(perp) - first.dot(perp);
|
||||
let off2 = def.dot(perp) - second.dot(perp);
|
||||
(first + perp * off1 + second + perp * off2) * 0.5
|
||||
return linear(
|
||||
first,
|
||||
second,
|
||||
lv(d.definition_point),
|
||||
[d.second_point.x - d.first_point.x, d.second_point.y - d.first_point.y],
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let anchor = match dim {
|
||||
Dimension::Radius(_) => return None,
|
||||
Dimension::Diameter(d) => {
|
||||
let chord = lv(d.angle_vertex);
|
||||
@@ -4353,7 +4376,7 @@ fn dimtmove_leader_endpoints(dim: &Dimension, txt: Vec3) -> Option<(Vec3, Vec3)>
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
Some((anchor, txt))
|
||||
Some((anchor, txt, false))
|
||||
}
|
||||
|
||||
/// Build a rectangle of filled triangles sitting under the dim text, used
|
||||
@@ -7188,8 +7211,10 @@ pub(crate) fn baked_large_radial_geometry(
|
||||
}
|
||||
}
|
||||
if style.is_some_and(|style| style.dimtmove == 1) {
|
||||
if let Some((anchor, endpoint)) = dimtmove_leader_endpoints(dimension, text.position) {
|
||||
if anchor.distance(endpoint) > text_height as f32 {
|
||||
if let Some((anchor, endpoint, under_text)) =
|
||||
dimtmove_leader_endpoints(dimension, text)
|
||||
{
|
||||
if under_text || anchor.distance(endpoint) > text_height as f32 {
|
||||
add_segment_with_text_break(
|
||||
&mut geometry.dim_lines,
|
||||
anchor,
|
||||
@@ -7527,3 +7552,89 @@ mod arch_format_tests {
|
||||
assert_eq!(format_engineering(-0.001, 2), "0'-0.00\"");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod dimtmove_leader_tests {
|
||||
use super::*;
|
||||
use acadrust::entities::DimensionLinear;
|
||||
use acadrust::types::Vector3;
|
||||
|
||||
fn v(x: f64, y: f64) -> Vector3 {
|
||||
Vector3::new(x, y, 0.0)
|
||||
}
|
||||
|
||||
fn layout(position: Vec3, half_width: f32) -> DimensionTextLayout {
|
||||
DimensionTextLayout {
|
||||
position,
|
||||
width: half_width * 2.0,
|
||||
break_box: Some(TextBreak {
|
||||
center: position,
|
||||
half_width,
|
||||
half_height: 1.0,
|
||||
padding: 0.0,
|
||||
rotation: 0.0,
|
||||
}),
|
||||
horizontal: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Text outside along the axis → the dim line extends underneath the text.
|
||||
#[test]
|
||||
fn text_outside_extends_dim_line_under_text() {
|
||||
let mut d = DimensionLinear::horizontal(v(0.0, 0.0), v(20.0, 0.0));
|
||||
d.definition_point = v(0.0, -5.0); // dimension line at y=-5
|
||||
let dim = Dimension::Linear(d);
|
||||
// text anchor outside on the right: centre x=30, half width 3 → far edge 33
|
||||
let txt = vec3_local(v(30.0, -3.0));
|
||||
let (anchor, end, under) = dimtmove_leader_endpoints(&dim, layout(txt, 3.0)).expect("leader");
|
||||
assert!(under, "outside text extends the dimension line");
|
||||
assert!((anchor.x - 20.0).abs() < 1e-4 && (anchor.y + 5.0).abs() < 1e-4, "near end should be the dim line's right end (20,-5), got {anchor:?}");
|
||||
assert!((end.x - 33.0).abs() < 1e-4 && (end.y + 5.0).abs() < 1e-4, "far end should be the text's far edge (33,-5), got {end:?}");
|
||||
}
|
||||
|
||||
/// Text outside on the left → extends leftwards.
|
||||
#[test]
|
||||
fn text_outside_left_extends_leftwards() {
|
||||
let mut d = DimensionLinear::horizontal(v(0.0, 0.0), v(20.0, 0.0));
|
||||
d.definition_point = v(0.0, -5.0);
|
||||
let dim = Dimension::Linear(d);
|
||||
let txt = vec3_local(v(-12.0, -3.0));
|
||||
let (anchor, end, under) = dimtmove_leader_endpoints(&dim, layout(txt, 2.0)).expect("leader");
|
||||
assert!(under);
|
||||
assert!((anchor.x - 0.0).abs() < 1e-4, "near end should be the dim line's left end, got {anchor:?}");
|
||||
assert!((end.x + 14.0).abs() < 1e-4, "far end should be the text's far edge at -14, got {end:?}");
|
||||
}
|
||||
|
||||
/// Text still inside (only moved up/down) → leader from the dim line to the text.
|
||||
#[test]
|
||||
fn text_inside_connects_to_text() {
|
||||
let mut d = DimensionLinear::horizontal(v(0.0, 0.0), v(20.0, 0.0));
|
||||
d.definition_point = v(0.0, -5.0);
|
||||
let dim = Dimension::Linear(d);
|
||||
let txt = vec3_local(v(10.0, 2.0));
|
||||
let (anchor, end, under) = dimtmove_leader_endpoints(&dim, layout(txt, 3.0)).expect("leader");
|
||||
assert!(!under, "inside → keep the connect-to-text leader (text break allowed)");
|
||||
assert!((anchor.x - 10.0).abs() < 1e-4 && (anchor.y + 5.0).abs() < 1e-4, "anchor should be the dim line point facing the text, got {anchor:?}");
|
||||
assert!((end.y - 2.0).abs() < 1e-4, "end should be the text position, got {end:?}");
|
||||
}
|
||||
|
||||
/// Radius dimensions draw no leader (unchanged).
|
||||
#[test]
|
||||
fn radius_has_no_leader() {
|
||||
let r = acadrust::entities::DimensionRadius::new(v(0.0, 0.0), v(0.0, 5.0));
|
||||
assert!(dimtmove_leader_endpoints(&Dimension::Radius(r), layout(vec3_local(v(0.0, 8.0)), 2.0)).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_does_not_draw_through_text() {
|
||||
let mut d = DimensionLinear::horizontal(v(0.0, 0.0), v(20.0, 0.0));
|
||||
d.definition_point = v(0.0, -5.0);
|
||||
let text = layout(Vec3::new(30.0, -5.0, 0.0), 3.0);
|
||||
let (start, end, outside) = dimtmove_leader_endpoints(&Dimension::Linear(d), text).unwrap();
|
||||
assert!(outside);
|
||||
let mut points = Vec::new();
|
||||
add_segment_with_text_break(&mut points, start, end, text.break_box);
|
||||
let finite: Vec<_> = points.into_iter().filter(|point| point[0].is_finite()).collect();
|
||||
assert_eq!(finite, vec![[20.0, -5.0, 0.0], [27.0, -5.0, 0.0]]);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user