1pub const CHAR_W: f32 = 6.0;
5pub const LINE_H: f32 = 10.0;
7pub const LINE_GAP: f32 = 2.0;
9
10pub fn wrap_text(text: &str, max_w: f32, font_scale: f32) -> Vec<String> {
13 let char_w = CHAR_W * font_scale;
14 if char_w <= 0.0 || max_w <= 0.0 {
15 return vec![text.to_owned()];
16 }
17 let max_chars = ((max_w / char_w).floor() as usize).max(1);
18 let mut lines: Vec<String> = Vec::new();
19 let mut cur = String::new();
20
21 for word in text.split(' ') {
22 if word.is_empty() { continue; }
23 if cur.is_empty() {
24 let mut w = word;
26 while w.len() > max_chars {
27 lines.push(w[..max_chars].to_owned());
28 w = &w[max_chars..];
29 }
30 cur.push_str(w);
31 } else if cur.len() + 1 + word.len() <= max_chars {
32 cur.push(' ');
33 cur.push_str(word);
34 } else {
35 lines.push(std::mem::take(&mut cur));
36 let mut w = word;
37 while w.len() > max_chars {
38 lines.push(w[..max_chars].to_owned());
39 w = &w[max_chars..];
40 }
41 cur.push_str(w);
42 }
43 }
44 if !cur.is_empty() || lines.is_empty() {
45 lines.push(cur);
46 }
47 lines
48}
49
50pub fn line_count(text: &str, max_w: f32, font_scale: f32) -> usize {
52 wrap_text(text, max_w, font_scale).len().max(1)
53}
54
55pub fn text_height(text: &str, max_w: f32, font_scale: f32) -> f32 {
57 let n = line_count(text, max_w, font_scale);
58 n as f32 * LINE_H * font_scale + (n.saturating_sub(1)) as f32 * LINE_GAP
59}