DIMRADIUS/DIMDIAMETER: a location outside the circle puts the text at that point (fixes #1170) (#1189)

This commit is contained in:
Marco Arnulfo
2026-10-19 02:30:31 -04:00
committed by GitHub
parent d33a6e634a
commit 2db6ab069a
4 changed files with 80 additions and 12 deletions
+6 -8
View File
@@ -260,18 +260,16 @@ fn radial(s: &mut Session, p: &Value, diameter: bool) -> Result<Value> {
dim(s, DimKind::Radius, c, Vec2::ZERO, Vec2::ZERO, pt, Vec2::ZERO, "")
};
let k = match at {
Some(at) => text_inside(k, c, c.dist(pt), at),
Some(at) => text_at(k, at),
None => k,
};
Ok(json!({ "handle": add_dim(s, k)?.hex() }))
}
/// A radius/diameter dimension line location picked inside the circle puts the text there, on
/// the dimension line (outside, the text keeps its default place beyond the arrowhead).
fn text_inside(mut k: EntityKind, c: Vec2, r: f64, at: Vec2) -> EntityKind {
if let EntityKind::Dimension(d) = &mut k
&& at.dist(c) < r
{
/// The dimension line location of a radius/diameter dimension puts the text there, inside or
/// outside the circle, as a user-positioned text (DXF flag 70 bit 128), like AutoCAD.
fn text_at(mut k: EntityKind, at: Vec2) -> EntityKind {
if let EntityKind::Dimension(d) = &mut k {
d.text_mid = v3(at);
d.user_text_pos = true;
}
@@ -1106,7 +1104,7 @@ impl RadialM {
} else {
dim(s, DimKind::Radius, c, Vec2::ZERO, Vec2::ZERO, pt, Vec2::ZERO, "")
});
text_inside(k, c, r, at)
text_at(k, at)
}
}
+2 -3
View File
@@ -36,11 +36,10 @@ fn radial_text_goes_inside_when_the_location_is_inside() {
assert!(covers(&g, 9.0) && !covers(&g, 4.0) && !covers(&g, 1.0));
assert!(g.lines_of(LineRole::Dim).flatten().all(|p| p.len() <= 10.0 + 1e-9));
// Outside: the default placement beyond the arrowhead.
// Outside: the text goes to the point too (dim_radial_outside.rs), beyond the arrowhead.
s.script("dimradius 0,10 0,15\n").unwrap();
assert!(s.running.is_none());
let (dm, g) = last_dim(&s);
assert!(!dm.user_text_pos);
let (_, g) = last_dim(&s);
assert!(g.text_pos.y > 10.0, "{:?}", g.text_pos);
// JSON form, diameter: the line spans the circle, broken around the text.
+62
View File
@@ -0,0 +1,62 @@
//! DIMRADIUS / DIMDIAMETER: a dimension line location picked outside the circle puts the text
//! at that point (group 11, flag 70 bit 128), as for a location inside; the leader stops short
//! of the text instead of running into it.
use cadcraft_engine::Session;
use cadcraft_engine::doc::{Dimension, EntityKind};
use cadcraft_engine::geom::Vec2;
use cadcraft_engine::render::{DimGeometry, LineRole};
use serde_json::json;
fn last_dim(s: &Session) -> (Dimension, DimGeometry) {
let d = s.doc().unwrap();
match &d.model.last().unwrap().kind {
EntityKind::Dimension(dm) => (dm.clone(), cadcraft_engine::render::dimension_in(d, dm)),
other => panic!("not a dimension: {other:?}"),
}
}
/// Group 70 of every DIMENSION in the saved DXF.
fn dxf_flags(s: &Session) -> Vec<i64> {
let text = cadcraft_io::write_dxf(s.doc().unwrap());
let lines: Vec<&str> = text.lines().map(str::trim).collect();
let mut flags = Vec::new();
let mut in_dim = false;
for p in lines.chunks(2) {
match p {
["0", v] => in_dim = *v == "DIMENSION",
["70", v] if in_dim => flags.push(v.parse().unwrap()),
_ => {}
}
}
flags
}
fn assert_text_at(s: &Session, at: Vec2) {
assert!(s.running.is_none());
let (dm, g) = last_dim(s);
assert!(dm.user_text_pos);
assert!(dm.text_mid.xy().dist(at) < 1e-9, "{:?}", dm.text_mid);
assert!(g.text_pos.dist(at) < 1e-9, "{:?}", g.text_pos);
let nearest = g.lines_of(LineRole::Dim).flatten().map(|p| p.dist(at)).fold(f64::INFINITY, f64::min);
assert!(nearest > 0.05, "the leader runs into the text: {nearest}");
}
#[test]
fn radial_text_goes_to_a_location_outside_the_circle() {
let mut s = Session::new();
s.script("circle 50,50 25\n").unwrap();
s.script("dimradius 75,50 100,80\n").unwrap();
assert_text_at(&s, Vec2::new(100.0, 80.0));
s.script("circle 300,50 25\n").unwrap();
s.script("dimdiameter 325,50 350,80\n").unwrap();
assert_text_at(&s, Vec2::new(350.0, 80.0));
s.execute("arc", &json!({"center": [50, 200], "radius": 25, "start": 0, "end": 90})).unwrap();
s.script("dimradius 75,200 100,230\n").unwrap();
assert_text_at(&s, Vec2::new(100.0, 230.0));
// Radius 4 | 32 | 128, diameter 3 | 32 | 128, as AutoCAD writes them.
assert_eq!(dxf_flags(&s), vec![164, 163, 164]);
}
+10 -1
View File
@@ -777,7 +777,16 @@ pub fn dimension_geometry_with(d: &Dimension, st: &DimStyle, dimscale: f64, font
end(&mut g, blk1, start, -u);
}
}
g.dim(vec![tip, tpos]);
// Text the user placed: the leader stops short of it by the offset the default
// placement leaves between leader end and text.
let lead = if !d.user_text_pos {
tpos
} else if horizontal {
text_mid - Vec2::new(tw / 2.0 + gap, 0.0) * if u.x >= 0.0 { 1.0 } else { -1.0 }
} else {
text_mid - u * (tw / 2.0 + gap)
};
g.dim(vec![tip, lead]);
}
end(&mut g, blk2, tip, u);
if horizontal {