forked from 1b-agent/opencadstudio-upstream
DIMTMOVE=1 ("moves the text and adds a leader") draws the leader from the midpoint
of the dimension line to the text, so a text dragged outside the dimension line gets
a diagonal that starts in the middle of the line and crosses the extension lines.
Mechanical-drawing practice (GB/T 4458.4, and what AutoCAD itself draws) extends the
dimension line along its own axis so that it runs underneath the text. Linear and
aligned dimensions now do that whenever the text is outside the dimension-line extent;
when the text is still inside it (only moved up/down) the previous connect-to-text
leader is kept. Every other dimension type is unchanged, and only dimensions with
DIMTMOVE=1 are affected at all (the shipped default is 0).
Adds four unit tests covering the placements (outside right, outside left, inside,
radius draws none) plus an integration render smoke test driven through the real
Scene::entity_wires() -> export_pdf() pipeline, gated by OCS_DIM_LEADER_PDF so CI
stays side-effect free.
113 lines
4.2 KiB
Rust
113 lines
4.2 KiB
Rust
//! Render smoke test: with DIMTMOVE=1 a linear dimension whose text is dragged
|
|
//! outside the dimension line draws a leader underneath the text
|
|
//! (GB/T 4458.4 / AutoCAD behaviour).
|
|
//!
|
|
//! Same pipeline as `pdf_export_text_check.rs` (Scene::entity_wires() →
|
|
//! export_pdf()) but writes a PDF for manual/visual inspection. The file is only
|
|
//! written when `OCS_DIM_LEADER_PDF` is set, so CI stays side-effect free.
|
|
use acadrust::entities::{Dimension, DimensionLinear};
|
|
use acadrust::types::Vector3;
|
|
use acadrust::xdata::{ExtendedDataRecord, XDataValue};
|
|
use acadrust::EntityType;
|
|
use OpenCADStudio::io::pdf_export::{export_pdf, PdfPlotOptions, PlotWire};
|
|
use OpenCADStudio::scene::Scene;
|
|
|
|
/// Build a per-object DSTYLE override record (same shape as the properties
|
|
/// panel / `src/entities/dim_override.rs` write).
|
|
fn dstyle(pairs: &[(i16, XDataValue)]) -> ExtendedDataRecord {
|
|
let mut rec = ExtendedDataRecord::new("ACAD");
|
|
rec.add_value(XDataValue::String("DSTYLE".into()));
|
|
rec.add_value(XDataValue::ControlString("{".into()));
|
|
for (code, value) in pairs {
|
|
rec.add_value(XDataValue::Integer16(*code));
|
|
rec.add_value(value.clone());
|
|
}
|
|
rec.add_value(XDataValue::ControlString("}".into()));
|
|
rec
|
|
}
|
|
|
|
fn v(x: f64, y: f64) -> Vector3 {
|
|
Vector3::new(x, y, 0.0)
|
|
}
|
|
|
|
/// Linear dimension with the text dragged outside to the right and DIMTMOVE=1:
|
|
/// the rendered dimension line must extend underneath the text.
|
|
#[test]
|
|
fn dim_leader_under_outside_text_renders() {
|
|
let out = std::env::var("OCS_DIM_LEADER_PDF").unwrap_or_default();
|
|
|
|
let mut scene = Scene::new();
|
|
let mut d = DimensionLinear::horizontal(v(20.0, 100.0), v(80.0, 100.0));
|
|
d.definition_point = v(20.0, 86.0); // dimension line at y=86
|
|
d.base.definition_point = d.definition_point;
|
|
d.base.actual_measurement = 60.0;
|
|
// Text dragged past the right end of the dimension line (centre x=98, end x=80).
|
|
d.base.text_middle_point = v(98.0, 89.0);
|
|
d.base.insertion_point = d.base.text_middle_point;
|
|
d.base.text_user_positioned = true;
|
|
// Per-object overrides, as the properties panel writes them: DIMTMOVE=1
|
|
// plus a visible text height / arrow size / gap.
|
|
d.base.style_name = "Standard".into();
|
|
d.base.common.extended_data.add_record(dstyle(&[
|
|
(279, XDataValue::Integer16(1)), // DIMTMOVE=1 → draw a leader when the text moves out
|
|
(140, XDataValue::Real(3.5)), // DIMTXT
|
|
(41, XDataValue::Real(3.5)), // DIMASZ
|
|
(147, XDataValue::Real(1.0)), // DIMGAP
|
|
(271, XDataValue::Integer16(2)), // DIMDEC
|
|
]));
|
|
scene.add_entity(EntityType::Dimension(Dimension::Linear(d)));
|
|
|
|
// Control case: the same dimension with DIMTMOVE=0 (no leader).
|
|
let mut d0 = DimensionLinear::horizontal(v(20.0, 60.0), v(80.0, 60.0));
|
|
d0.definition_point = v(20.0, 46.0);
|
|
d0.base.definition_point = d0.definition_point;
|
|
d0.base.actual_measurement = 60.0;
|
|
d0.base.text_middle_point = v(98.0, 49.0);
|
|
d0.base.insertion_point = d0.base.text_middle_point;
|
|
d0.base.text_user_positioned = true;
|
|
d0.base.style_name = "Standard".into();
|
|
d0.base.common.extended_data.add_record(dstyle(&[
|
|
(279, XDataValue::Integer16(0)), // control: no leader
|
|
(140, XDataValue::Real(3.5)),
|
|
(41, XDataValue::Real(3.5)),
|
|
(147, XDataValue::Real(1.0)),
|
|
(271, XDataValue::Integer16(2)),
|
|
]));
|
|
scene.add_entity(EntityType::Dimension(Dimension::Linear(d0)));
|
|
|
|
let plot_wires: Vec<PlotWire> = scene
|
|
.entity_wires()
|
|
.iter()
|
|
.map(|w| PlotWire {
|
|
wire: w.clone(),
|
|
draw_depth: 0.0,
|
|
})
|
|
.collect();
|
|
assert!(!plot_wires.is_empty(), "scene should produce geometry");
|
|
|
|
if out.trim().is_empty() {
|
|
return; // env var unset: exercise the render path only, write nothing
|
|
}
|
|
let path = std::path::PathBuf::from(&out);
|
|
if let Some(dir) = path.parent() {
|
|
std::fs::create_dir_all(dir).ok();
|
|
}
|
|
export_pdf(
|
|
&plot_wires,
|
|
&[],
|
|
&[],
|
|
210.0,
|
|
297.0,
|
|
0.0,
|
|
0.0,
|
|
0,
|
|
1.0,
|
|
None,
|
|
&path,
|
|
None,
|
|
PdfPlotOptions::default(),
|
|
)
|
|
.expect("export pdf");
|
|
println!("dim leader render smoke pdf written: {out}");
|
|
}
|