1use crate::diagnostics::LookupError;
2use crate::material::Color;
3
4use super::{LabelKey, NodeKey, NodeKind, Scene, Transform};
5
6#[derive(Debug, Clone, PartialEq)]
7pub struct LabelDesc {
8 text: String,
9 rasterization: LabelRasterization,
10 billboard: LabelBillboard,
11 color: Color,
12 size: f32,
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum LabelRasterization {
17 Sdf,
18 Msdf,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum LabelBillboard {
23 ScreenAligned,
24}
25
26impl Scene {
27 pub fn add_label(
28 &mut self,
29 parent: NodeKey,
30 label: LabelDesc,
31 transform: Transform,
32 ) -> Result<LabelKey, LookupError> {
33 let label_key = self.labels.insert(label);
34 if let Err(error) = self.insert_node(parent, NodeKind::Label(label_key), transform) {
35 self.labels.remove(label_key);
36 return Err(error);
37 }
38 Ok(label_key)
39 }
40
41 pub fn label(&self, label: LabelKey) -> Option<&LabelDesc> {
42 self.labels.get(label)
43 }
44
45 pub fn set_label_text(
46 &mut self,
47 label: LabelKey,
48 text: impl Into<String>,
49 ) -> Result<(), LookupError> {
50 let label = self
51 .labels
52 .get_mut(label)
53 .ok_or(LookupError::LabelNotFound(label))?;
54 let text = text.into();
55 if label.text != text {
56 label.text = text;
57 self.structure_revision = self.structure_revision.saturating_add(1);
58 }
59 Ok(())
60 }
61}
62
63impl LabelDesc {
64 pub fn sdf(text: impl Into<String>) -> Self {
65 Self::new(text, LabelRasterization::Sdf)
66 }
67
68 pub fn msdf(text: impl Into<String>) -> Self {
69 Self::new(text, LabelRasterization::Msdf)
70 }
71
72 pub fn text(&self) -> &str {
73 &self.text
74 }
75
76 pub const fn rasterization(&self) -> LabelRasterization {
77 self.rasterization
78 }
79
80 pub const fn billboard(&self) -> LabelBillboard {
81 self.billboard
82 }
83
84 pub const fn color(&self) -> Color {
85 self.color
86 }
87
88 pub const fn size(&self) -> f32 {
89 self.size
90 }
91
92 pub fn with_color(mut self, color: Color) -> Self {
93 self.color = color;
94 self
95 }
96
97 pub fn with_size(mut self, size: f32) -> Self {
98 self.size = positive_or(size, 1.0);
99 self
100 }
101
102 pub const fn with_billboard(mut self, billboard: LabelBillboard) -> Self {
103 self.billboard = billboard;
104 self
105 }
106
107 fn new(text: impl Into<String>, rasterization: LabelRasterization) -> Self {
108 Self {
109 text: text.into(),
110 rasterization,
111 billboard: LabelBillboard::ScreenAligned,
112 color: Color::WHITE,
113 size: 1.0,
114 }
115 }
116}
117
118fn positive_or(value: f32, fallback: f32) -> f32 {
119 if !value.is_finite() || value <= 0.0 {
120 fallback
121 } else {
122 value
123 }
124}