mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-25 03:16:50 -06:00
Squash of PR #486 by @vangel76. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
87 lines
3.4 KiB
Rust
87 lines
3.4 KiB
Rust
//! Annotative objects seen through layout viewports (#468): they draw at their paper size in
|
||
//! every viewport whose scale they support, and not at all in others when ANNOALLVISIBLE is 0.
|
||
|
||
use cadcraft_doc::{AnnoScale, Common, Drawing, EntityKind, Space, Text, Viewport};
|
||
use cadcraft_geom::{Bounds2, Vec3};
|
||
use cadcraft_render::{Options, VIEWPORT_CONTENT, build};
|
||
|
||
/// A viewport on Layout1, 100 × 100 paper units, showing a model window `100 × scale`.
|
||
fn viewport(x: f64, scale: f64) -> Viewport {
|
||
serde_json::from_value(serde_json::json!({
|
||
"center": [x, 50.0, 0.0],
|
||
"width": 100.0,
|
||
"height": 100.0,
|
||
"viewCenter": [0.0, 0.0],
|
||
"viewHeight": 100.0 * scale,
|
||
"id": 2,
|
||
}))
|
||
.unwrap()
|
||
}
|
||
|
||
/// A metric drawing with annotative text 2.5 high on paper supporting 1:50 and 1:100, seen
|
||
/// through one viewport at `scale`.
|
||
fn drawing(scale: f64, all_visible: bool) -> Drawing {
|
||
let mut d = Drawing::new_metric();
|
||
d.header.set_i64("ANNOALLVISIBLE", i64::from(all_visible));
|
||
let anno = Common { anno_scales: vec![AnnoScale::new("1:50", 1.0, 50.0), AnnoScale::new("1:100", 1.0, 100.0)], ..Default::default() };
|
||
let text = Text {
|
||
insert: Vec3::ZERO,
|
||
align_pt: None,
|
||
height: 2.5,
|
||
value: "ROOM".into(),
|
||
rotation: 0.0,
|
||
width_factor: 1.0,
|
||
oblique: 0.0,
|
||
style: "Standard".into(),
|
||
halign: Default::default(),
|
||
valign: Default::default(),
|
||
};
|
||
d.add(&Space::Model, anno, EntityKind::Text(text)).unwrap();
|
||
d.add(&Space::Paper("Layout1".into()), Common::default(), EntityKind::Viewport(viewport(50.0, scale))).unwrap();
|
||
d
|
||
}
|
||
|
||
/// Bounds of the model-space content drawn in the viewport, in paper units.
|
||
fn content(d: &Drawing) -> Bounds2 {
|
||
let l = build(d, &Space::Paper("Layout1".into()), &Options::default());
|
||
let mut b = Bounds2::EMPTY;
|
||
for p in l.prims.iter().filter(|p| p.handle == VIEWPORT_CONTENT) {
|
||
for q in l.points(p) {
|
||
b.add(*q);
|
||
}
|
||
}
|
||
b
|
||
}
|
||
|
||
#[test]
|
||
fn same_paper_height_at_every_supported_scale() {
|
||
let at_50 = content(&drawing(50.0, false));
|
||
let at_100 = content(&drawing(100.0, false));
|
||
assert!(!at_50.is_empty() && !at_100.is_empty());
|
||
assert!((at_50.height() / at_100.height() - 1.0).abs() < 1e-9, "{} vs {}", at_50.height(), at_100.height());
|
||
assert!((at_50.width() / at_100.width() - 1.0).abs() < 1e-9);
|
||
// Glyphs span most of the 2.5 text height on paper.
|
||
assert!(at_50.height() > 1.5 && at_50.height() < 3.5, "{}", at_50.height());
|
||
}
|
||
|
||
#[test]
|
||
fn unsupported_viewport_scale_hides_or_shows_at_the_objects_scale() {
|
||
assert!(content(&drawing(20.0, false)).is_empty());
|
||
// ANNOALLVISIBLE 1: drawn at its first scale (1:50), so 50/20 = 2.5× its paper size here.
|
||
let shown = content(&drawing(20.0, true));
|
||
let at_50 = content(&drawing(50.0, false));
|
||
assert!((shown.height() / at_50.height() - 2.5).abs() < 1e-9, "{} vs {}", shown.height(), at_50.height());
|
||
}
|
||
|
||
#[test]
|
||
fn model_space_uses_cannoscale() {
|
||
let mut d = drawing(50.0, false);
|
||
let list = |d: &Drawing| build(d, &Space::Model, &Options::default()).prims.len();
|
||
// CANNOSCALE 1:1 isn't supported: hidden.
|
||
assert_eq!(list(&d), 0);
|
||
d.header.set_str("CANNOSCALE", "1:100");
|
||
assert!(list(&d) > 0);
|
||
let b = build(&d, &Space::Model, &Options::default()).bounds;
|
||
assert!(b.height() > 150.0 && b.height() < 350.0, "{}", b.height());
|
||
}
|