Files
cadcraft/crates/render/tests/it/render_props.rs
aiongg 9cf115e808 CI: one test binary per crate, nextest, line tables only (#1989)
* Build: line tables only in dev, no debug info for dependencies

* Tests: one integration-test binary per crate

Each crate's tests/*.rs becomes a module of tests/it/main.rs. Files that
install process-wide state (the file io hooks, user aliases, SAVETIME and
SAVEFILEPATH, user linetype and pattern files, registered fonts) stay their
own test binaries.

* xtask ci: nextest, tests without examples, one wasm check call
2026-10-23 13:34:26 -04:00

187 lines
7.4 KiB
Rust

//! Dimensions take viewport layer colours, leader arrows their style's DIMSCALE, attributes
//! their own properties (issue #313).
use std::sync::Arc;
use cadcraft_color::{Color, Rgb};
use cadcraft_doc::{
Attrib, Block, Common, DimKind, DimStyle, Dimension, Drawing, Entity, EntityKind, HAlign, Handle, Insert, Leader, Line, Lineweight, Space, Text,
VAlign, Viewport,
};
use cadcraft_geom::{Bounds2, Vec2, Vec3};
use cadcraft_render::{Kind, Options, VIEWPORT_CONTENT, build};
fn rgb(i: u8) -> Rgb {
Color::Index(i).resolve(Color::Index(7), Color::Index(7))
}
fn layer(d: &mut Drawing, name: &str, color: u8, lw: Lineweight) {
d.ensure_layer(name);
let l = d.layer_mut(name).unwrap();
l.color = Color::Index(color);
l.lineweight = lw;
}
fn on(layer: &str) -> Common {
Common { layer: layer.into(), ..Default::default() }
}
#[test]
fn dimensions_take_the_viewport_layer_colour() {
let mut d = Drawing::new_imperial();
layer(&mut d, "A", 1, Lineweight::ByLayer);
let dim = Dimension {
kind: DimKind::Linear { rotation: 0.0 },
defpt: Vec3::new(0.0, 2.0, 0.0),
text_mid: Vec3::ZERO,
p13: Vec3::ZERO,
p14: Vec3::new(10.0, 0.0, 0.0),
p15: Vec3::ZERO,
p16: Vec3::ZERO,
text: String::new(),
style: "Standard".into(),
measurement: 0.0,
text_rotation: 0.0,
user_text_pos: false,
block: None,
overrides: Default::default(),
assoc: Vec::new(),
marks: Default::default(),
};
d.add(&Space::Model, on("A"), EntityKind::Dimension(dim)).unwrap();
d.add(&Space::Model, on("A"), EntityKind::Line(Line { a: Vec3::new(0.0, -1.0, 0.0), b: Vec3::new(10.0, -1.0, 0.0) })).unwrap();
let vp = Viewport {
center: Vec3::new(10.0, 10.0, 0.0),
width: 20.0,
height: 10.0,
view_center: Vec2::new(5.0, 1.0),
view_height: 10.0,
id: 2,
locked: false,
frozen_layers: Vec::new(),
layer_colors: vec![("a".into(), Color::Index(3))],
clip: None,
on: true,
};
d.add(&Space::Paper("Layout1".into()), Common::default(), EntityKind::Viewport(vp)).unwrap();
let l = build(&d, &Space::Paper("Layout1".into()), &Options::default());
let seen: Vec<Rgb> = l.prims.iter().filter(|p| p.handle == VIEWPORT_CONTENT).map(|p| p.color).collect();
assert!(seen.iter().filter(|c| **c == rgb(3)).count() > 3, "dimension lines, arrows and text: {seen:?}");
assert!(seen.iter().all(|c| *c == rgb(3)), "everything on layer A is green in the viewport: {seen:?}");
// Model space keeps the layer's own colour.
let m = build(&d, &Space::Model, &Options::default());
assert!(m.prims.iter().all(|p| p.color == rgb(1)));
}
/// Length of the leader's arrowhead along the leader (x).
fn arrow_length(d: &Drawing) -> f64 {
let l = build(d, &Space::Model, &Options::default());
let tris: Vec<Vec2> = l.prims.iter().filter(|p| p.kind == Kind::Tris).flat_map(|p| l.points(p).to_vec()).collect();
Bounds2::from_points(tris).width()
}
#[test]
fn leader_arrows_use_their_style_dimscale() {
let mut d = Drawing::new_imperial();
d.dim_styles.push(DimStyle { name: "BIG".into(), scale: 10.0, arrow_size: 0.18, ..DimStyle::default() });
let leader = Leader { vertices: vec![Vec3::ZERO, Vec3::new(5.0, 0.0, 0.0)], arrow: true, spline: false, style: "BIG".into() };
d.add(&Space::Model, Common::default(), EntityKind::Leader(leader)).unwrap();
d.header.set_f64("DIMSCALE", 1.0);
assert!((arrow_length(&d) - 1.8).abs() < 1e-6, "{}", arrow_length(&d));
// A style DIMSCALE of 0 falls back to the drawing's.
d.dim_styles.iter_mut().filter(|s| s.name == "BIG").for_each(|s| s.scale = 0.0);
d.header.set_f64("DIMSCALE", 2.0);
assert!((arrow_length(&d) - 0.36).abs() < 1e-6, "{}", arrow_length(&d));
// Both 0: never a zero-size arrow.
d.header.set_f64("DIMSCALE", 0.0);
assert!((arrow_length(&d) - 0.18).abs() < 1e-6, "{}", arrow_length(&d));
}
fn text(x: f64, value: &str) -> Text {
Text {
insert: Vec3::new(x, 0.0, 0.0),
align_pt: None,
height: 1.0,
value: value.into(),
rotation: 0.0,
width_factor: 1.0,
oblique: 0.0,
style: "Standard".into(),
halign: HAlign::Left,
valign: VAlign::Baseline,
}
}
fn attdef(handle: u64, tag: &str, x: f64, common: Common) -> Entity {
let a = Attrib {
tag: tag.into(),
text: text(x, tag),
invisible: false,
constant: false,
prompt: String::new(),
props: Default::default(),
multiline: None,
modes: Default::default(),
};
Entity { common, ..Entity::new(Handle(handle), EntityKind::AttDef(a)) }
}
/// Colour and lineweight of the attribute text drawn between x0 and x0 + 5 (none = not drawn).
fn attrib_at(d: &Drawing, x0: f64) -> Option<(Rgb, f32)> {
let l = build(d, &Space::Model, &Options { lineweights: true, ..Options::default() });
let mut it = l.prims.iter().filter(|p| l.points(p).iter().all(|q| q.x >= x0 - 0.5 && q.x < x0 + 5.0)).map(|p| (p.color, p.lw));
let first = it.next()?;
assert!(it.all(|x| x == first), "one colour and lineweight per attribute");
Some(first)
}
#[test]
fn attributes_draw_with_their_own_properties() {
let mut d = Drawing::new_imperial();
layer(&mut d, "TXT", 2, Lineweight::Mm100(50));
layer(&mut d, "INS", 5, Lineweight::Mm100(18));
let mut blk = Block::new("TAGS");
// ByLayer on its own layer; ByBlock; ByLayer on layer 0 (= the reference's layer); explicit.
blk.entities.push(attdef(0x10, "BYLAYER", 0.0, on("TXT")));
blk.entities.push(attdef(0x11, "BYBLOCK", 10.0, Common { color: Color::ByBlock, lineweight: Lineweight::ByBlock, ..on("0") }));
blk.entities.push(attdef(0x12, "LAYER0", 20.0, on("0")));
blk.entities.push(attdef(0x13, "OWN", 30.0, Common { color: Color::Index(6), lineweight: Lineweight::Mm100(70), ..on("TXT") }));
d.blocks.insert(blk.name.clone(), Arc::new(blk));
let attribs = [("bylayer", 0.0), ("ByBlock", 10.0), ("LAYER0", 20.0), ("OWN", 30.0), ("NODEF", 40.0)]
.map(|(tag, x)| Attrib {
tag: tag.into(),
text: text(x, "X"),
invisible: false,
constant: false,
prompt: String::new(),
props: Default::default(),
multiline: None,
modes: Default::default(),
})
.to_vec();
let ins = Insert {
block: "TAGS".into(),
insert: Vec3::ZERO,
scale: Vec3::new(1.0, 1.0, 1.0),
rotation: 0.0,
attribs,
cols: 1,
rows: 1,
col_spacing: 0.0,
row_spacing: 0.0,
};
let common = Common { color: Color::Index(1), lineweight: Lineweight::Mm100(35), ..on("INS") };
d.add(&Space::Model, common, EntityKind::Insert(ins)).unwrap();
assert_eq!(attrib_at(&d, 0.0), Some((rgb(2), 0.5)), "ByLayer: the attribute's layer");
assert_eq!(attrib_at(&d, 10.0), Some((rgb(1), 0.35)), "ByBlock: the block reference's");
assert_eq!(attrib_at(&d, 20.0), Some((rgb(5), 0.18)), "layer 0: the block reference's layer");
assert_eq!(attrib_at(&d, 30.0), Some((rgb(6), 0.7)), "explicit");
assert_eq!(attrib_at(&d, 40.0), Some((rgb(1), 0.35)), "no definition: the block reference's");
// An attribute on a layer that is off is hidden; the others stay.
d.layer_mut("TXT").unwrap().on = false;
assert_eq!(attrib_at(&d, 0.0), None);
assert_eq!(attrib_at(&d, 10.0), Some((rgb(1), 0.35)));
}