1     let get_length (s : String
2                     i : u32
3                     font : Font
4                     end_chars : Slice<Char>
5                     use_parentheses : bool
6                     allow_commands : bool) =
7         let mut x = 0
8         let mut index = i
9         let mut skipping = false
10        let mut maybe_prev_c = None
11        let mut parentheses = 0
12        while index < s.length do
13            let c = s[index]
14            if c == '[' && allow_commands then
15                assert not skipping
16                skipping = true
17    
18            else if c == ']' && allow_commands then
19                assert skipping
20                skipping = false
21    
22            else if not skipping then
23                if c == '(' then
24                    parentheses += 1
25    
26                else if c == ')' then
27                    parentheses -= 1
28                    if parentheses < 0 then
29                        parentheses = 0
30    
31                if end_chars.contains c && (parentheses == 0 || not use_parentheses) then
32                    break
33                else
34                    let offset = case maybe_prev_c of
35                        None -> 0
36                        Some prev_c -> font.offset prev_c c
37    
38                    let char_width =
39                        if c == ' '
40                        then font.space_width as i32
41                        else font.rectangles.try_get c
42                             |> or_else { font.rectangles.try_get Sys.unprintable_char }
43                             |> unwrap
44                             |> width
45                             |> as<i32>
46    
47                    x += offset + char_width
48                    maybe_prev_c = Some c
49    
50            index += 1
51    
52        x as u32
53    
54    let get_word_length (s : String
55                         i : u32
56                         font : Font
57                         use_parentheses : bool
58                         allow_commands : bool) =
59        get_length s i font [' ', '\n'] use_parentheses allow_commands
60    
61    let get_braced_length (s : String
62                           i : u32
63                           font : Font
64                           allow_commands : bool) =
65        get_length s i font ['}'] false allow_commands
66    
67    module string_mesh
68    
69    def create (text : String
70                font : Font
71                max_width : u32
72                is_rgba : bool
73                is_multicolor : bool
74                allow_commands : bool
75                get_color : String -> Vector3) =
76        let space_width = font.space_width
77        let rectangles = font.rectangles
78        let offsets = List.new
79        let char_height = rectangles['a'].height
80        let line_height = font.line_height as i32
81        let font_width = font.memory.width
82        let font_height = if is_rgba
83                          then font.memory_rgba.height
84                          else font.memory.height
85    
86        let return_index = text.try_index_of '\n'
87        let s = if return_index ? Some index
88                then text.take index
89                else text
90    
91        let margin = font.margin
92    
93        let quads = List.new
94        let mut x = margin.left as i32
95        let mut y = margin.up as i32
96        let mut unrestricted_x = x
97        let mut unrestricted_y = y
98        let mut width : u32 = 0
99        let mut unrestricted_width = width
100       let mut is_command = false
101       let command = StringBuilder.new
102       let mut color = StringMesh.default_color
103       let mut link_start : Option<(i32, i32)> = None
104       let links = obs/List.new
105       let mut maybe_prev_c : Option<Char> = None
106       let mut maybe_brace_x : Option<i32> = None
107       let mut brace_count = 0
108       let mut is_first_of_line = true
109       let mut first_x = None
110   
111       let add_last_offset () =
112           if maybe_prev_c ? Some prev_c then
113               let last_offset = font.offset prev_c ' '
114               x += last_offset
115               unrestricted_x += last_offset
116               if x as u32 > width then
117                   width = x as u32
118   
119               if unrestricted_x as u32 > unrestricted_width then
120                   unrestricted_width = unrestricted_x as u32
121   
122       for i = 0 until s.length do
123           let mut end_of_line = false
124           let c = s[i]
125           if c == '[' && allow_commands then
126               is_command = true
127           else if is_command && (c == ' ' || c == ']') then
128               if c == ']' then
129                   is_command = false
130   
131               if command.as_string == "link" then
132                   link_start = Some (x, y)
133   
134               else if command.as_string.starts_with "x_" then
135                   let (_, value) = command.as_string.split_at 2
136                   x += i32.parse value
137   
138               else if command.as_string.starts_with "y_" then
139                   let (_, value) = command.as_string.split_at 2
140                   y += i32.parse value
141               else
142                   color = get_color command.as_string
143                   if command.as_string == "end" && link_start.is_some then
144                       let (left, up) = link_start.unwrap
145                       let bounds = Bounds
146                           left = left as u32
147                           right = x as u32
148                           up = up as u32
149                           down = y + line_height |> as<u32>
150   
151                       links.add bounds
152                       link_start = None
153   
154               command.clear
155   
156           else if is_command then
157               command.append c
158   
159           else if c == '\n' then
160               add_last_offset
161               Bounds left = x as u32
162                      right = x as u32 + space_width
163                      up = y as u32
164                      down = y as u32 + char_height
165               |> offsets.add
166   
167               x = margin.left as i32
168               is_first_of_line = true
169               first_x = None
170               y += line_height
171               unrestricted_x = unrestricted_x.max x
172               unrestricted_y += line_height
173               maybe_prev_c = None
174           else
175               if is_first_of_line then
176                   first_x = Some x
177   
178               let (offset, char_width) =
179                   let prev_c = maybe_prev_c.unwrap_or ' '
180                   let offset = font.offset prev_c c
181                   let w = if c == ' '
182                           then space_width
183                           else rectangles[c].width
184   
185                   (offset, w)
186   
187               if maybe_prev_c == Some '{' then
188                   brace_count += 1
189                   if brace_count == 1 then
190                       assert maybe_brace_x.is_none
191                       maybe_brace_x = Some (x + offset)
192   
193               else if maybe_prev_c == Some '}' then
194                   brace_count -= 1
195                   if brace_count == 0 then
196                       assert maybe_brace_x.is_some
197                       maybe_brace_x = None
198   
199               let has_char_on_same_line (char : Char) =
200                   let mut index = i + 2
201                   while index < s.length do
202                       let nth = s[index]
203                       if nth == char then
204                           return true
205   
206                       if nth == '\n' then
207                           return false
208   
209                       index += 1
210   
211                   false
212   
213               let should_break_for_braced () =
214                   let is_single_line_braced_next = c == ' '
215                                                    && s.length > i + 2
216                                                    && s[i + 1] == '{'
217                                                    && has_char_on_same_line '}'
218   
219                   if not is_single_line_braced_next || max_width == 0 then
220                       false
221                   else
222                       let braced_length = get_braced_length s (i + 1) font allow_commands
223                       assert max_width <> 0
224                       let remaining_width = max_width as i32 - x
225                       let lines = braced_length as f32 / remaining_width as f32
226                       lines > 3
227   
228               let is_break_for_braced = should_break_for_braced
229   
230               if (max_width == 0
231                   || c == ' '
232                      && x + offset + space_width as i32
233                         + (get_word_length s (i + 1) font maybe_brace_x.is_some
234                                            allow_commands) as i32
235                         + margin.right as i32
236                         <= max_width as i32
237                   || c <> ' '
238                      && x + offset + char_width as i32 + margin.right as i32
239                         <= max_width as i32)
240                  && not is_break_for_braced
241               then
242                   x += offset
243               else
244                   add_last_offset
245   
246                   x = first_x.unwrap_or margin.left.as<i32>
247                   if is_break_for_braced then
248                       x = margin.left as i32 + 30 + first_x.unwrap_or 0
249                       first_x = None
250                   else if maybe_brace_x ? Some brace_x then
251                       x = brace_x
252   
253                   y += line_height
254                   end_of_line = true
255   
256               unrestricted_x += offset
257   
258               if c <> ' ' then
259                   let rectangle = rectangles[c]
260                   let left = rectangle.left as f32 / font_width as f32
261                   let right = rectangle.right as f32 / font_width as f32
262                   let down = rectangle.down as f32 / font_height as f32
263                   let up = rectangle.up as f32 / font_height as f32
264                   let quad = Quad
265                       up_left = Vector3 x.as<f32> y.as<f32> 0
266                       up_right = Vector3 (x as f32 + rectangle.width as f32) y.as<f32> 0
267                       down_left = Vector3 x.as<f32> (y as f32 + rectangle.height as f32) 0
268                       down_right = Vector3 (x as f32 + rectangle.width as f32)
269                                            (y as f32 + rectangle.height as f32)
270                                            0
271                       uv_up_left = Vector2 left down
272                       uv_up_right = Vector2 right down
273                       uv_down_left = Vector2 left up
274                       uv_down_right = Vector2 right up
275                       color = if is_multicolor
276                               then color
277                               else Vector3.gray 1
278   
279                   quads.add quad
280   
281               Bounds left = x as u32
282                      right = x as u32 + char_width
283                      up = y as u32
284                      down = y as u32 + char_height
285               |> offsets.add
286   
287               if not end_of_line || c <> ' ' then
288                   x += char_width as i32
289                   if x as u32 > width then
290                       width = x as u32
291   
292                   unrestricted_x += char_width as i32
293                   if unrestricted_x as u32 > unrestricted_width then
294                       unrestricted_width = unrestricted_x as u32
295   
296               maybe_prev_c = Some c
297               if is_first_of_line then
298                   is_first_of_line = false
299   
300       if s.length > 0 then
301           add_last_offset
302   
303       Bounds left = x as u32 + 1
304              right = x as u32 + space_width
305              up = y as u32
306              down = y as u32 + char_height
307       |> offsets.add
308   
309       width += margin.right
310       unrestricted_width += margin.right
311       let mesh_memory = MeshMemory.from_quads quads.as_slice
312       let maybe_links = if links.is_empty
313                         then None
314                         else Some links.as_readonly
315   
316       StringMeshMemory
317           memory = mesh_memory
318           offsets = offsets.as_readonly
319           links = maybe_links
320           =char_height
321           =width
322           height = y as u32 + char_height + margin.down
323           =unrestricted_width
324           unrestricted_height = unrestricted_y as u32 + char_height + margin.down
325