MTEXT: annotative scaling and SCALE on multileader text scale the text width and columns too (fixes #708, fixes #709) (#743)
Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4 files changed
+110
-1
No files matched your search
@@ -159,6 +159,9 @@ pub fn anno_scaled(e: &Entity, f: f64) -> Entity {
|
||||
EntityKind::MText(t) => {
|
||||
t.height *= f;
|
||||
t.width *= f;
|
||||
if let Some(c) = &mut t.columns {
|
||||
c.scale(f);
|
||||
}
|
||||
}
|
||||
EntityKind::Dimension(dm) => {
|
||||
dm.overrides.insert("scale".into(), serde_json::json!(f));
|
||||
@@ -172,6 +175,9 @@ pub fn anno_scaled(e: &Entity, f: f64) -> Entity {
|
||||
t.insert = land + (t.insert - land) * f;
|
||||
t.height *= f;
|
||||
t.width *= f;
|
||||
if let Some(c) = &mut t.columns {
|
||||
c.scale(f);
|
||||
}
|
||||
}
|
||||
if let Some(ins) = &mut m.block {
|
||||
ins.insert = land + (ins.insert - land) * f;
|
||||
|
||||
@@ -1212,6 +1212,10 @@ impl EntityKind {
|
||||
if let Some(t) = &mut ml.text {
|
||||
t3(&mut t.insert);
|
||||
t.height *= s;
|
||||
t.width *= s;
|
||||
if let Some(c) = &mut t.columns {
|
||||
c.scale(s);
|
||||
}
|
||||
}
|
||||
if let Some(b) = &mut ml.block {
|
||||
// Transformed exactly like a block reference.
|
||||
|
||||
@@ -283,3 +283,56 @@ fn hostile_parameters_are_refused() {
|
||||
assert!(s.execute("scalelistedit", &p).is_err(), "{p}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn annotative_mtext_columns_follow_the_scale() {
|
||||
// #708: at 1:2, two columns 10 wide with a gutter of 1 and a height of 20 on paper draw 20
|
||||
// wide, 2 apart and 40 tall: the box is 42 × 40, not 41 × 20.
|
||||
let mut s = Session::new();
|
||||
setvar(&mut s, "CANNOSCALE", json!("1:2"));
|
||||
let r = s
|
||||
.execute(
|
||||
"mtext",
|
||||
&json!({ "at": [0, 0], "height": 1, "width": 10, "text": "a\\Pb\\Pc", "columns": { "type": "static", "count": 2, "gutter": 1, "height": 20 } }),
|
||||
)
|
||||
.unwrap();
|
||||
let h = Handle::parse_hex(r["handle"].as_str().unwrap()).unwrap();
|
||||
s.execute("properties.set", &json!({ "handles": [h.hex()], "annotative": true })).unwrap();
|
||||
assert_eq!(scales(&s, h), ["1:2"]);
|
||||
let v = |s: &mut Session, name: &str| s.execute("getvar", &json!({ "name": name })).unwrap()["value"].clone();
|
||||
assert_eq!(v(&mut s, "EXTMIN"), json!([0.0, -40.0, 0.0]));
|
||||
assert_eq!(v(&mut s, "EXTMAX"), json!([42.0, 0.0, 0.0]));
|
||||
// Dynamic columns with manual heights, and the text of a multileader, scale the same way.
|
||||
let mut e = entity(&s, h);
|
||||
if let EntityKind::MText(t) = &mut e.kind
|
||||
&& let Some(c) = &mut t.columns
|
||||
{
|
||||
c.heights = vec![3.0, 5.0];
|
||||
}
|
||||
let mtext = match &e.kind {
|
||||
EntityKind::MText(t) => t.clone(),
|
||||
k => panic!("{k:?}"),
|
||||
};
|
||||
let ml = Entity {
|
||||
kind: EntityKind::MLeader(cadcraft_engine::doc::MLeader {
|
||||
leaders: Vec::new(),
|
||||
landing: Default::default(),
|
||||
dogleg: 1.0,
|
||||
text: Some(mtext),
|
||||
style: "Standard".into(),
|
||||
arrow_size: 1.0,
|
||||
spline: false,
|
||||
block: None,
|
||||
}),
|
||||
..e.clone()
|
||||
};
|
||||
for e in [e, ml] {
|
||||
let t = match cadcraft_engine::doc::anno_scaled(&e, 2.0).kind {
|
||||
EntityKind::MText(t) => t,
|
||||
EntityKind::MLeader(m) => m.text.unwrap(),
|
||||
k => panic!("{k:?}"),
|
||||
};
|
||||
let c = t.columns.unwrap();
|
||||
assert_eq!((t.height, t.width, c.count, c.gutter, c.height, c.heights), (2.0, 20.0, 2, 2.0, 40.0, vec![6.0, 10.0]));
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
//! MTEXT columns and paragraph formatting from the command line, scripts and the JSON form
|
||||
//! (#532).
|
||||
|
||||
use cadcraft_doc::{EntityKind, MText, MTextColumnType};
|
||||
use cadcraft_doc::{EntityKind, MLeader, MText, MTextColumnType, MTextColumns};
|
||||
use cadcraft_engine::Session;
|
||||
use serde_json::json;
|
||||
|
||||
@@ -79,3 +79,49 @@ fn columns_shape_the_drawn_text_and_its_extents() {
|
||||
let b = cadcraft_doc::entity_bounds(d, e, 0);
|
||||
assert!((b.width() - 34.0).abs() < 1e-9 && (b.height() - 6.0).abs() < 1e-9, "{b:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scale_scales_multileader_text_width_and_columns() {
|
||||
// #709: SCALE 2 on a multileader's text doubles its width and column lengths like it does
|
||||
// for a standalone MTEXT, not only its height.
|
||||
let text = MText {
|
||||
insert: Default::default(),
|
||||
height: 2.0,
|
||||
width: 10.0,
|
||||
attach: 1,
|
||||
rotation: 0.0,
|
||||
style: "Standard".into(),
|
||||
contents: "hello world".into(),
|
||||
line_spacing: 1.0,
|
||||
line_spacing_exact: false,
|
||||
columns: Some(MTextColumns {
|
||||
kind: MTextColumnType::Static,
|
||||
count: 2,
|
||||
gutter: 1.0,
|
||||
height: 5.0,
|
||||
auto_height: true,
|
||||
heights: vec![3.0, 4.0],
|
||||
flow_reversed: false,
|
||||
}),
|
||||
};
|
||||
let mut s = Session::new();
|
||||
let ml = MLeader {
|
||||
leaders: Vec::new(),
|
||||
landing: Default::default(),
|
||||
dogleg: 1.0,
|
||||
text: Some(text),
|
||||
style: "Standard".into(),
|
||||
arrow_size: 1.0,
|
||||
spline: false,
|
||||
block: None,
|
||||
};
|
||||
let h = s.add_entity(EntityKind::MLeader(ml)).unwrap();
|
||||
s.execute("scale", &json!({ "handles": [h.hex()], "base": [0, 0], "factor": 2 })).unwrap();
|
||||
let t = match &s.doc().unwrap().entity(h).unwrap().kind {
|
||||
EntityKind::MLeader(m) => m.text.clone().unwrap(),
|
||||
k => panic!("{k:?}"),
|
||||
};
|
||||
let c = t.columns.unwrap();
|
||||
assert_eq!((t.height, t.width), (4.0, 20.0));
|
||||
assert_eq!((c.kind, c.count, c.gutter, c.height, c.heights), (MTextColumnType::Static, 2, 2.0, 10.0, vec![6.0, 8.0]));
|
||||
}
|
||||
Reference in new issue
Block a user