Skip to main content

esoc_scene/
style.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2//! Visual styles for marks: stroke, fill, font, marker shapes.
3
4use esoc_color::Color;
5
6/// Line stroke style.
7#[derive(Clone, Debug)]
8pub struct StrokeStyle {
9    /// Stroke color.
10    pub color: Color,
11    /// Line width in pixels.
12    pub width: f32,
13    /// Dash pattern (lengths of dash, gap, dash, gap, ...).
14    pub dash: Vec<f32>,
15    /// Dash offset.
16    pub dash_offset: f32,
17    /// Line cap style.
18    pub line_cap: LineCap,
19    /// Line join style.
20    pub line_join: LineJoin,
21}
22
23impl Default for StrokeStyle {
24    fn default() -> Self {
25        Self {
26            color: Color::BLACK,
27            width: 1.0,
28            dash: Vec::new(),
29            dash_offset: 0.0,
30            line_cap: LineCap::Butt,
31            line_join: LineJoin::Miter,
32        }
33    }
34}
35
36impl StrokeStyle {
37    /// Create a solid stroke.
38    pub fn solid(color: Color, width: f32) -> Self {
39        Self {
40            color,
41            width,
42            ..Default::default()
43        }
44    }
45
46    /// Whether this stroke is invisible.
47    pub fn is_none(&self) -> bool {
48        self.width <= 0.0 || self.color.a <= 0.0
49    }
50}
51
52/// Line cap style.
53#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
54pub enum LineCap {
55    /// Flat end at the endpoint.
56    #[default]
57    Butt,
58    /// Rounded end.
59    Round,
60    /// Square end extending past the endpoint.
61    Square,
62}
63
64/// Line join style.
65#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
66pub enum LineJoin {
67    /// Sharp corner.
68    #[default]
69    Miter,
70    /// Rounded corner.
71    Round,
72    /// Beveled corner.
73    Bevel,
74}
75
76/// Fill style.
77#[derive(Clone, Debug, Default)]
78pub enum FillStyle {
79    /// No fill.
80    #[default]
81    None,
82    /// Solid color fill.
83    Solid(Color),
84}
85
86impl FillStyle {
87    /// Get the color if solid.
88    pub fn color(&self) -> Option<Color> {
89        match self {
90            Self::None => None,
91            Self::Solid(c) => Some(*c),
92        }
93    }
94
95    /// Whether this is no fill.
96    pub fn is_none(&self) -> bool {
97        matches!(self, Self::None)
98    }
99}
100
101impl From<Color> for FillStyle {
102    fn from(c: Color) -> Self {
103        Self::Solid(c)
104    }
105}
106
107/// Font style for text marks.
108#[derive(Clone, Debug)]
109pub struct FontStyle {
110    /// Font family name.
111    pub family: String,
112    /// Font size in pixels.
113    pub size: f32,
114    /// Font weight (400 = normal, 700 = bold).
115    pub weight: u16,
116    /// Whether to use italic style.
117    pub italic: bool,
118}
119
120impl Default for FontStyle {
121    fn default() -> Self {
122        Self {
123            family: "sans-serif".to_string(),
124            size: 12.0,
125            weight: 400,
126            italic: false,
127        }
128    }
129}
130
131impl FontStyle {
132    /// Bold variant.
133    pub fn bold(mut self) -> Self {
134        self.weight = 700;
135        self
136    }
137
138    /// Set size.
139    pub fn with_size(mut self, size: f32) -> Self {
140        self.size = size;
141        self
142    }
143}
144
145/// Marker shapes for point marks.
146#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
147pub enum MarkerShape {
148    /// Circle marker.
149    #[default]
150    Circle,
151    /// Square marker.
152    Square,
153    /// Diamond (rotated square).
154    Diamond,
155    /// Upward triangle.
156    TriangleUp,
157    /// Downward triangle.
158    TriangleDown,
159    /// Cross (+).
160    Cross,
161    /// Star.
162    Star,
163    /// Plus (same as cross but thinner).
164    Plus,
165}
166
167impl MarkerShape {
168    /// Shape type index for GPU shader dispatch.
169    pub fn type_index(self) -> u32 {
170        match self {
171            Self::Circle => 0,
172            Self::Square => 1,
173            Self::Diamond => 2,
174            Self::TriangleUp => 3,
175            Self::TriangleDown => 4,
176            Self::Cross => 5,
177            Self::Star => 6,
178            Self::Plus => 7,
179        }
180    }
181}