From: Ben Pfaff Date: Fri, 8 May 2026 15:17:57 +0000 (-0700) Subject: Give patterns solid backgrounds X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=af5a3050976d34d9d36c2f8107c4d8ede82a85ad;p=pspp Give patterns solid backgrounds --- diff --git a/rust/pspp/build.rs b/rust/pspp/build.rs index 964684f4c8..545489cc2b 100644 --- a/rust/pspp/build.rs +++ b/rust/pspp/build.rs @@ -687,12 +687,14 @@ fn build_patterns() -> AnyResult<()> { .filter(|(key, _value)| key != "gifindex" && key != "fill") .collect::>(); let id = attributes.remove("id").unwrap(); + let height = attributes.remove("height").unwrap().parse::().unwrap(); + let width = attributes.remove("width").unwrap().parse::().unwrap(); let index = id .strip_prefix("pattern") .unwrap() .parse::() .unwrap(); - patterns[index] = Some((attributes, content)); + patterns[index] = Some((width, height, attributes, content)); //writeln!(&mut output, "{index} {attributes:?} {content:?}").unwrap(); } } @@ -714,10 +716,12 @@ fn build_patterns() -> AnyResult<()> { pub static PATTERNS: [Pattern; 30] = [" ) .unwrap(); - for (attributes, content) in &patterns { + for (width, height, attributes, content) in &patterns { writeln!( &mut output, " Pattern {{ + width: {width:?}, + height: {height:?}, attributes: &[" ) .unwrap(); diff --git a/rust/pspp/src/output/pivot/look.rs b/rust/pspp/src/output/pivot/look.rs index 80863fafc1..c41656f273 100644 --- a/rust/pspp/src/output/pivot/look.rs +++ b/rust/pspp/src/output/pivot/look.rs @@ -882,7 +882,7 @@ impl FontStyle { } /// Color used in [FontStyle]. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Color { /// Alpha channel. /// @@ -943,6 +943,11 @@ impl Color { ColorDisplayCss(*self) } + /// Displays opaque colors as `rrggbb` and others as `rrggbbaa`. + pub fn display_hex(&self) -> impl Display { + ColorDisplayHex(*self) + } + /// Returns the red, green, and blue channels of this color. pub fn into_rgb(&self) -> (u8, u8, u8) { (self.r, self.g, self.b) @@ -1036,7 +1041,7 @@ struct ColorDisplayCss(Color); impl Display for ColorDisplayCss { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let Color { alpha, r, g, b } = self.0; + let (r, g, b, alpha) = self.0.into_rgba(); match alpha { 255 => write!(f, "#{r:02x}{g:02x}{b:02x}"), _ => write!(f, "rgb({r}, {g}, {b}, {:.2})", alpha as f64 / 255.0), @@ -1044,6 +1049,22 @@ impl Display for ColorDisplayCss { } } +/// A structure for formatting a [Color] as hex digits. +/// +/// See [Color::display_hex]. +struct ColorDisplayHex(Color); + +impl Display for ColorDisplayHex { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let (r, g, b, a) = self.0.into_rgba(); + write!(f, "{r:02x}{g:02x}{b:02x}")?; + if a != 255 { + write!(f, "{a:02x}")?; + } + Ok(()) + } +} + /// Style for drawing a border in a pivot table. #[derive(Copy, Clone, Debug, PartialEq, Deserialize)] pub struct BorderStyle { diff --git a/rust/pspp/src/spv/read/graph.rs b/rust/pspp/src/spv/read/graph.rs index c3ed50f7a6..2ed7ea3aa2 100644 --- a/rust/pspp/src/spv/read/graph.rs +++ b/rust/pspp/src/spv/read/graph.rs @@ -64,7 +64,7 @@ use crate::{ read::graph::{ format::Formatter, frames::{Component, Frames, Position}, - patterns::Patterns, + patterns::{ColoredPattern, Patterns}, style::{Style, StyleCycle}, }, }, @@ -917,7 +917,11 @@ impl Visualization { let color = style.color.unwrap_or(Color::BLACK); let color2 = style.color2.unwrap_or(Color::BLACK); - let pattern = patterns.insert(style.pattern.unwrap_or_default(), color); + let pattern = patterns.insert(ColoredPattern { + index: style.pattern.unwrap_or_default(), + color, + color2: style.color2.unwrap_or(Color::WHITE), + }); fn points( rect: &Rect, diff --git a/rust/pspp/src/spv/read/graph/patterns.rs b/rust/pspp/src/spv/read/graph/patterns.rs index 72590084f0..7c66006b8d 100644 --- a/rust/pspp/src/spv/read/graph/patterns.rs +++ b/rust/pspp/src/spv/read/graph/patterns.rs @@ -1,15 +1,17 @@ -use std::{ - collections::{BTreeMap, BTreeSet}, - fmt::Display, -}; +use std::{collections::BTreeSet, fmt::Display}; -use svg::{Node, node::Blob}; +use svg::{ + Node, + node::{Blob, element::Rectangle}, +}; use crate::output::pivot::look::Color; mod data; pub struct Pattern { + width: f64, + height: f64, attributes: &'static [(&'static str, &'static str)], content: &'static str, } @@ -24,8 +26,20 @@ impl Pattern { data::PATTERNS.get(index) } - pub fn to_svg(&self) -> svg::node::element::Pattern { - let mut group = svg::node::element::Pattern::new().add(Blob::new(self.content)); + pub fn to_svg(&self, color: Color, color2: Color) -> svg::node::element::Pattern { + let mut group = svg::node::element::Pattern::new() + .set("fill", color.display_css().to_string()) + .set("width", self.width) + .set("height", self.height) + .add( + Rectangle::new() + .set("x", 0) + .set("y", 0) + .set("width", self.width) + .set("height", self.height) + .set("fill", color2.display_css().to_string()), + ) + .add(Blob::new(self.content)); for (name, value) in self.attributes.into_iter().copied() { group.assign(name, value); } @@ -33,76 +47,73 @@ impl Pattern { } } -#[derive(Clone, Debug, Default)] -pub struct Patterns { - instances: BTreeMap>, +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct ColoredPattern { + pub index: usize, + pub color: Color, + pub color2: Color, } -struct PatternName { - index: usize, - color: Color, -} -impl Display for PatternName { +impl Display for ColoredPattern { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let (r, g, b, a) = self.color.into_rgba(); - write!(f, "pattern{}_{r:02x}{g:02x}{b:02x}", self.index)?; - if a != 255 { - write!(f, "_{a:02x}")?; - } + write!( + f, + "pattern{}_{}_{}", + self.index, + self.color.display_hex(), + self.color2.display_hex() + )?; Ok(()) } } +#[derive(Clone, Debug, Default)] +pub struct Patterns { + instances: BTreeSet, +} + impl Patterns { pub fn new() -> Self { Self::default() } - pub fn insert(&mut self, index: usize, color: Color) -> String { - if index != 0 && Pattern::for_index(index).is_some() { - self.instances.entry(index).or_default().insert(color); - format!("url(#{})", PatternName { index, color }) + pub fn insert(&mut self, p: ColoredPattern) -> String { + if p.index != 0 && Pattern::for_index(p.index).is_some() { + self.instances.insert(p); + format!("url(#{p})") } else { - color.display_css().to_string() + p.color.display_css().to_string() } } pub fn to_svg(&self) -> impl Iterator { - self.instances.iter().flat_map(|(index, colors)| { - colors.iter().map(move |color| { - Pattern::for_index(*index) - .unwrap() - .to_svg() - .set( - "id", - PatternName { - index: *index, - color: *color, - } - .to_string(), - ) - .set("fill", color.display_css().to_string()) - }) + self.instances.iter().map(|cp| { + Pattern::for_index(cp.index) + .unwrap() + .to_svg(cp.color, cp.color2) + .set("id", cp.to_string()) }) } } #[cfg(test)] mod tests { - use crate::spv::read::graph::patterns::Pattern; + use crate::{output::pivot::look::Color, spv::read::graph::patterns::Pattern}; #[test] fn pattern() { let pattern = Pattern::for_index(1).unwrap(); - let svg = pattern.to_svg().to_string(); - + let svg = pattern.to_svg(Color::BLACK, Color::WHITE).to_string(); + println!("{svg}"); assert_eq!( svg, - r#" + r##" + + - "#, - "This assertion will need to be updated if pattern 1 is changed in patterns.svg." + +"## ); } } diff --git a/rust/pspp/src/spv/testdata/graph3.expected.svg b/rust/pspp/src/spv/testdata/graph3.expected.svg index 90c070141b..a64b0ace26 100644 --- a/rust/pspp/src/spv/testdata/graph3.expected.svg +++ b/rust/pspp/src/spv/testdata/graph3.expected.svg @@ -57,12 +57,22 @@ Percent Percent - - + + - - + + + + + + + + + + + + diff --git a/rust/pspp/src/spv/testdata/graph3.xml b/rust/pspp/src/spv/testdata/graph3.xml index ccbb18be4f..52513b9af6 100644 --- a/rust/pspp/src/spv/testdata/graph3.xml +++ b/rust/pspp/src/spv/testdata/graph3.xml @@ -74,7 +74,7 @@