1use three_d::{Srgba, Mat4, vec2, Vec2, vec3, vec4, SquareMatrix, Transform};
4use fontdue::{
5 Font,
6 layout::{GlyphPosition, Layout, LayoutSettings, TextStyle}
7};
8
9
10use crate::{TextAlign, TextPosition};
13use crate::geometry::mesh::TextMeshData;
14
15
16pub struct TextRef<'a> {
20 pub text: &'a str,
22 pub size: f32,
24 pub raster_size: Option<f32>,
26 pub line_height: Option<f32>,
28 pub padding: Vec2,
30 pub color: Srgba,
32 pub shadow: Option<(Srgba, Vec2)>,
34 pub character_color: Option<&'a dyn Fn(usize, usize, char) -> Srgba>,
36 pub align: TextAlign,
38 pub position: TextPosition,
40 pub transform: Mat4,
42 pub character_transform: Option<&'a dyn Fn(usize, usize, char) -> (Mat4, Mat4)>
44}
45
46impl<'a> Default for TextRef<'a> {
47 fn default() -> Self {
48 Self {
49 text: "Text",
50 size: 32.0,
51 raster_size: None,
52 line_height: None,
53 padding: vec2(0.0, 0.0),
54 color: Srgba::WHITE,
55 character_color: None,
56 align: TextAlign::default(),
57 shadow: None,
58 position: TextPosition::Pixels(vec2(0.0, 0.0)),
59 transform: Mat4::identity(),
60 character_transform: None
61 }
62 }
63}
64
65impl<'a> TextRef<'a> {
66 pub fn new(text: &'a str) -> Self {
68 Self {
69 text,
70 ..Default::default()
71 }
72 }
73
74 pub fn size(mut self, size: f32) -> Self {
76 self.size = size;
77 self
78 }
79
80 pub fn raster_size(mut self, raster_size: f32) -> Self {
82 self.raster_size = Some(raster_size);
83 self
84 }
85
86 pub fn line_height(mut self, line_height: f32) -> Self {
88 self.line_height = Some(line_height);
89 self
90 }
91
92 pub fn padding(mut self, padding: Vec2) -> Self {
94 self.padding = padding;
95 self
96 }
97
98 pub fn color(mut self, color: Srgba) -> Self {
100 self.color = color;
101 self
102 }
103
104 pub fn shadow(mut self, color: Srgba, offset: Vec2) -> Self {
106 self.shadow = Some((color, offset));
107 self
108 }
109
110 pub fn character_color(mut self, color: &'a dyn Fn(usize, usize, char) -> Srgba) -> Self {
114 self.character_color = Some(color);
115 self
116 }
117
118 pub fn align(mut self, align: TextAlign) -> Self {
120 self.align = align;
121 self
122 }
123
124 pub fn position(mut self, position: TextPosition) -> Self {
126 self.position = position;
127 self
128 }
129
130 pub fn transform(mut self, transform: Mat4) -> Self {
132 self.transform = transform;
133 self
134 }
135
136 pub fn character_transform(mut self, transform: &'a dyn Fn(usize, usize, char) -> (Mat4, Mat4)) -> Self {
138 self.character_transform = Some(transform);
139 self
140 }
141}
142
143impl<'a> TextRef<'a> {
144 pub(crate) fn glyph_raster_size(&self) -> f32 {
145 self.raster_size.unwrap_or(self.size)
146 }
147
148 pub(crate) fn walk_glyphs<
149 T,
150 C: FnMut(
151 usize,
152 usize,
153 &GlyphPosition,
154 Option<&'a dyn Fn(usize, usize, char) -> (Mat4, Mat4)>,
155 Option<&'a dyn Fn(usize, usize, char) -> Srgba>
156
157 ) -> Option<T>
158
159 >(&self, font: &Font, layout: &mut Layout, line_height: f32, mut callback: C) -> (Vec<T>, Vec2) {
160 layout.reset(&LayoutSettings {
161 line_height: self.line_height.unwrap_or(line_height),
162 ..Default::default()
163 });
164 layout.append(&[font], &TextStyle::new(self.text, self.size, 0));
165
166 layout.append(&[font], &TextStyle::new(" ", self.size, 0));
174
175 let count = layout.glyphs().len();
176 let mut results = Vec::with_capacity(count);
177 let mut bounds = vec2(0.0f32, 0.0);
178 for (index, glyph) in layout.glyphs().iter().enumerate() {
179 if let Some(result) = callback(index, count, glyph, self.character_transform, self.character_color) {
180 results.push(result);
181 }
182 bounds.x = bounds.x.max(glyph.x + glyph.width as f32);
183 bounds.y = bounds.y.max(glyph.y.abs());
184 }
185 (results, bounds + self.padding * 2.0)
186 }
187
188 pub(crate) fn extend_mesh(
189 &self,
190 mesh_data: &mut TextMeshData,
191 viewport: Vec2,
192 global_offset: Vec2,
193 local_offset: Vec2,
194 dimensions: Vec2
195 ) {
196 let index = mesh_data.positions.len() as u32;
198 mesh_data.indices.extend_from_slice(&[index, index + 1, index + 2, index + 2, index + 3, index]);
199
200 let color = vec4(0.0, 0.0, 0.0, 0.0);
201 mesh_data.colors.extend_from_slice(&[color, color, color, color]);
202 mesh_data.quad_uvs.extend_from_slice(&[vec2(1.0, 1.0), vec2(1.0, 0.0), vec2(0.0, 0.0), vec2(0.0, 1.0)]);
203 mesh_data.glyph_uvs.extend_from_slice(&[vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(0.0, 0.0)]);
204
205 let width = dimensions.x - self.padding.x * 2.0;
207 let height = dimensions.y - self.padding.y * 2.0;
208 let offset = vec3(self.padding.x, -height - self.padding.y, 0.0);
209
210 let tl = offset + vec3(0.0, 0.0, 0.0);
212 let br = offset + vec3(width, height, 0.0);
213 let bl = offset + vec3(0.0, height, 0.0);
214 let tr = offset + vec3(width, 0.0, 0.0);
215
216 let center = vec3(local_offset.x.round(), local_offset.y.round(), 0.0);
218
219 let g = self.position.to_pixels(viewport) + global_offset;
221 let translation = vec3(g.x.round(), g.y.round(), 0.0);
222 mesh_data.positions.extend_from_slice(&[
223 self.transform.transform_vector(center + br) + translation,
224 self.transform.transform_vector(center + tr) + translation,
225 self.transform.transform_vector(center + tl) + translation,
226 self.transform.transform_vector(center + bl) + translation
227 ]);
228 }
229}
230