mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-24 19:06:56 -06:00
A PlotStyleTable model in doc with our own built-in tables (default.ctb keeps object properties, monochrome.ctb prints black, grayscale.ctb prints the grey of each colour's luminance). The render builder resolves each object's style by its index colour (true colours by the nearest index) and applies colour, screening, linetype and lineweight. PLOT/EXPORTPDF use the page setup's table (or the plotStyleTable option); layouts with Display plot styles (PAGESETUP displayPlotStyles, DXF plot flag 2) show it on screen and in PNG/SVG exports. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
118 lines
5.4 KiB
Rust
118 lines
5.4 KiB
Rust
//! Plot style tables applied to the display list (issue #410): colour-dependent tables map an
|
|
//! object's colour to its printed colour, screening, linetype and lineweight; layouts show them
|
|
//! when "Display plot styles" is on.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use cadcraft_color::{Color, Rgb};
|
|
use cadcraft_doc::{Common, Drawing, EntityKind, Line, Lineweight, PlotStyle, PlotStyleKind, PlotStyleTable, Space};
|
|
use cadcraft_geom::Vec3;
|
|
use cadcraft_render::{DisplayList, Options, build, build_plot};
|
|
|
|
const BLACK: Rgb = Rgb(0, 0, 0);
|
|
|
|
fn line(y: f64) -> EntityKind {
|
|
EntityKind::Line(Line { a: Vec3::new(0.0, y, 0.0), b: Vec3::new(10.0, y, 0.0) })
|
|
}
|
|
|
|
/// Lines in red (1), green (3), colour 7 (ByLayer on layer 0) and a true orange, in `space`.
|
|
fn drawing(space: &Space) -> Drawing {
|
|
let mut d = Drawing::new_metric();
|
|
let c = |color| Common { color, ..Default::default() };
|
|
d.add(space, c(Color::Index(1)), line(0.0)).unwrap();
|
|
d.add(space, c(Color::Index(3)), line(1.0)).unwrap();
|
|
d.add(space, Common::default(), line(2.0)).unwrap();
|
|
d.add(space, c(Color::True(Rgb(255, 128, 0))), line(3.0)).unwrap();
|
|
d
|
|
}
|
|
|
|
fn with_table(t: PlotStyleTable) -> Options {
|
|
Options { plot_style_table: Some(Arc::new(t)), lineweights: true, ..Default::default() }
|
|
}
|
|
|
|
fn colours(l: &DisplayList) -> Vec<Rgb> {
|
|
l.prims.iter().map(|p| p.display_rgb(Rgb(255, 255, 255))).collect()
|
|
}
|
|
|
|
#[test]
|
|
fn monochrome_prints_every_colour_black() {
|
|
let d = drawing(&Space::Model);
|
|
let l = build_plot(&d, &Space::Model, &with_table(PlotStyleTable::builtin("monochrome.ctb").unwrap()));
|
|
assert_eq!(l.prims.len(), 4);
|
|
assert!(l.prims.iter().all(|p| p.color == BLACK && !p.aci7), "{:?}", l.prims);
|
|
// Without a table the colours are the objects'.
|
|
let plain = build_plot(&d, &Space::Model, &Options::default());
|
|
assert_eq!(colours(&plain), [Rgb(255, 0, 0), Rgb(0, 255, 0), BLACK, Rgb(255, 128, 0)]);
|
|
}
|
|
|
|
#[test]
|
|
fn grayscale_and_default_tables() {
|
|
let d = drawing(&Space::Model);
|
|
let l = build_plot(&d, &Space::Model, &with_table(PlotStyleTable::builtin("grayscale.ctb").unwrap()));
|
|
for c in colours(&l) {
|
|
assert!(c.0 == c.1 && c.1 == c.2, "grey: {c:?}");
|
|
}
|
|
let c = colours(&l);
|
|
assert!(c[1].0 > c[0].0, "green is lighter than red");
|
|
assert_eq!(c[2], BLACK, "colour 7 prints black");
|
|
let keep = build_plot(&d, &Space::Model, &with_table(PlotStyleTable::builtin("default.ctb").unwrap()));
|
|
assert_eq!(colours(&keep), colours(&build_plot(&d, &Space::Model, &Options::default())));
|
|
}
|
|
|
|
#[test]
|
|
fn screening_lineweight_and_linetype_overrides() {
|
|
let mut d = Drawing::new_metric();
|
|
d.header.set_f64("LTSCALE", 0.05);
|
|
d.add(&Space::Model, Common { color: Color::Index(1), lineweight: Lineweight::Mm100(13), ..Default::default() }, line(0.0)).unwrap();
|
|
d.add(&Space::Model, Common { color: Color::Index(5), ..Default::default() }, line(1.0)).unwrap();
|
|
let table = PlotStyleTable::color_dependent("pens.ctb", "", |i| {
|
|
(i == 1).then(|| PlotStyle {
|
|
color: Some(BLACK),
|
|
screening: 50,
|
|
lineweight: Some(70),
|
|
linetype: Some("DASHED".into()),
|
|
..PlotStyle::default()
|
|
})
|
|
});
|
|
let l = build_plot(&d, &Space::Model, &with_table(table.clone()));
|
|
let red: Vec<_> = l.prims.iter().filter(|p| p.color == Rgb(128, 128, 128)).collect();
|
|
assert!(red.len() > 1, "dashed into several pieces: {}", red.len());
|
|
assert!(red.iter().all(|p| p.lw == 0.7), "pen lineweight replaces 0.13 mm");
|
|
let blue: Vec<_> = l.prims.iter().filter(|p| p.color == Rgb(0, 0, 255)).collect();
|
|
assert_eq!(blue.len(), 1, "colour 5 keeps its own style");
|
|
assert_eq!(blue[0].lw, 0.25, "object lineweight (LWDEFAULT)");
|
|
// With lineweights off nothing gets a width.
|
|
let thin = build_plot(&d, &Space::Model, &Options { lineweights: false, ..with_table(table) });
|
|
assert!(thin.prims.iter().all(|p| p.lw == 0.0));
|
|
}
|
|
|
|
#[test]
|
|
fn layout_display_plot_styles_flag() {
|
|
let space = Space::Paper("Layout1".into());
|
|
let mut d = drawing(&space);
|
|
d.layouts[0].page.plot_style_table = "monochrome.ctb".into();
|
|
let red = |l: &DisplayList| l.prims.iter().any(|p| p.color == Rgb(255, 0, 0));
|
|
assert!(red(&build(&d, &space, &Options::default())), "flag off: object colours on screen");
|
|
d.layouts[0].page.show_plot_styles = true;
|
|
assert!(!red(&build(&d, &space, &Options::default())), "flag on: monochrome on screen");
|
|
// Plotting takes its table from the options (the plotter passes the page's).
|
|
assert!(red(&build_plot(&d, &space, &Options::default())));
|
|
// Model space and unknown tables draw object colours.
|
|
d.layouts[0].page.plot_style_table = "unknown.ctb".into();
|
|
assert!(red(&build(&d, &space, &Options::default())));
|
|
}
|
|
|
|
#[test]
|
|
fn hostile_tables_do_not_panic() {
|
|
let d = drawing(&Space::Model);
|
|
for styles in [Vec::new(), vec![PlotStyle { screening: 255, lineweight: Some(u16::MAX), linetype: Some("NOPE".into()), ..PlotStyle::default() }]]
|
|
{
|
|
let t = PlotStyleTable { name: "x.ctb".into(), styles, ..PlotStyleTable::default() };
|
|
let l = build_plot(&d, &Space::Model, &with_table(t));
|
|
assert_eq!(l.prims.len(), 4);
|
|
assert!(l.prims.iter().all(|p| p.lw <= 2.11));
|
|
}
|
|
let named = PlotStyleTable { kind: PlotStyleKind::Named, styles: vec![PlotStyle::object("Normal")], ..PlotStyleTable::default() };
|
|
assert_eq!(build_plot(&d, &Space::Model, &with_table(named)).prims.len(), 4);
|
|
}
|