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