reelkit-cli 0.6.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/package.json +52 -9
- package/skill/SKILL.md +40 -19
- package/skill/THIRD_PARTY.md +104 -2
- package/skill/commands/launch-film.md +7 -0
- package/skill/reference/art-styles.md +70 -0
- package/skill/reference/asset-reuse.md +13 -2
- package/skill/reference/backgrounds.md +63 -0
- package/skill/reference/beat-sync.md +25 -19
- package/skill/reference/brand-motion.md +62 -0
- package/skill/reference/captions.md +11 -5
- package/skill/reference/clips.md +3 -3
- package/skill/reference/component-authoring.md +1 -1
- package/skill/reference/continuity.md +26 -7
- package/skill/reference/delivery-review.md +40 -0
- package/skill/reference/hebrew-rtl.md +3 -4
- package/skill/reference/{remotion-composition.md → hyperframes-composition.md} +18 -11
- package/skill/reference/kit.md +144 -12
- package/skill/reference/launch-film.md +194 -0
- package/skill/reference/motion-design.md +18 -16
- package/skill/reference/scene-treatments.md +20 -0
- package/skill/reference/scriptwriting.md +4 -1
- package/skill/reference/sound-design.md +34 -12
- package/skill/reference/studio-editing.md +55 -0
- package/skill/reference/styles.md +9 -6
- package/skill/reference/three-d.md +135 -0
- package/skill/reference/voice-sync.md +108 -0
- package/src/agents.ts +23 -12
- package/src/api/client.ts +4 -1
- package/src/cli.ts +18 -7
- package/src/commands/assets.ts +314 -30
- package/src/commands/build.ts +148 -35
- package/src/commands/init.ts +1 -1
- package/src/commands/install.ts +1 -1
- package/src/commands/plan.ts +8 -5
- package/src/commands/ref.ts +5 -2
- package/src/contract/index.ts +5 -3
- package/src/hyperframes/Root.tsx +1 -0
- package/src/hyperframes/fonts.ts +54 -0
- package/src/hyperframes/frame.tsx +46 -0
- package/src/hyperframes/host.tsx +38 -0
- package/src/hyperframes/kit/Assemble3D.tsx +92 -0
- package/src/hyperframes/kit/BrandTransform3D.tsx +12 -0
- package/src/hyperframes/kit/BrowserFrame.tsx +83 -0
- package/src/{remotion → hyperframes}/kit/Camera.tsx +7 -5
- package/src/{remotion → hyperframes}/kit/Captions.tsx +34 -18
- package/src/hyperframes/kit/Card3D.tsx +211 -0
- package/src/{remotion → hyperframes}/kit/Carry.tsx +1 -1
- package/src/hyperframes/kit/ChapterFrame.tsx +68 -0
- package/src/{remotion → hyperframes}/kit/ClipLayer.tsx +1 -1
- package/src/{remotion → hyperframes}/kit/Counter.tsx +1 -1
- package/src/hyperframes/kit/CounterRoll.tsx +75 -0
- package/src/{remotion → hyperframes}/kit/Entrance.tsx +1 -1
- package/src/{remotion → hyperframes}/kit/FootageLayer.tsx +1 -1
- package/src/hyperframes/kit/GlassPanel.tsx +43 -0
- package/src/hyperframes/kit/Grounds.tsx +177 -0
- package/src/hyperframes/kit/Headline.tsx +97 -0
- package/src/hyperframes/kit/Hero3D.tsx +197 -0
- package/src/hyperframes/kit/HudOverlay.tsx +52 -0
- package/src/hyperframes/kit/ImageLayers.tsx +48 -0
- package/src/{remotion → hyperframes}/kit/KenBurnsImage.tsx +1 -1
- package/src/{remotion → hyperframes}/kit/KeyedClip.tsx +1 -1
- package/src/{remotion → hyperframes}/kit/Layers.tsx +1 -1
- package/src/{remotion → hyperframes}/kit/LowerThird.tsx +1 -1
- package/src/hyperframes/kit/Music.tsx +19 -0
- package/src/hyperframes/kit/NamedCursor.tsx +54 -0
- package/src/hyperframes/kit/Orbit3D.tsx +49 -0
- package/src/hyperframes/kit/Particles3D.tsx +74 -0
- package/src/hyperframes/kit/Place.tsx +12 -0
- package/src/hyperframes/kit/PromptBox.tsx +84 -0
- package/src/hyperframes/kit/Scene3D.tsx +70 -0
- package/src/hyperframes/kit/SceneFrame.tsx +96 -0
- package/src/{remotion → hyperframes}/kit/ScreenOverlay.tsx +1 -1
- package/src/{remotion → hyperframes}/kit/Sfx.tsx +1 -1
- package/src/hyperframes/kit/SoundCues.tsx +22 -0
- package/src/hyperframes/kit/TerminalLog.tsx +98 -0
- package/src/hyperframes/kit/Text3D.tsx +78 -0
- package/src/hyperframes/kit/TextOnImage.tsx +41 -0
- package/src/{remotion → hyperframes}/kit/TitleCard.tsx +1 -1
- package/src/{remotion → hyperframes}/kit/Voiceover.tsx +1 -1
- package/src/hyperframes/kit/Warp3D.tsx +59 -0
- package/src/hyperframes/kit/bg-math.ts +179 -0
- package/src/hyperframes/kit/brand-transform.ts +25 -0
- package/src/{remotion → hyperframes}/kit/caption-groups.ts +7 -3
- package/src/hyperframes/kit/caption-style.ts +45 -0
- package/src/hyperframes/kit/docs.ts +249 -0
- package/src/hyperframes/kit/image-layers-math.ts +115 -0
- package/src/hyperframes/kit/index.ts +74 -0
- package/src/hyperframes/kit/inter-bold-typeface.ts +3 -0
- package/src/{remotion → hyperframes}/kit/motion-math.ts +36 -2
- package/src/hyperframes/kit/music-math.ts +59 -0
- package/src/hyperframes/kit/quiet-three.ts +11 -0
- package/src/hyperframes/kit/sample-text.ts +55 -0
- package/src/hyperframes/kit/scene3d-context.ts +5 -0
- package/src/hyperframes/kit/seeded.ts +13 -0
- package/src/hyperframes/kit/sound-cues.ts +89 -0
- package/src/hyperframes/kit/sound-kinds.ts +135 -0
- package/src/{remotion → hyperframes}/kit/theme.ts +43 -39
- package/src/hyperframes/kit/three-fx-math.ts +192 -0
- package/src/hyperframes/kit/three-math.ts +145 -0
- package/src/hyperframes/kit/transition-math.ts +116 -0
- package/src/hyperframes/kit/ui-math.ts +145 -0
- package/src/hyperframes/kit/ui-theme.ts +25 -0
- package/src/hyperframes/kit/word-anchor.ts +107 -0
- package/src/hyperframes/math.ts +62 -0
- package/src/hyperframes/three.tsx +10 -0
- package/src/pipeline/beatsnap.ts +72 -0
- package/src/pipeline/review.ts +44 -10
- package/src/pipeline/schema.ts +51 -4
- package/src/pipeline/timing.ts +27 -1
- package/src/project/background.ts +33 -0
- package/src/project/chromakey.ts +1 -1
- package/src/project/layers.ts +60 -0
- package/src/project/manifest.ts +59 -14
- package/src/project/music.ts +19 -5
- package/src/project/project.ts +4 -1
- package/src/project/serve.ts +2 -2
- package/src/project/soundreport.ts +347 -0
- package/src/project/svgcheck.ts +21 -0
- package/src/render/component-preview.ts +11 -55
- package/src/render/contact-sheet.ts +39 -0
- package/src/render/continuity.ts +14 -4
- package/src/render/deps.ts +15 -3
- package/src/render/render.ts +62 -57
- package/src/render/serve.ts +31 -0
- package/src/render/sound-notes.ts +106 -0
- package/src/render/static-check.ts +15 -4
- package/src/render/validate.ts +4 -4
- package/src/render/word-check.ts +181 -0
- package/src/render/worker.ts +71 -0
- package/src/testing/conformance.ts +12 -0
- package/src/testing/fake-api.ts +4 -4
- package/src/testing/fixtures.ts +4 -1
- package/src/remotion/Root.tsx +0 -31
- package/src/remotion/kit/Music.tsx +0 -19
- package/src/remotion/kit/SceneFrame.tsx +0 -19
- package/src/remotion/kit/docs.ts +0 -124
- package/src/remotion/kit/index.ts +0 -29
- package/src/remotion/kit/music-math.ts +0 -42
- /package/src/{remotion → hyperframes}/kit/Icon.tsx +0 -0
- /package/src/{remotion → hyperframes}/kit/beat.ts +0 -0
- /package/src/{remotion → hyperframes}/kit/brand-icons.ts +0 -0
- /package/src/{remotion → hyperframes}/kit/media.ts +0 -0
- /package/src/{remotion → hyperframes}/types.ts +0 -0
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// Inter Bold as a three.js typeface (the Latin letters, digits, punctuation and a few symbols), converted from the Inter Project's font files,
|
|
2
|
+
// SIL Open Font License 1.1 (see skill/THIRD_PARTY.md). It is data, so that extruded text needs no network and no file to load.
|
|
3
|
+
export const interBoldTypeface = {"glyphs":{"0":{"ha":674,"x_min":46,"x_max":628,"o":"m 337 -10 l 337 -10 q 180 34 245 -10 q 81 162 115 79 q 46 363 46 245 l 46 363 q 81 565 46 481 q 180 693 115 648 q 337 737 245 737 l 337 737 q 494 693 429 737 q 594 564 559 648 q 628 363 628 481 l 628 363 q 594 162 628 245 q 494 34 560 79 q 337 -10 429 -10 m 337 114 l 337 114 q 413 143 382 114 q 460 228 444 172 q 477 363 477 284 l 477 363 q 460 500 477 443 q 413 585 444 556 q 337 614 382 614 l 337 614 q 261 585 292 614 q 214 499 230 555 q 198 363 198 443 l 198 363 q 214 228 198 284 q 261 143 230 172 q 337 114 292 114"},"1":{"ha":431,"x_min":45,"x_max":366,"o":"m 206 728 l 366 728 l 366 0 l 217 0 l 217 596 l 212 596 l 45 478 l 45 615 l 206 728"},"2":{"ha":630,"x_min":54,"x_max":575,"o":"m 575 0 l 61 0 l 61 107 l 318 349 q 374 407 352 381 q 409 459 397 434 q 421 513 421 484 l 421 513 q 406 569 421 546 q 366 605 392 593 q 308 618 341 618 l 308 618 q 249 604 274 618 q 210 565 224 591 q 196 504 196 540 l 196 504 l 54 504 q 86 628 54 575 q 176 709 119 680 q 309 737 234 737 l 309 737 q 444 710 386 737 q 533 634 501 682 q 565 522 565 585 l 565 522 q 550 442 565 482 q 493 354 534 403 q 377 236 452 305 l 377 236 l 268 128 l 268 123 l 575 123 l 575 0"},"3":{"ha":646,"x_min":48,"x_max":600,"o":"m 322 -10 l 322 -10 q 181 17 243 -10 q 85 93 120 44 q 48 204 49 141 l 48 204 l 198 204 q 216 156 200 177 q 260 125 232 136 q 322 114 288 114 l 322 114 q 386 126 358 114 q 429 161 413 139 q 444 213 444 184 l 444 213 q 428 267 444 244 q 381 302 412 290 q 309 315 351 315 l 309 315 l 241 315 l 241 426 l 309 426 q 371 439 344 426 q 413 473 398 451 q 429 524 429 495 l 429 524 q 416 573 429 552 q 379 606 402 594 q 323 618 355 618 l 323 618 q 265 607 292 618 q 223 575 239 595 q 206 526 207 554 l 206 526 l 63 526 q 98 636 63 588 q 191 710 132 684 q 324 737 250 737 l 324 737 q 455 710 399 737 q 542 636 511 683 q 573 533 573 590 l 573 533 q 535 431 573 472 q 437 378 498 390 l 437 378 l 437 373 q 526 343 490 366 q 581 285 563 320 q 600 204 600 249 l 600 204 q 564 94 600 142 q 466 18 528 45 q 322 -10 403 -10"},"4":{"ha":676,"x_min":49,"x_max":631,"o":"m 397 129 l 49 129 l 49 247 l 354 728 l 541 728 l 541 250 l 631 250 l 631 129 l 541 129 l 541 0 l 397 0 l 397 129 m 200 250 l 400 250 l 400 563 l 395 563 l 200 255 l 200 250"},"5":{"ha":622,"x_min":45,"x_max":576,"o":"m 307 -10 l 307 -10 q 174 18 232 -10 q 81 94 115 45 q 45 204 47 142 l 45 204 l 191 204 q 208 154 192 176 q 250 120 224 132 q 307 108 276 108 l 307 108 q 371 125 343 108 q 416 170 399 141 q 432 238 432 200 l 432 238 q 415 307 432 277 q 370 354 399 337 q 304 370 342 370 l 304 370 q 241 357 271 370 q 195 322 211 344 l 195 322 l 61 346 l 96 728 l 536 728 l 536 604 l 219 604 l 201 420 l 205 420 q 262 464 224 447 q 348 481 301 481 l 348 481 q 439 463 397 481 q 511 414 480 446 q 559 338 542 382 q 576 242 576 294 l 576 242 q 542 112 576 168 q 448 23 508 55 q 307 -10 387 -10"},"6":{"ha":649,"x_min":46,"x_max":604,"o":"m 334 -10 l 334 -10 q 227 9 279 -10 q 135 69 175 27 q 70 178 94 110 q 46 347 46 247 l 46 347 q 66 511 46 438 q 125 634 87 583 q 218 711 164 685 q 339 737 272 737 l 339 737 q 466 709 411 737 q 555 634 521 681 q 596 526 589 586 l 596 526 l 450 526 q 411 587 440 564 q 339 609 382 609 l 339 609 q 259 581 292 609 q 209 500 226 552 q 192 376 192 448 l 192 376 l 197 376 q 241 431 214 408 q 305 466 269 454 q 380 478 340 478 l 380 478 q 495 448 444 478 q 575 365 545 418 q 604 243 604 312 l 604 243 q 570 112 604 169 q 475 23 536 55 q 334 -10 415 -10 m 333 108 l 333 108 q 397 126 369 108 q 442 173 426 143 q 459 239 459 202 l 459 239 q 443 305 459 276 q 398 352 427 334 q 334 369 370 369 l 334 369 q 284 359 307 369 q 244 331 261 349 q 217 289 227 313 q 208 239 208 266 l 208 239 q 224 174 208 203 q 268 126 240 144 q 333 108 297 108"},"7":{"ha":582,"x_min":37,"x_max":545,"o":"m 241 0 l 86 0 l 389 600 l 389 604 l 37 604 l 37 728 l 545 728 l 545 603 l 241 0"},"8":{"ha":651,"x_min":46,"x_max":605,"o":"m 325 -10 l 325 -10 q 181 17 244 -10 q 82 90 118 44 q 46 195 46 137 l 46 195 q 67 279 46 241 q 123 343 87 317 q 203 376 158 369 l 203 376 l 203 380 q 108 438 145 393 q 72 542 72 482 l 72 542 q 105 642 72 598 q 195 712 138 687 q 325 737 252 737 l 325 737 q 455 712 398 737 q 546 642 513 687 q 579 542 579 598 l 579 542 q 542 438 579 482 q 449 380 505 393 l 449 380 l 449 376 q 528 343 492 369 q 584 279 563 317 q 605 195 605 241 l 605 195 q 569 90 605 137 q 470 17 533 44 q 325 -10 407 -10 m 325 101 l 325 101 q 391 114 363 101 q 434 152 418 127 q 450 207 450 176 l 450 207 q 434 264 450 239 q 389 303 417 289 q 325 317 361 317 l 325 317 q 261 303 289 317 q 217 264 233 289 q 201 207 201 240 l 201 207 q 216 152 201 176 q 260 114 231 128 q 325 101 288 101 m 325 427 l 325 427 q 381 439 356 427 q 419 475 405 452 q 433 526 433 497 l 433 526 q 419 577 433 555 q 381 611 405 599 q 325 624 357 624 l 325 624 q 269 611 293 624 q 231 577 245 599 q 218 526 218 555 l 218 526 q 231 475 218 497 q 270 439 245 452 q 325 427 294 427"},"9":{"ha":649,"x_min":46,"x_max":604,"o":"m 311 -11 l 311 -11 q 183 18 239 -11 q 94 94 128 46 q 53 203 61 143 l 53 203 l 201 203 q 238 141 209 164 q 311 118 267 118 l 311 118 q 391 146 358 118 q 441 227 424 175 q 458 352 458 280 l 458 352 l 453 352 q 409 297 437 320 q 346 262 381 274 q 271 250 311 250 l 271 250 q 155 280 206 250 q 75 363 104 310 q 46 485 46 417 l 46 485 q 80 616 46 559 q 175 706 114 673 q 317 738 236 739 l 317 738 q 423 719 372 738 q 515 659 475 701 q 580 550 556 618 q 604 381 604 481 l 604 381 q 583 217 604 289 q 524 93 563 144 q 432 16 486 42 q 311 -11 377 -11 m 316 359 l 316 359 q 366 369 343 359 q 406 398 389 380 q 433 439 423 416 q 443 490 443 462 l 443 490 q 427 554 443 525 q 382 601 410 583 q 317 619 354 619 l 317 619 q 253 602 281 619 q 208 555 224 584 q 191 489 191 525 l 191 489 q 207 423 191 452 q 252 376 224 394 q 316 359 280 359"}," ":{"ha":237,"x_min":0,"x_max":0,"o":""},"!":{"ha":338,"x_min":83,"x_max":256,"o":"m 237 230 l 101 230 l 90 728 l 248 728 l 237 230 m 169 -9 l 169 -9 q 107 14 131 -9 q 83 73 83 37 l 83 73 q 107 133 83 110 q 169 156 131 156 l 169 156 q 232 133 208 156 q 256 73 256 110 l 256 73 q 232 14 256 37 q 169 -9 208 -9"},"\"":{"ha":552,"x_min":99,"x_max":453,"o":"m 225 425 l 114 425 l 99 728 l 240 728 l 225 425 m 438 425 l 327 425 l 312 728 l 453 728 l 438 425"},"#":{"ha":649,"x_min":11,"x_max":638,"o":"m 448 0 l 337 0 l 367 182 l 222 182 l 192 0 l 82 0 l 111 182 l 11 182 l 30 292 l 129 292 l 153 435 l 53 435 l 71 545 l 171 545 l 201 728 l 312 728 l 282 545 l 427 545 l 457 728 l 567 728 l 538 545 l 638 545 l 619 435 l 519 435 l 496 292 l 596 292 l 577 182 l 478 182 l 448 0 m 264 435 l 240 292 l 385 292 l 408 435 l 264 435"},"$":{"ha":655,"x_min":42,"x_max":613,"o":"m 361 -98 l 303 -98 l 303 -10 q 181 16 234 -6 l 181 16 q 80 96 116 43 q 42 226 43 148 l 42 226 l 187 226 q 208 164 189 189 q 259 127 228 140 l 259 127 q 303 117 279 120 l 303 117 l 303 309 l 258 320 q 113 391 166 343 q 61 518 61 439 l 61 518 q 96 633 61 584 q 194 710 132 683 l 194 710 q 303 736 243 732 l 303 736 l 303 826 l 361 826 l 361 736 q 475 710 425 732 l 475 710 q 569 633 535 682 q 604 520 603 584 l 604 520 l 459 520 q 422 588 456 564 l 422 588 q 361 610 397 605 l 361 610 l 361 428 l 396 420 q 481 392 442 410 q 551 349 521 375 q 597 288 581 323 q 613 207 613 253 l 613 207 q 580 92 613 141 q 484 16 547 43 l 484 16 q 361 -10 432 -6 l 361 -10 l 361 -98 m 361 294 l 361 117 q 401 127 383 120 l 401 127 q 447 159 430 138 q 463 208 463 180 l 463 208 q 448 250 463 233 q 406 279 434 267 l 406 279 q 361 294 387 287 l 361 294 m 303 444 l 303 444 l 303 610 q 269 601 284 607 l 269 601 q 228 571 242 590 q 214 528 214 552 l 214 528 q 230 483 214 501 q 273 455 246 465 l 273 455 q 303 444 287 448"},"%":{"ha":1016,"x_min":91,"x_max":925,"o":"m 250 380 l 250 380 q 162 402 198 380 q 109 461 127 424 q 91 541 91 498 l 91 541 l 91 579 q 109 659 91 623 q 163 717 128 695 q 250 739 199 739 l 250 739 q 337 717 301 739 q 390 659 372 695 q 408 579 408 623 l 408 579 l 408 541 q 390 460 408 497 q 336 402 372 424 q 250 380 301 380 m 250 470 l 250 470 q 294 492 282 470 q 306 541 306 514 l 306 541 l 306 579 q 295 628 306 605 q 250 650 283 650 l 250 650 q 205 627 217 650 q 192 579 192 604 l 192 579 l 192 541 q 205 492 192 514 q 250 470 217 470 m 766 -13 l 766 -13 q 679 9 715 -13 q 626 68 644 31 q 607 147 607 104 l 607 147 l 607 186 q 626 266 607 229 q 680 324 645 302 q 766 346 716 346 l 766 346 q 854 324 818 346 q 907 266 889 302 q 925 186 925 229 l 925 186 l 925 147 q 906 67 925 104 q 853 9 888 31 q 766 -13 817 -13 m 766 77 l 766 77 q 811 99 799 77 q 823 147 823 121 l 823 147 l 823 186 q 811 235 823 212 q 766 257 800 257 l 766 257 q 722 234 734 257 q 709 186 709 211 l 709 186 l 709 147 q 722 99 709 121 q 766 77 734 77 m 306 0 l 205 0 l 705 728 l 806 728 l 306 0"},"&":{"ha":672,"x_min":40,"x_max":667,"o":"m 283 -11 l 283 -11 q 153 17 208 -11 q 69 90 98 44 q 40 192 40 136 l 40 192 q 57 269 40 236 q 105 331 74 302 l 105 331 q 174 384 135 357 l 174 384 q 168 391 171 387 l 168 391 q 122 466 140 426 q 103 552 103 505 l 103 552 q 128 646 103 604 q 197 712 152 688 q 300 736 241 736 l 300 736 q 400 713 358 736 q 465 651 442 690 q 488 565 488 612 l 488 565 q 476 501 488 531 q 443 446 464 472 q 392 399 421 421 l 392 399 l 348 368 l 471 226 q 483 253 478 239 l 483 253 q 501 353 501 298 l 501 353 l 624 353 q 609 232 624 285 q 569 141 595 179 l 569 141 q 559 125 564 133 l 559 125 l 667 0 l 513 0 l 469 49 q 383 5 431 20 l 383 5 q 283 -11 332 -11 m 390 138 l 249 296 l 243 292 q 198 248 213 270 q 183 198 183 227 l 183 198 q 196 150 183 170 q 232 118 209 129 q 287 107 256 107 l 287 107 q 365 125 327 107 l 365 125 q 390 138 377 131 l 390 138 m 276 452 l 276 452 l 315 478 q 343 500 331 488 q 362 527 355 513 q 369 558 369 542 l 369 558 q 352 601 369 583 q 301 618 334 618 l 301 618 q 264 610 280 618 q 239 586 248 602 q 230 551 230 571 l 230 551 q 242 503 230 527 q 275 454 254 479 l 275 454 q 276 452 275 453"},"'":{"ha":339,"x_min":99,"x_max":240,"o":"m 225 425 l 114 425 l 99 728 l 240 728 l 225 425"},"(":{"ha":377,"x_min":83,"x_max":345,"o":"m 83 296 l 83 296 q 99 465 83 377 q 144 633 115 552 q 208 772 172 713 l 208 772 l 345 772 q 284 614 310 696 q 245 451 259 532 q 232 296 232 370 l 232 296 q 243 168 232 232 q 280 29 255 103 q 345 -136 305 -44 l 345 -136 l 208 -136 q 115 77 146 -33 q 83 296 83 187"},")":{"ha":377,"x_min":33,"x_max":294,"o":"m 168 -136 l 33 -136 q 98 31 73 -43 q 134 169 123 104 q 145 296 145 233 l 145 296 q 132 451 145 370 q 93 614 118 532 q 33 772 68 696 l 33 772 l 168 772 q 234 633 206 713 q 278 465 262 552 q 294 296 294 377 l 294 296 q 262 76 294 186 q 168 -136 230 -34 l 168 -136"},"*":{"ha":559,"x_min":78,"x_max":480,"o":"m 326 318 l 232 318 l 242 457 l 125 379 l 78 461 l 204 522 l 78 583 l 125 667 l 242 589 l 232 728 l 326 728 l 317 589 l 434 667 l 480 583 l 355 522 l 480 461 l 434 379 l 317 457 l 326 318"},"+":{"ha":679,"x_min":89,"x_max":590,"o":"m 401 40 l 277 40 l 277 232 l 89 232 l 89 349 l 277 349 l 277 541 l 401 541 l 401 349 l 590 349 l 590 232 l 401 232 l 401 40"},",":{"ha":334,"x_min":58,"x_max":248,"o":"m 158 -193 l 58 -193 l 101 110 l 248 110 l 158 -193"},"-":{"ha":468,"x_min":68,"x_max":399,"o":"m 68 358 l 399 358 l 399 241 l 68 241 l 68 358"},".":{"ha":334,"x_min":83,"x_max":251,"o":"m 167 -9 l 167 -9 q 107 15 131 -9 q 83 75 83 39 l 83 75 q 107 135 83 110 q 167 159 131 159 l 167 159 q 227 135 203 159 q 251 75 251 110 l 251 75 q 227 15 251 39 q 167 -9 203 -9"},"/":{"ha":388,"x_min":14,"x_max":375,"o":"m 248 762 l 375 762 l 141 -109 l 14 -109 l 248 762"},":":{"ha":334,"x_min":83,"x_max":251,"o":"m 167 -9 l 167 -9 q 107 15 131 -9 q 83 75 83 39 l 83 75 q 107 135 83 110 q 167 159 131 159 l 167 159 q 227 135 203 159 q 251 75 251 110 l 251 75 q 227 15 251 39 q 167 -9 203 -9 m 167 353 l 167 353 q 107 377 131 353 q 83 437 83 401 l 83 437 q 107 497 83 473 q 167 521 131 521 l 167 521 q 227 497 203 521 q 251 437 251 473 l 251 437 q 227 377 251 401 q 167 353 203 353"},";":{"ha":343,"x_min":58,"x_max":260,"o":"m 158 -193 l 58 -193 l 101 110 l 248 110 l 158 -193 m 176 353 l 176 353 q 116 377 140 353 q 91 437 91 401 l 91 437 q 116 497 91 473 q 176 521 140 521 l 176 521 q 236 497 211 521 q 260 437 260 473 l 260 437 q 236 377 260 401 q 176 353 211 353"},"<":{"ha":679,"x_min":81,"x_max":579,"o":"m 579 9 l 81 230 l 81 351 l 579 572 l 579 431 l 240 293 l 240 287 l 579 149 l 579 9"},"=":{"ha":679,"x_min":96,"x_max":583,"o":"m 583 346 l 96 346 l 96 469 l 583 469 l 583 346 m 583 112 l 96 112 l 96 235 l 583 235 l 583 112"},">":{"ha":679,"x_min":100,"x_max":598,"o":"m 598 351 l 598 230 l 100 9 l 100 149 l 439 287 l 439 293 l 100 431 l 100 572 l 598 351"},"?":{"ha":560,"x_min":39,"x_max":512,"o":"m 321 224 l 190 224 l 190 235 q 201 333 190 297 q 233 390 212 369 q 284 429 254 411 l 284 429 q 339 476 317 451 q 362 536 362 500 l 362 536 q 350 580 362 562 q 318 609 338 599 q 273 619 298 619 l 273 619 q 229 609 250 619 q 194 579 208 599 q 179 529 181 559 l 179 529 l 39 529 q 72 645 40 599 q 157 714 104 691 q 273 737 210 737 l 273 737 q 397 714 344 737 q 482 646 451 690 q 512 541 512 603 l 512 541 q 483 437 512 479 q 403 366 454 396 l 403 366 q 357 332 375 349 q 330 293 339 315 q 321 235 321 271 l 321 235 l 321 224 m 256 -9 l 256 -9 q 194 14 218 -9 q 169 73 169 37 l 169 73 q 194 133 169 110 q 256 156 218 156 l 256 156 q 319 133 295 156 q 343 73 343 110 l 343 73 q 319 14 343 37 q 256 -9 295 -9"},"@":{"ha":1016,"x_min":46,"x_max":970,"o":"m 519 -209 l 519 -209 q 319 -178 406 -209 q 170 -88 231 -147 q 77 56 109 -29 q 46 252 46 142 l 46 252 q 76 444 46 357 q 167 592 107 530 q 314 688 226 655 q 515 722 401 722 l 515 722 q 705 691 621 722 q 848 603 789 659 q 939 469 908 546 q 970 303 970 393 l 970 303 q 959 189 970 242 q 924 97 948 137 q 861 35 899 58 q 768 13 823 13 l 768 13 q 708 22 736 13 q 661 49 680 30 q 637 95 642 67 l 637 95 l 633 95 q 581 35 620 58 q 485 13 543 13 l 485 13 q 372 43 418 13 q 300 129 325 74 q 275 257 275 184 l 275 257 q 302 384 275 329 q 377 469 329 438 q 488 500 425 500 l 488 500 q 571 483 536 500 q 619 439 605 466 l 619 439 l 625 439 l 625 489 l 725 489 l 725 160 q 736 123 725 136 q 769 109 747 109 l 769 109 q 818 129 800 109 q 844 189 836 148 q 852 293 852 230 l 852 293 q 831 423 852 365 q 770 523 811 481 q 667 588 729 565 q 523 611 605 611 l 523 611 q 371 587 437 611 q 260 517 305 563 q 192 405 215 471 q 169 255 169 338 l 169 255 q 193 104 169 170 q 261 -6 216 39 q 374 -74 307 -51 q 530 -97 441 -97 l 530 -97 q 606 -91 570 -97 q 673 -76 643 -85 q 724 -59 704 -67 l 724 -59 l 762 -161 q 700 -184 738 -172 q 616 -202 662 -195 q 519 -209 569 -209 m 499 117 l 499 117 q 566 133 540 117 q 605 181 592 149 q 618 259 618 212 l 618 259 q 605 334 618 306 q 565 376 591 363 q 500 390 539 390 l 500 390 q 437 373 463 390 q 397 327 411 356 q 384 259 384 298 l 384 259 q 396 186 384 218 q 434 135 409 154 q 499 117 460 117"},"A":{"ha":747,"x_min":24,"x_max":723,"o":"m 188 0 l 24 0 l 272 728 l 468 728 l 723 0 l 557 0 l 501 169 l 243 169 l 188 0 m 280 286 l 462 286 l 440 351 q 398 500 419 419 l 398 500 q 369 610 384 552 l 369 610 q 341 499 354 551 l 341 499 q 301 351 321 417 l 301 351 l 280 286"},"B":{"ha":662,"x_min":66,"x_max":623,"o":"m 373 0 l 66 0 l 66 728 l 354 728 q 488 703 435 728 q 568 637 542 679 q 595 541 595 595 l 595 541 q 578 466 595 498 q 531 413 561 434 q 464 384 501 393 l 464 384 l 464 377 q 541 354 505 375 q 600 293 578 332 q 623 201 623 254 l 623 201 q 595 98 623 143 q 511 26 566 52 q 373 0 456 0 l 373 0 m 215 320 l 215 123 l 344 123 q 439 148 409 123 q 470 215 470 173 l 470 215 q 455 269 470 246 q 413 306 440 293 q 348 320 385 320 l 348 320 l 215 320 m 215 606 l 215 422 l 333 422 q 390 433 364 422 q 429 466 415 445 q 444 517 444 487 l 444 517 q 416 581 444 556 q 335 606 388 606 l 335 606 l 215 606"},"C":{"ha":740,"x_min":46,"x_max":697,"o":"m 384 -10 l 384 -10 q 211 34 287 -10 q 90 162 134 78 q 46 363 46 245 l 46 363 q 90 566 46 482 q 211 693 135 649 q 384 737 288 737 l 384 737 q 499 720 446 737 q 594 669 553 703 q 662 588 636 636 q 697 479 688 540 l 697 479 l 546 479 q 527 532 541 509 q 492 572 513 555 q 445 596 472 588 q 387 605 418 605 l 387 605 q 287 577 330 605 q 221 494 245 548 q 197 363 197 440 l 197 363 q 221 231 197 285 q 288 150 245 177 q 386 123 330 123 l 386 123 q 444 131 417 123 q 492 156 471 140 q 527 196 513 172 q 546 249 541 219 l 546 249 l 697 249 q 667 152 691 198 q 603 69 643 105 q 508 11 563 32 q 384 -10 453 -10"},"D":{"ha":722,"x_min":66,"x_max":676,"o":"m 322 0 l 322 0 l 66 0 l 66 728 l 324 728 q 512 684 434 728 q 634 559 591 640 q 676 364 676 478 l 676 364 q 634 169 676 250 q 512 44 591 87 q 322 0 433 0 m 215 599 l 215 128 l 314 128 q 432 153 385 128 q 504 230 480 178 q 528 364 528 283 l 528 364 q 504 497 528 445 q 432 574 479 549 q 315 599 385 599 l 315 599 l 215 599"},"E":{"ha":607,"x_min":66,"x_max":554,"o":"m 554 0 l 66 0 l 66 728 l 553 728 l 553 604 l 215 604 l 215 428 l 527 428 l 527 307 l 215 307 l 215 124 l 554 124 l 554 0"},"F":{"ha":587,"x_min":66,"x_max":544,"o":"m 215 0 l 66 0 l 66 728 l 544 728 l 544 604 l 215 604 l 215 401 l 512 401 l 512 279 l 215 279 l 215 0"},"G":{"ha":750,"x_min":46,"x_max":702,"o":"m 388 -10 l 388 -10 q 209 35 286 -10 q 89 164 132 81 q 46 363 46 248 l 46 363 q 91 565 46 481 q 212 693 136 648 q 385 737 289 737 l 385 737 q 499 719 446 737 q 594 669 552 702 q 661 592 635 636 q 695 493 687 547 l 695 493 l 543 493 q 522 540 536 519 q 488 575 508 561 q 443 597 468 590 q 387 605 417 605 l 387 605 q 288 577 331 605 q 221 495 245 549 q 197 364 197 441 q 221 233 197 287 q 288 151 245 179 q 389 123 331 123 l 389 123 q 479 142 442 123 q 538 196 517 161 l 538 196 q 558 274 557 230 l 558 274 l 399 274 l 399 388 l 702 388 l 702 297 q 662 133 702 201 q 550 27 621 64 q 388 -10 479 -10"},"H":{"ha":747,"x_min":66,"x_max":681,"o":"m 215 0 l 66 0 l 66 728 l 215 728 l 215 434 l 532 434 l 532 728 l 681 728 l 681 0 l 532 0 l 532 310 l 215 310 l 215 0"},"I":{"ha":281,"x_min":66,"x_max":215,"o":"m 66 728 l 215 728 l 215 0 l 66 0 l 66 728"},"J":{"ha":584,"x_min":33,"x_max":519,"o":"m 277 -10 l 277 -10 q 98 50 164 -10 q 33 219 33 109 l 33 219 l 33 261 l 182 261 l 182 216 q 208 137 182 164 q 277 110 233 110 l 277 110 q 346 137 320 110 q 371 217 371 164 l 371 217 l 371 728 l 519 728 l 519 219 q 454 50 519 109 q 277 -10 390 -10"},"K":{"ha":719,"x_min":66,"x_max":694,"o":"m 215 0 l 66 0 l 66 728 l 215 728 l 215 529 l 213 364 q 220 375 216 370 l 220 375 q 267 444 241 409 q 326 519 293 478 l 326 519 l 504 728 l 687 728 l 415 413 l 694 0 l 518 0 l 311 314 l 215 208 l 215 0"},"L":{"ha":565,"x_min":66,"x_max":529,"o":"m 529 0 l 66 0 l 66 728 l 215 728 l 215 124 l 529 124 l 529 0"},"M":{"ha":932,"x_min":66,"x_max":866,"o":"m 214 0 l 66 0 l 66 728 l 294 728 l 413 394 q 429 333 420 370 q 449 253 439 296 l 449 253 q 467 171 458 211 l 467 171 q 484 252 475 210 l 484 252 q 504 333 494 295 q 521 394 513 370 l 521 394 l 637 728 l 866 728 l 866 0 l 716 0 l 716 343 q 717 402 716 367 q 719 479 718 438 q 721 563 720 521 l 721 563 q 722 599 722 581 l 722 599 q 711 558 717 579 l 711 558 q 689 474 700 514 q 667 399 677 433 q 649 343 657 365 l 649 343 l 529 0 l 403 0 l 281 343 q 263 399 273 365 q 241 473 253 432 q 218 557 230 513 l 218 557 q 208 594 213 575 l 208 594 q 209 564 208 580 l 209 564 q 211 481 210 523 q 213 404 212 439 q 214 343 214 368 l 214 343 l 214 0"},"N":{"ha":762,"x_min":66,"x_max":696,"o":"m 218 0 l 66 0 l 66 728 l 232 728 l 462 359 q 498 298 479 331 q 535 224 516 265 l 535 224 q 553 182 544 204 l 553 182 q 551 219 552 200 l 551 219 q 546 307 548 265 q 544 378 544 349 l 544 378 l 544 728 l 696 728 l 696 0 l 530 0 l 321 334 q 278 407 298 372 q 238 485 259 442 l 238 485 q 208 544 225 511 l 208 544 q 212 486 210 514 l 212 486 q 217 399 215 438 q 218 334 218 359 l 218 334 l 218 0"},"O":{"ha":771,"x_min":46,"x_max":725,"o":"m 385 -10 l 385 -10 q 212 34 289 -10 q 91 162 135 78 q 46 363 46 245 l 46 363 q 91 566 46 482 q 212 693 135 649 q 385 737 289 737 l 385 737 q 559 693 482 737 q 680 566 635 649 q 725 363 725 482 l 725 363 q 680 162 725 245 q 559 34 635 78 q 385 -10 482 -10 m 385 123 l 385 123 q 484 150 442 123 q 550 232 526 178 q 573 363 573 286 l 573 363 q 550 496 573 442 q 484 577 526 550 q 385 605 442 605 l 385 605 q 287 577 329 605 q 221 495 244 549 q 197 363 197 441 l 197 363 q 221 232 197 286 q 287 150 244 178 q 385 123 329 123"},"P":{"ha":648,"x_min":66,"x_max":610,"o":"m 215 0 l 66 0 l 66 728 l 350 728 q 491 696 433 728 q 579 609 549 665 q 610 482 610 554 l 610 482 q 579 355 610 410 q 489 269 548 300 q 346 238 430 238 l 346 238 l 215 238 l 215 0 m 215 604 l 215 358 l 322 358 q 398 374 368 358 q 442 417 427 390 q 456 482 456 445 q 442 546 456 519 q 397 589 427 574 q 322 604 368 604 l 322 604 l 215 604"},"Q":{"ha":777,"x_min":46,"x_max":725,"o":"m 385 -10 l 385 -10 q 212 34 289 -10 q 91 162 135 78 q 46 363 46 245 l 46 363 q 91 566 46 482 q 212 693 135 649 q 385 737 289 737 l 385 737 q 559 693 482 737 q 680 566 635 649 q 725 363 725 482 l 725 363 q 680 162 725 245 l 680 162 q 611 72 652 109 l 611 72 l 716 -61 l 577 -61 l 517 14 q 385 -10 457 -10 m 432 128 l 339 250 l 467 250 l 520 182 q 550 232 538 203 l 550 232 q 573 363 573 286 l 573 363 q 550 496 573 442 q 484 577 526 550 q 385 605 442 605 l 385 605 q 287 577 329 605 q 221 495 244 549 q 197 363 197 441 l 197 363 q 221 232 197 286 q 287 150 244 178 q 385 123 329 123 l 385 123 q 432 128 410 123 l 432 128"},"R":{"ha":657,"x_min":66,"x_max":636,"o":"m 215 0 l 66 0 l 66 728 l 350 728 q 491 698 433 728 q 579 616 549 669 q 610 491 610 563 q 579 367 610 419 q 489 288 548 315 l 489 288 q 481 284 485 286 l 481 284 l 636 0 l 471 0 l 332 260 l 215 260 l 215 0 m 215 604 l 215 380 l 322 380 q 397 393 368 380 q 441 430 427 405 q 456 491 456 455 l 456 491 q 441 553 456 528 q 397 591 427 578 q 322 604 368 604 l 322 604 l 215 604"},"S":{"ha":655,"x_min":42,"x_max":613,"o":"m 333 -11 l 333 -11 q 180 16 245 -11 q 80 96 116 43 q 42 226 43 148 l 42 226 l 187 226 q 208 164 189 189 q 259 127 228 140 q 331 115 291 115 l 331 115 q 401 127 371 115 q 447 159 430 138 q 463 208 463 180 l 463 208 q 448 250 463 233 q 406 279 434 267 q 338 300 378 291 l 338 300 l 258 320 q 113 391 166 343 q 61 518 61 439 l 61 518 q 96 633 61 584 q 194 710 132 683 q 334 737 255 737 l 334 737 q 475 710 415 737 q 569 633 535 682 q 604 520 603 584 l 604 520 l 459 520 q 422 588 456 564 q 333 612 389 612 l 333 612 q 269 601 295 612 q 228 571 242 590 q 214 528 214 552 l 214 528 q 230 483 214 501 q 273 454 246 465 q 330 436 300 443 l 330 436 l 396 420 q 481 392 442 410 q 551 349 521 375 q 597 288 581 323 q 613 207 613 253 l 613 207 q 580 92 613 141 q 484 16 547 43 q 333 -11 421 -11"},"T":{"ha":667,"x_min":37,"x_max":631,"o":"m 259 604 l 37 604 l 37 728 l 631 728 l 631 604 l 409 604 l 409 0 l 259 0 l 259 604"},"U":{"ha":732,"x_min":66,"x_max":667,"o":"m 366 -10 l 366 -10 q 208 23 276 -10 q 103 116 141 57 q 66 255 66 176 l 66 255 l 66 728 l 215 728 l 215 268 q 234 192 215 225 q 286 140 252 159 q 366 122 320 122 l 366 122 q 446 140 412 122 q 498 192 479 159 q 517 268 517 225 l 517 268 l 517 728 l 667 728 l 667 255 q 629 116 667 176 q 524 23 591 57 q 366 -10 456 -10"},"V":{"ha":747,"x_min":24,"x_max":723,"o":"m 474 0 l 279 0 l 24 728 l 189 728 l 306 376 q 348 230 327 310 l 348 230 q 377 120 362 178 l 377 120 q 405 231 392 179 l 405 231 q 446 376 426 310 l 446 376 l 558 728 l 723 728 l 474 0"},"W":{"ha":1038,"x_min":24,"x_max":1014,"o":"m 390 0 l 219 0 l 24 728 l 187 728 l 271 370 q 290 274 281 325 q 307 171 298 223 l 307 171 q 310 149 309 160 l 310 149 q 314 171 312 160 l 314 171 q 332 274 323 223 q 353 370 341 325 l 353 370 l 441 728 l 596 728 l 685 370 q 705 274 696 325 q 724 171 714 223 l 724 171 q 727 151 725 161 l 727 151 q 730 171 729 161 l 730 171 q 747 274 738 223 q 766 370 756 325 l 766 370 l 851 728 l 1014 728 l 818 0 l 648 0 l 551 372 q 525 506 536 434 l 525 506 q 518 548 521 527 l 518 548 q 513 509 515 529 l 513 509 q 486 372 503 438 l 486 372 l 390 0"},"X":{"ha":738,"x_min":25,"x_max":713,"o":"m 197 0 l 25 0 l 286 372 l 48 728 l 221 728 l 301 605 q 338 543 323 571 q 364 490 352 516 l 364 490 q 371 476 367 483 l 371 476 q 378 490 375 483 l 378 490 q 405 543 390 515 q 442 605 419 571 l 442 605 l 523 728 l 692 728 l 457 376 l 713 0 l 537 0 l 436 152 q 403 205 416 183 q 381 246 391 226 l 381 246 q 369 267 375 255 l 369 267 q 358 246 363 256 l 358 246 q 335 205 348 227 q 302 152 322 183 l 302 152 l 197 0"},"Y":{"ha":731,"x_min":24,"x_max":707,"o":"m 442 0 l 293 0 l 293 273 l 24 728 l 198 728 l 321 501 q 355 433 340 467 l 355 433 q 369 399 362 417 l 369 399 q 382 434 375 418 l 382 434 q 414 501 396 467 l 414 501 l 533 728 l 707 728 l 442 273 l 442 0"},"Z":{"ha":664,"x_min":55,"x_max":609,"o":"m 609 0 l 56 0 l 56 89 l 351 506 q 411 583 378 544 l 411 583 q 432 606 421 594 l 432 606 q 391 605 411 605 l 391 605 q 292 604 341 604 l 292 604 l 55 604 l 55 728 l 608 728 l 608 638 l 318 229 q 256 149 290 188 l 256 149 q 232 122 244 135 l 232 122 q 278 123 255 123 l 278 123 q 380 124 329 124 l 380 124 l 609 124 l 609 0"},"[":{"ha":377,"x_min":89,"x_max":327,"o":"m 327 -136 l 89 -136 l 89 772 l 327 772 l 327 661 l 230 661 l 230 -25 l 327 -25 l 327 -136"},"\\":{"ha":388,"x_min":14,"x_max":375,"o":"m 375 -109 l 248 -109 l 14 762 l 141 762 l 375 -109"},"]":{"ha":377,"x_min":50,"x_max":289,"o":"m 289 -136 l 50 -136 l 50 -25 l 147 -25 l 147 661 l 50 661 l 50 772 l 289 772 l 289 -136"},"^":{"ha":487,"x_min":34,"x_max":453,"o":"m 146 397 l 34 397 l 184 710 l 303 710 l 453 397 l 340 397 l 247 611 l 240 611 l 146 397"},"_":{"ha":476,"x_min":0,"x_max":476,"o":"m 0 0 l 476 0 l 476 -113 l 0 -113 l 0 0"},"`":{"ha":365,"x_min":79,"x_max":282,"o":"m 282 615 l 179 615 l 79 767 l 214 767 l 282 615"},"a":{"ha":581,"x_min":33,"x_max":518,"o":"m 215 -11 l 215 -11 q 122 8 163 -11 q 57 62 81 26 q 33 153 33 99 l 33 153 q 50 229 33 198 q 96 278 67 260 q 162 307 125 297 q 239 320 199 316 l 239 320 q 316 329 287 325 q 358 343 345 333 q 372 369 372 352 l 372 369 l 372 372 q 362 411 372 395 q 333 436 352 427 q 286 444 313 444 l 286 444 q 237 436 258 444 q 203 413 216 427 q 183 381 189 399 l 183 381 l 49 403 q 96 484 63 451 q 177 535 128 518 q 287 553 226 553 l 287 553 q 372 542 331 553 q 447 510 414 532 q 499 452 479 488 q 518 368 518 417 l 518 368 l 518 0 l 379 0 l 379 76 l 375 76 q 339 31 361 50 q 286 0 317 12 q 215 -11 255 -11 m 256 92 l 256 92 q 317 106 291 92 q 358 143 343 120 q 373 195 373 167 l 373 195 l 373 254 q 353 246 366 250 q 324 239 340 242 q 292 233 308 236 q 264 229 276 231 l 264 229 q 217 217 237 226 q 185 194 196 208 q 174 157 174 179 l 174 157 q 185 121 174 136 q 214 100 195 107 q 256 92 232 92"},"b":{"ha":630,"x_min":63,"x_max":590,"o":"m 368 -9 l 368 -9 q 293 6 324 -9 q 243 43 262 21 q 213 87 224 65 l 213 87 l 207 87 l 207 0 l 63 0 l 63 728 l 209 728 l 209 454 l 213 454 q 242 499 224 476 q 292 537 261 521 q 369 553 323 553 l 369 553 q 480 521 430 553 q 560 428 530 490 q 590 272 590 365 l 590 272 q 561 118 590 181 q 481 23 532 56 q 368 -9 431 -9 m 323 108 l 323 108 q 387 129 361 108 q 427 188 414 150 q 440 272 440 225 l 440 272 q 427 357 440 320 q 388 415 414 394 q 323 436 362 436 l 323 436 q 259 415 286 436 q 219 359 233 395 q 206 272 206 322 l 206 272 q 219 186 206 223 q 260 129 233 149 q 323 108 286 108"},"c":{"ha":588,"x_min":40,"x_max":550,"o":"m 307 -11 l 307 -11 q 164 25 224 -11 q 72 123 104 60 q 40 271 40 187 l 40 271 q 72 419 40 355 q 164 517 104 482 q 307 553 224 553 l 307 553 q 396 540 355 553 q 469 504 437 527 q 521 447 500 480 q 549 371 542 413 l 549 371 l 413 346 q 399 384 409 367 q 377 413 390 401 q 346 431 364 425 q 308 438 329 438 l 308 438 q 242 417 269 438 q 202 358 216 396 q 189 271 189 320 l 189 271 q 202 185 189 223 q 242 126 216 147 q 308 104 269 104 l 308 104 q 347 111 329 104 q 378 130 364 118 q 401 160 392 143 q 414 200 410 178 l 414 200 l 550 175 q 522 97 542 131 q 470 39 501 63 q 397 2 438 15 q 307 -11 355 -11"},"d":{"ha":630,"x_min":40,"x_max":568,"o":"m 262 -9 l 262 -9 q 149 23 199 -9 q 69 118 98 56 q 40 272 40 181 l 40 272 q 70 428 40 365 q 150 521 100 490 q 261 553 201 553 l 261 553 q 338 537 308 553 q 388 499 369 521 q 417 454 407 476 l 417 454 l 421 454 l 421 728 l 568 728 l 568 0 l 424 0 l 424 87 l 417 87 q 387 43 407 65 q 337 6 368 21 q 262 -9 307 -9 m 307 108 l 307 108 q 371 129 344 108 q 411 186 397 149 q 424 272 424 223 l 424 272 q 411 359 424 322 q 371 415 397 395 q 307 436 345 436 l 307 436 q 242 415 269 436 q 203 357 216 394 q 190 272 190 320 l 190 272 q 203 188 190 225 q 243 129 217 150 q 307 108 269 108"},"e":{"ha":596,"x_min":40,"x_max":558,"o":"m 309 -11 l 309 -11 q 165 23 226 -11 q 72 121 104 58 q 40 270 40 184 l 40 270 q 72 418 40 354 q 163 517 104 481 q 302 553 222 553 l 302 553 q 402 536 355 553 q 483 484 448 519 q 538 398 519 450 q 558 276 558 346 l 558 276 l 558 235 l 185 235 q 201 164 186 193 l 201 164 q 245 116 216 132 q 312 100 273 100 l 312 100 q 358 107 337 100 q 394 129 379 114 q 417 164 409 143 l 417 164 l 550 139 q 504 60 537 94 q 422 8 471 26 q 309 -11 373 -11 m 186 327 l 186 327 l 417 327 q 405 376 414 354 l 405 376 q 366 425 392 408 q 303 442 341 442 l 303 442 q 239 425 265 442 q 198 377 212 407 l 198 377 q 186 327 188 354"},"f":{"ha":398,"x_min":10,"x_max":388,"o":"m 252 546 l 366 546 l 366 434 l 252 434 l 252 0 l 106 0 l 106 434 l 10 434 l 10 546 l 106 546 l 106 588 q 129 685 106 646 q 192 742 152 723 q 283 762 232 762 l 283 762 q 346 756 318 762 q 388 747 375 750 l 388 747 l 362 636 q 340 641 353 639 q 313 644 328 644 l 313 644 q 266 628 280 644 q 252 583 252 612 l 252 583 l 252 546"},"g":{"ha":632,"x_min":40,"x_max":569,"o":"m 305 -216 l 305 -216 q 186 -198 237 -216 q 104 -151 135 -181 q 60 -82 72 -121 l 60 -82 l 186 -47 q 207 -76 192 -62 q 245 -100 221 -91 q 304 -109 269 -109 l 304 -109 q 392 -84 360 -109 q 424 -4 424 -58 l 424 -4 l 424 97 l 413 97 q 385 54 403 75 q 336 21 366 34 q 262 8 306 8 l 262 8 q 150 37 200 8 q 70 125 100 65 q 40 274 40 184 l 40 274 q 70 428 40 366 q 151 521 101 490 q 262 553 202 553 l 262 553 q 339 537 308 553 q 390 499 371 522 q 419 454 409 476 l 419 454 l 425 454 l 425 546 l 569 546 l 569 4 q 535 -118 569 -69 q 442 -191 501 -167 q 305 -216 382 -216 m 308 119 l 308 119 q 371 138 345 119 q 411 191 397 156 q 425 275 425 226 l 425 275 q 411 359 425 323 q 371 416 397 396 q 308 436 345 436 l 308 436 q 243 415 269 436 q 203 358 217 394 q 190 275 190 322 l 190 275 q 203 192 190 227 q 243 138 217 157 q 308 119 270 119"},"h":{"ha":623,"x_min":63,"x_max":561,"o":"m 209 316 l 209 316 l 209 0 l 63 0 l 63 728 l 206 728 l 206 441 q 260 516 227 487 l 260 516 q 373 553 303 553 l 373 553 q 471 528 429 553 q 537 457 514 503 q 561 347 561 411 l 561 347 l 561 0 l 414 0 l 414 322 q 387 401 414 373 q 315 430 361 430 l 315 430 q 260 417 284 430 q 222 378 236 403 q 209 316 209 353"},"i":{"ha":271,"x_min":56,"x_max":216,"o":"m 209 0 l 63 0 l 63 546 l 209 546 l 209 0 m 136 617 l 136 617 q 79 639 103 617 q 56 692 56 661 l 56 692 q 79 746 56 724 q 136 768 103 768 l 136 768 q 193 746 169 768 q 216 692 216 724 l 216 692 q 193 639 216 661 q 136 617 169 617"},"j":{"ha":271,"x_min":-24,"x_max":216,"o":"m 62 -26 l 62 546 l 209 546 l 209 -28 q 185 -129 209 -90 q 115 -186 160 -168 q 5 -204 69 -204 l 5 -204 l -24 -204 l -24 -89 l -5 -89 q 47 -73 32 -89 q 62 -26 62 -57 l 62 -26 m 136 617 l 136 617 q 79 639 103 617 q 56 692 56 661 l 56 692 q 79 746 56 724 q 136 768 103 768 l 136 768 q 193 746 169 768 q 216 692 216 724 l 216 692 q 193 639 216 661 q 136 617 169 617"},"k":{"ha":580,"x_min":63,"x_max":576,"o":"m 209 0 l 63 0 l 63 728 l 209 728 l 209 336 l 217 336 l 396 546 l 565 546 l 361 309 l 576 0 l 403 0 l 251 222 l 209 175 l 209 0"},"l":{"ha":271,"x_min":63,"x_max":209,"o":"m 63 728 l 209 728 l 209 0 l 63 0 l 63 728"},"m":{"ha":913,"x_min":63,"x_max":850,"o":"m 209 0 l 63 0 l 63 546 l 198 546 l 204 440 q 232 492 215 470 l 232 492 q 290 539 257 524 q 360 554 323 554 l 360 554 q 455 517 419 554 l 455 517 q 500 432 484 487 l 500 432 q 531 488 512 464 l 531 488 q 596 538 559 521 q 676 554 634 554 l 676 554 q 765 532 726 554 q 827 469 805 510 q 850 368 850 428 l 850 368 l 850 0 l 704 0 l 704 340 q 678 409 704 387 q 617 432 653 432 l 617 432 q 569 420 589 432 q 538 386 549 408 q 527 336 527 365 l 527 336 l 527 0 l 385 0 l 385 344 q 362 408 385 384 q 300 432 338 432 l 300 432 q 254 420 274 432 q 221 385 233 408 q 209 330 209 363 l 209 330 l 209 0"},"n":{"ha":623,"x_min":63,"x_max":560,"o":"m 209 316 l 209 316 l 209 0 l 63 0 l 63 546 l 201 546 l 203 433 q 260 516 224 485 l 260 516 q 373 553 303 553 l 373 553 q 471 528 429 553 q 537 457 514 503 q 560 347 560 411 l 560 347 l 560 0 l 414 0 l 414 322 q 387 401 414 373 q 315 430 361 430 l 315 430 q 260 417 284 430 q 222 378 236 403 q 209 316 209 353"},"o":{"ha":613,"x_min":40,"x_max":574,"o":"m 307 -11 l 307 -11 q 165 25 225 -11 q 72 123 104 60 q 40 271 40 187 l 40 271 q 72 419 40 355 q 165 517 104 482 q 307 553 225 553 l 307 553 q 449 517 389 553 q 541 419 509 482 q 574 271 574 355 l 574 271 q 541 123 574 187 q 449 25 509 60 q 307 -11 389 -11 m 307 104 l 307 104 q 372 126 346 104 q 412 186 398 148 q 425 271 425 224 l 425 271 q 412 357 425 319 q 372 416 398 395 q 307 438 346 438 l 307 438 q 241 416 268 438 q 202 357 215 395 q 189 271 189 319 l 189 271 q 202 186 189 224 q 241 126 215 148 q 307 104 268 104"},"p":{"ha":630,"x_min":63,"x_max":590,"o":"m 209 -204 l 63 -204 l 63 546 l 207 546 l 207 454 l 213 454 q 242 499 224 476 q 292 537 261 521 q 369 553 323 553 l 369 553 q 480 521 430 553 q 560 428 530 490 q 590 272 590 365 l 590 272 q 561 118 590 181 q 481 23 532 56 q 368 -9 431 -9 l 368 -9 q 293 6 324 -9 q 243 43 262 21 q 213 87 224 65 l 213 87 l 209 87 l 209 -204 m 323 108 l 323 108 q 387 129 361 108 q 427 188 414 150 q 440 272 440 225 l 440 272 q 427 357 440 320 q 388 415 414 394 q 323 436 362 436 l 323 436 q 259 415 286 436 q 219 359 233 395 q 206 272 206 322 l 206 272 q 219 186 206 223 q 260 129 233 149 q 323 108 286 108"},"q":{"ha":630,"x_min":40,"x_max":568,"o":"m 568 546 l 568 -204 l 421 -204 l 421 87 l 417 87 q 387 43 407 65 q 337 6 368 21 q 262 -9 307 -9 l 262 -9 q 149 23 199 -9 q 69 118 98 56 q 40 272 40 181 l 40 272 q 70 428 40 365 q 150 521 100 490 q 261 553 201 553 l 261 553 q 338 537 308 553 q 388 499 369 521 q 417 454 407 476 l 417 454 l 424 454 l 424 546 l 568 546 m 307 108 l 307 108 q 371 129 344 108 q 411 186 397 149 q 424 272 424 223 l 424 272 q 411 359 424 322 q 371 415 397 395 q 307 436 345 436 l 307 436 q 242 415 269 436 q 203 357 216 394 q 190 272 190 320 l 190 272 q 203 188 190 225 q 243 129 217 150 q 307 108 269 108"},"r":{"ha":407,"x_min":63,"x_max":389,"o":"m 209 0 l 63 0 l 63 546 l 204 546 l 204 451 l 210 451 q 260 527 225 501 q 342 553 296 553 l 342 553 q 366 552 353 553 q 389 548 379 551 l 389 548 l 389 417 q 361 423 379 421 q 326 425 342 425 l 326 425 q 266 410 292 425 q 224 370 239 396 q 209 311 209 345 l 209 311 l 209 0"},"s":{"ha":560,"x_min":38,"x_max":523,"o":"m 279 -11 l 279 -11 q 163 8 213 -11 q 80 63 112 27 q 38 149 47 99 l 38 149 l 174 173 q 212 116 185 135 q 284 97 239 97 l 284 97 q 350 113 326 97 q 374 153 374 128 l 374 153 q 357 188 374 174 q 304 209 339 202 l 304 209 l 209 229 q 91 283 130 245 q 52 383 52 322 l 52 383 q 81 474 52 436 q 161 532 110 512 q 282 553 212 553 l 282 553 q 394 535 346 553 q 470 484 441 517 q 509 407 500 452 l 509 407 l 379 384 q 347 430 371 412 q 284 448 324 448 l 284 448 q 223 433 248 448 q 199 392 199 417 l 199 392 q 215 357 199 372 q 269 335 230 343 l 269 335 l 367 316 q 485 263 446 300 q 523 168 523 227 l 523 168 q 492 74 523 115 q 406 12 461 34 q 279 -11 351 -11"},"t":{"ha":366,"x_min":10,"x_max":349,"o":"m 231 546 l 333 546 l 333 434 l 231 434 l 231 160 q 243 121 231 134 q 281 109 254 109 l 281 109 q 305 111 290 109 q 328 115 320 113 l 328 115 l 349 5 q 300 -5 324 -2 q 254 -8 276 -8 l 254 -8 q 128 32 172 -8 q 85 147 85 72 l 85 147 l 85 434 l 10 434 l 10 546 l 85 546 l 85 676 l 231 676 l 231 546"},"u":{"ha":623,"x_min":63,"x_max":561,"o":"m 250 -7 l 250 -7 q 151 18 194 -7 q 86 89 109 42 q 63 199 63 135 l 63 199 l 63 546 l 209 546 l 209 224 q 235 145 209 173 q 308 116 262 116 l 308 116 q 363 129 339 116 q 400 168 387 143 q 414 230 414 194 l 414 230 l 414 546 l 561 546 l 561 0 l 422 0 l 420 114 q 363 30 399 61 l 363 30 q 250 -7 318 -7"},"v":{"ha":600,"x_min":15,"x_max":584,"o":"m 383 0 l 216 0 l 15 546 l 171 546 l 263 263 q 292 155 279 209 l 292 155 q 301 113 296 134 l 301 113 q 311 155 306 134 l 311 155 q 340 263 323 209 l 340 263 l 431 546 l 584 546 l 383 0"},"w":{"ha":850,"x_min":15,"x_max":835,"o":"m 325 0 l 176 0 l 15 546 l 169 546 l 216 344 q 243 227 229 290 l 243 227 q 261 134 252 183 l 261 134 q 280 225 270 182 l 280 225 q 308 344 295 289 l 308 344 l 358 546 l 493 546 l 542 344 q 569 226 554 289 l 569 226 q 588 134 579 182 l 588 134 q 606 225 597 182 l 606 225 q 632 344 619 289 l 632 344 l 679 546 l 835 546 l 673 0 l 524 0 l 463 210 q 446 281 455 242 q 429 359 437 319 l 429 359 q 425 377 427 368 l 425 377 q 421 359 423 368 l 421 359 q 403 280 412 319 q 386 210 395 242 l 386 210 l 325 0"},"x":{"ha":580,"x_min":21,"x_max":560,"o":"m 176 0 l 21 0 l 196 280 l 31 546 l 189 546 l 239 460 q 281 376 262 417 l 281 376 q 292 353 286 365 l 292 353 q 302 376 297 364 l 302 376 q 345 460 321 417 l 345 460 l 396 546 l 552 546 l 383 278 l 560 0 l 402 0 l 342 103 q 299 186 318 145 l 299 186 q 288 208 293 197 l 288 208 q 278 186 283 197 l 278 186 q 236 103 260 145 l 236 103 l 176 0"},"y":{"ha":602,"x_min":15,"x_max":586,"o":"m 59 -193 l 59 -193 l 92 -83 l 110 -87 q 163 -92 140 -95 q 199 -73 186 -88 q 216 -31 213 -58 l 216 -31 l 220 -1 l 15 546 l 171 546 l 263 263 q 290 155 279 209 l 290 155 q 297 119 293 137 l 297 119 q 306 156 302 137 l 306 156 q 337 263 319 209 l 337 263 l 433 546 l 586 546 l 354 -64 q 312 -141 338 -108 q 248 -190 286 -173 q 154 -208 209 -208 l 154 -208 q 100 -204 125 -208 q 59 -193 74 -200"},"z":{"ha":573,"x_min":59,"x_max":514,"o":"m 514 0 l 59 0 l 59 89 l 328 424 l 328 428 l 67 428 l 67 546 l 505 546 l 505 449 l 251 122 l 251 118 l 514 118 l 514 0"},"{":{"ha":469,"x_min":68,"x_max":419,"o":"m 419 -26 l 419 -136 q 321 -129 366 -136 q 244 -102 276 -122 q 195 -42 212 -81 q 177 63 177 -2 l 177 63 l 177 147 q 153 227 177 202 l 153 227 q 85 251 133 249 l 85 251 l 68 251 l 68 384 l 85 384 q 153 409 133 387 l 153 409 q 177 488 177 433 l 177 488 l 177 573 q 195 677 177 638 q 244 737 212 717 q 321 765 276 757 q 419 772 366 772 l 419 772 l 419 661 q 334 636 356 661 q 313 559 313 612 l 313 559 l 313 453 q 304 400 313 426 q 269 354 294 375 l 269 354 q 198 323 244 335 l 198 323 l 198 313 q 269 281 244 301 l 269 281 q 304 235 294 261 q 313 183 313 209 l 313 183 l 313 77 q 334 -1 313 24 q 419 -26 356 -26 l 419 -26"},"|":{"ha":372,"x_min":116,"x_max":255,"o":"m 116 962 l 255 962 l 255 -234 l 116 -234 l 116 962"},"}":{"ha":469,"x_min":50,"x_max":400,"o":"m 50 -136 l 50 -136 l 50 -26 q 135 -1 113 -26 q 156 77 156 24 l 156 77 l 156 183 q 165 235 156 209 q 200 281 174 261 l 200 281 q 271 313 225 301 l 271 313 l 271 323 q 200 354 225 335 l 200 354 q 165 400 174 375 q 156 453 156 426 l 156 453 l 156 559 q 135 636 156 612 q 50 661 113 661 l 50 661 l 50 772 q 148 765 104 772 q 225 737 192 757 q 274 677 257 717 q 292 573 292 638 l 292 573 l 292 488 q 316 409 292 433 l 316 409 q 398 384 339 384 l 398 384 l 400 384 l 400 252 q 315 227 339 251 q 292 147 292 202 l 292 147 l 292 63 q 274 -42 292 -2 q 225 -102 257 -81 q 148 -129 192 -122 q 50 -136 104 -136"},"~":{"ha":679,"x_min":76,"x_max":602,"o":"m 190 198 l 77 198 q 94 311 75 265 q 147 380 112 356 q 229 403 183 403 l 229 403 q 293 389 263 403 q 363 341 324 375 l 363 341 q 398 313 384 323 q 431 304 412 304 l 431 304 q 473 324 457 304 q 488 386 489 344 l 488 386 l 602 386 q 585 273 604 319 q 530 204 565 228 q 449 181 495 181 l 449 181 q 383 196 414 181 q 315 243 352 211 l 315 243 q 279 271 292 263 q 248 280 266 280 l 248 280 q 206 261 223 280 q 190 198 189 242 l 190 198"},"£":{"ha":639,"x_min":46,"x_max":596,"o":"m 438 0 l 46 0 l 46 124 q 108 143 86 124 q 128 200 130 163 l 128 200 l 122 316 l 46 316 l 46 411 l 118 411 l 114 490 q 140 623 109 567 q 226 708 170 678 q 354 738 282 738 l 354 738 q 467 715 417 738 q 549 647 517 691 q 591 543 582 603 l 591 543 l 454 521 q 423 590 447 566 q 357 614 398 614 l 357 614 q 298 600 321 614 q 264 560 274 586 q 254 498 253 534 l 254 498 l 257 411 l 449 411 l 449 316 l 260 316 l 264 222 q 247 161 265 185 q 201 125 229 138 l 201 125 q 197 124 199 125 l 197 124 l 433 124 q 454 131 447 124 q 460 151 460 137 l 460 151 l 460 178 l 596 178 l 596 151 q 556 39 596 77 q 438 0 516 0 l 438 0"},"€":{"ha":685,"x_min":28,"x_max":630,"o":"m 283 476 l 538 476 l 503 399 l 271 399 q 270 363 270 382 l 270 363 q 271 328 270 345 l 271 328 l 470 328 l 433 249 l 283 249 q 291 226 286 237 l 291 226 q 348 148 311 173 q 436 123 386 123 l 436 123 q 493 131 467 123 q 538 151 519 140 q 571 173 558 163 l 571 173 l 627 55 q 540 7 589 23 q 436 -10 491 -10 l 436 -10 q 272 34 343 -10 q 160 162 201 79 l 160 162 q 131 249 141 202 l 131 249 l 28 249 l 57 328 l 121 328 q 120 363 120 345 l 120 363 q 121 399 120 381 l 121 399 l 28 399 l 57 476 l 130 476 q 160 564 141 524 l 160 564 q 272 693 201 648 q 436 737 343 737 l 436 737 q 541 720 492 737 q 630 671 590 704 l 630 671 l 574 552 q 540 575 560 563 q 493 595 519 587 q 436 604 467 604 l 436 604 q 348 578 386 604 q 291 500 311 553 l 291 500 q 283 476 286 488 l 283 476"},"©":{"ha":914,"x_min":82,"x_max":832,"o":"m 259 363 l 259 363 q 286 465 259 419 q 359 538 313 510 q 460 565 405 565 l 460 565 q 551 543 509 565 q 618 486 592 522 q 651 403 645 449 l 651 403 l 547 403 q 517 448 542 431 q 460 466 492 466 l 460 466 q 409 452 433 466 q 372 415 386 438 q 358 364 358 392 l 358 364 q 372 313 358 336 q 409 276 386 290 q 460 262 433 262 l 460 262 q 515 279 492 262 q 544 324 538 297 l 544 324 l 647 324 q 616 241 642 278 q 550 184 590 205 q 460 163 509 163 l 460 163 q 359 190 405 163 q 286 262 313 217 q 259 363 259 308 m 457 -11 l 457 -11 q 312 18 379 -11 q 192 99 244 47 q 111 218 140 150 q 82 364 82 286 l 82 364 q 111 509 82 441 q 192 629 140 577 q 312 710 244 681 q 457 739 379 739 l 457 739 q 603 710 535 739 q 722 629 670 681 q 803 509 774 577 q 832 364 832 441 l 832 364 q 803 218 832 286 q 722 99 774 150 q 603 18 670 47 q 457 -11 535 -11 m 457 104 l 457 104 q 558 125 511 104 q 640 180 604 145 q 696 263 676 216 q 716 364 716 310 l 716 364 q 696 464 716 417 q 640 547 676 511 q 558 603 604 583 q 457 623 511 623 l 457 623 q 356 603 403 623 q 274 547 310 583 q 218 464 238 511 q 198 364 198 417 l 198 364 q 218 263 198 310 q 274 180 238 216 q 356 125 310 145 q 457 104 403 104"},"®":{"ha":662,"x_min":47,"x_max":615,"o":"m 331 169 l 331 169 q 221 191 272 169 q 130 252 169 213 q 69 343 91 292 q 47 453 47 395 q 69 563 47 512 q 130 654 91 615 q 221 715 169 693 q 331 737 272 737 l 331 737 q 442 715 390 737 q 532 654 493 693 q 593 563 571 615 q 615 453 615 512 q 593 343 615 395 q 532 252 571 292 q 442 191 493 213 q 331 169 390 169 m 331 244 l 331 244 q 437 272 389 244 q 512 348 484 300 q 541 453 541 395 q 512 559 541 511 q 437 634 484 606 q 331 663 389 663 l 331 663 q 226 634 273 663 q 150 559 178 606 q 122 453 122 511 l 122 453 q 150 348 122 396 q 226 272 178 300 q 331 244 273 244 m 456 324 l 381 324 l 340 413 l 296 413 l 296 324 l 229 324 l 229 586 l 357 586 q 402 576 381 586 q 435 547 422 566 q 447 500 447 528 q 435 453 447 472 l 435 453 q 406 425 423 435 l 406 425 l 456 324 m 296 537 l 296 457 l 339 457 q 365 467 354 457 q 376 499 376 478 l 376 499 q 365 528 376 519 q 340 537 354 537 l 340 537 l 296 537"},"°":{"ha":459,"x_min":56,"x_max":403,"o":"m 230 385 l 230 385 q 143 408 182 385 q 80 471 103 432 q 56 558 56 511 l 56 558 q 80 646 56 606 q 143 709 103 686 q 230 732 182 732 l 230 732 q 317 709 278 732 q 380 646 356 686 q 403 558 403 606 l 403 558 q 380 471 403 511 q 317 408 356 432 q 230 385 278 385 m 230 483 l 230 483 q 268 494 251 484 q 295 521 285 504 q 305 558 305 538 l 305 558 q 295 596 305 579 q 268 623 285 613 q 230 633 250 633 l 230 633 q 192 623 209 633 q 165 596 175 613 q 155 559 155 579 l 155 559 q 165 521 155 538 q 192 494 175 504 q 230 483 209 484"},"±":{"ha":679,"x_min":90,"x_max":588,"o":"m 277 323 l 90 323 l 90 437 l 277 437 l 277 562 l 401 562 l 401 437 l 588 437 l 588 323 l 401 323 l 401 198 l 277 198 l 277 323 m 588 28 l 90 28 l 90 146 l 588 146 l 588 28"},"×":{"ha":679,"x_min":83,"x_max":597,"o":"m 597 124 l 506 34 l 340 199 l 174 34 l 83 124 l 250 290 l 83 456 l 174 547 l 340 381 l 506 547 l 597 456 l 431 290 l 597 124"},"÷":{"ha":679,"x_min":92,"x_max":587,"o":"m 92 350 l 587 350 l 587 231 l 92 231 l 92 350 m 339 10 l 339 10 q 280 35 304 10 q 255 95 255 59 l 255 95 q 280 153 255 128 q 339 177 304 177 l 339 177 q 398 153 374 177 q 423 95 423 128 l 423 95 q 398 35 423 59 q 339 10 374 10 m 339 404 l 339 404 q 280 428 305 403 q 255 488 255 453 l 255 488 q 280 547 255 522 q 339 571 305 571 l 339 571 q 398 547 374 571 q 423 488 423 522 l 423 488 q 398 428 423 453 q 339 404 374 403"},"–":{"ha":500,"x_min":0,"x_max":500,"o":"m 0 358 l 500 358 l 500 241 l 0 241 l 0 358"},"—":{"ha":1000,"x_min":0,"x_max":1000,"o":"m 0 358 l 1000 358 l 1000 241 l 0 241 l 0 358"},"‘":{"ha":311,"x_min":81,"x_max":270,"o":"m 227 425 l 81 425 l 170 728 l 270 728 l 227 425"},"’":{"ha":311,"x_min":81,"x_max":270,"o":"m 180 425 l 81 425 l 124 728 l 270 728 l 180 425"},"“":{"ha":540,"x_min":81,"x_max":500,"o":"m 227 425 l 81 425 l 170 728 l 270 728 l 227 425 m 457 425 l 310 425 l 400 728 l 500 728 l 457 425"},"”":{"ha":532,"x_min":81,"x_max":491,"o":"m 180 425 l 81 425 l 124 728 l 270 728 l 180 425 m 401 425 l 302 425 l 345 728 l 491 728 l 401 425"},"…":{"ha":1002,"x_min":83,"x_max":919,"o":"m 167 -9 l 167 -9 q 107 15 131 -9 q 83 75 83 39 l 83 75 q 107 135 83 110 q 167 159 131 159 l 167 159 q 227 135 203 159 q 251 75 251 110 l 251 75 q 227 15 251 39 q 167 -9 203 -9 m 501 -9 l 501 -9 q 441 15 465 -9 q 417 75 417 39 l 417 75 q 441 135 417 110 q 501 159 465 159 l 501 159 q 561 135 537 159 q 585 75 585 110 l 585 75 q 561 15 585 39 q 501 -9 537 -9 m 835 -9 l 835 -9 q 775 15 799 -9 q 750 75 750 39 l 750 75 q 775 135 750 110 q 835 159 799 159 l 835 159 q 895 135 871 159 q 919 75 919 110 l 919 75 q 895 15 919 39 q 835 -9 871 -9"},"•":{"ha":474,"x_min":81,"x_max":393,"o":"m 237 134 l 237 134 q 158 155 194 134 q 102 211 123 176 q 81 290 81 247 l 81 290 q 102 369 81 333 q 158 425 123 404 q 237 446 194 446 l 237 446 q 316 425 280 446 q 372 369 351 404 q 393 290 393 333 l 393 290 q 372 211 393 247 q 316 155 351 176 q 237 134 280 134"},"·":{"ha":334,"x_min":83,"x_max":251,"o":"m 167 261 l 167 261 q 107 285 131 261 q 83 345 83 310 l 83 345 q 107 405 83 381 q 167 429 131 429 l 167 429 q 227 405 203 429 q 251 345 251 381 l 251 345 q 227 285 251 310 q 167 261 203 261"}},"familyName":"Inter","ascender":969,"descender":-241,"underlinePosition":-100,"underlineThickness":50,"boundingBox":{"yMin":-241,"xMin":-200,"yMax":969,"xMax":1400},"resolution":1000,"original_font_information":{"copyright":"Copyright 2016 The Inter Project Authors (https://github.com/rsms/inter)","fullName":"Inter Bold","license":"SIL Open Font License, Version 1.1"}};
|
|
@@ -42,7 +42,7 @@ const springOf = (o: Timing): SpringConfig => ({ stiffness: o.stiffness ?? sprin
|
|
|
42
42
|
// Where a value stands at `frame` among sorted keys. The move toward key N starts `lead` frames before key N's frame (or at key N-1's
|
|
43
43
|
// frame when they are closer than that) and ends on key N's frame; before and after a move the value holds. It returns the two keys
|
|
44
44
|
// the frame is between and how far along the move is (from === to when it is holding).
|
|
45
|
-
function locate<K extends { frame: number }>(sorted: K[], frame: number, fps: number, o: Timing): { from: K; to: K; p: number } {
|
|
45
|
+
export function locate<K extends { frame: number }>(sorted: K[], frame: number, fps: number, o: Timing): { from: K; to: K; p: number } {
|
|
46
46
|
const lead = Math.max(1, o.lead ?? DEFAULT_LEAD);
|
|
47
47
|
let from = sorted[0]!;
|
|
48
48
|
for (let j = 1; j < sorted.length; j++) {
|
|
@@ -55,7 +55,7 @@ function locate<K extends { frame: number }>(sorted: K[], frame: number, fps: nu
|
|
|
55
55
|
return { from, to: from, p: 0 };
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const mix = (a: number, b: number, p: number) => a + (b - a) * p;
|
|
58
|
+
export const mix = (a: number, b: number, p: number) => a + (b - a) * p;
|
|
59
59
|
|
|
60
60
|
export type BoxKey = { frame: number; x: number; y: number; width: number; height: number; radius?: number; opacity?: number; rotate?: number };
|
|
61
61
|
export type Box = { x: number; y: number; width: number; height: number; radius: number; opacity: number; rotate: number };
|
|
@@ -111,3 +111,37 @@ export function cameraTransform(c: CameraView, w: number, h: number): { origin:
|
|
|
111
111
|
transform: `translate(${px((0.5 - c.x) * w)}, ${px((0.5 - c.y) * h)}) rotate(${Math.round(c.rotate * 1000) / 1000}deg) scale(${Math.round(c.zoom * 10000) / 10000})`,
|
|
112
112
|
};
|
|
113
113
|
}
|
|
114
|
+
|
|
115
|
+
// A punch: a quick push of 8 to 15 percent onto a point, for emphasis on a word or a click. It rises over `frames` frames with a small overshoot,
|
|
116
|
+
// holds for `hold` frames and eases back over `release`. The zoom is about the point itself, so the point stays where it is on screen.
|
|
117
|
+
export type Punch = { frame: number; x: number; y: number; amount?: number; frames?: number; hold?: number; release?: number };
|
|
118
|
+
|
|
119
|
+
// 0 before the punch, 1 at its full push, with a little over 1 on the way in (an ease-out-back with a small constant).
|
|
120
|
+
export function punchEnvelope(p: Punch, frame: number): number {
|
|
121
|
+
const rise = Math.max(1, p.frames ?? 5), hold = Math.max(0, p.hold ?? 6), release = Math.max(1, p.release ?? 8);
|
|
122
|
+
const t = frame - p.frame;
|
|
123
|
+
if (t <= 0) return 0;
|
|
124
|
+
if (t < rise) { const u = t / rise - 1, c = 1.1; return 1 + (c + 1) * u ** 3 + c * u ** 2; }
|
|
125
|
+
if (t <= rise + hold) return 1;
|
|
126
|
+
const u = (t - rise - hold) / release;
|
|
127
|
+
return u >= 1 ? 0 : 1 - (3 * u * u - 2 * u * u * u);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// The camera with every punch applied. A punch of strength `e` scales the view by 1 + amount * e about its point.
|
|
131
|
+
export function applyPunches(view: CameraView, punches: readonly Punch[], frame: number): CameraView {
|
|
132
|
+
let { x, y, zoom } = view;
|
|
133
|
+
for (const p of punches) {
|
|
134
|
+
const e = punchEnvelope(p, frame);
|
|
135
|
+
if (e === 0) continue;
|
|
136
|
+
const m = 1 + Math.min(0.5, Math.max(0, p.amount ?? 0.1)) * e;
|
|
137
|
+
// Zooming by m with the point P kept still on screen moves the centre to P - (P - C) / m.
|
|
138
|
+
x = p.x - (p.x - x) / m; y = p.y - (p.y - y) / m; zoom *= m;
|
|
139
|
+
}
|
|
140
|
+
return { ...view, x, y, zoom };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// The camera key that pushes in until an element fills the frame: the element's box, as fractions of the frame (x and y its centre), so that
|
|
144
|
+
// its longer side just reaches the frame's edge. Put it in `keys`, then the next key pulls out or on to the next element.
|
|
145
|
+
export function zoomTo(frame: number, box: { x: number; y: number; width: number; height: number }): CameraKey {
|
|
146
|
+
return { frame, x: box.x, y: box.y, zoom: Math.max(1, Math.min(1 / Math.max(1e-3, box.width), 1 / Math.max(1e-3, box.height))) };
|
|
147
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { WordTiming } from "../../pipeline/schema";
|
|
2
|
+
|
|
3
|
+
// The music is already down this long before a word starts, and stays down this long after it ends.
|
|
4
|
+
export const DUCK_PAD_SEC = 0.25;
|
|
5
|
+
// The music falls over this long before a word (it is down 0.25 s before the word starts) and rises over this long after a pause.
|
|
6
|
+
export const DUCK_FALL_SEC = 0.25;
|
|
7
|
+
export const DUCK_RISE_SEC = 0.4;
|
|
8
|
+
// Only a gap of at least this long between words lets the music come up; a shorter one keeps it steady so it does not pump. With sentences 0.3 s
|
|
9
|
+
// apart the music holds under a whole narrated passage and lifts in real pauses only: before the first word and after the last.
|
|
10
|
+
export const DUCK_GAP_SEC = 1.2;
|
|
11
|
+
// What the music sits at under a voice, and the level it comes up to, as `volume` multipliers: the voice is 1.
|
|
12
|
+
export const DEFAULT_DUCK_TO = 0.18;
|
|
13
|
+
export const DEFAULT_MUSIC_VOLUME = 0.5;
|
|
14
|
+
export const FADE_OUT_SEC = 1;
|
|
15
|
+
// A dip eases in and out over this many frames just outside its range, so the track never jumps.
|
|
16
|
+
export const DIP_RAMP_FRAMES = 6;
|
|
17
|
+
|
|
18
|
+
export type MusicDip = { from: number; to: number; volume: number };
|
|
19
|
+
|
|
20
|
+
export type SpeechScene = { startFrame: number; words: WordTiming[] };
|
|
21
|
+
|
|
22
|
+
// The stretches of the video, in seconds, during which a voice is heard (with the padding around each word); words closer than
|
|
23
|
+
// DUCK_GAP_SEC (less than 1.2 s) apart are one stretch.
|
|
24
|
+
export function speechSpans(scenes: SpeechScene[], fps: number): [number, number][] {
|
|
25
|
+
const words = scenes.flatMap((s) => s.words.map((w) => [s.startFrame / fps + w.startSec, s.startFrame / fps + w.endSec] as const)).sort((a, b) => a[0] - b[0]);
|
|
26
|
+
const spans: [number, number][] = [];
|
|
27
|
+
for (const [start, end] of words) {
|
|
28
|
+
const last = spans[spans.length - 1];
|
|
29
|
+
if (last && start - (last[1] - DUCK_PAD_SEC) < DUCK_GAP_SEC) last[1] = Math.max(last[1], end + DUCK_PAD_SEC);
|
|
30
|
+
else spans.push([start - DUCK_PAD_SEC, end + DUCK_PAD_SEC]);
|
|
31
|
+
}
|
|
32
|
+
return spans;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// How far down the music is at `t` seconds, 0 (not at all) to 1 (fully): full inside a stretch, falling over DUCK_FALL_SEC just before its start and
|
|
36
|
+
// rising over DUCK_RISE_SEC just after its end.
|
|
37
|
+
export function duckAmount(spans: [number, number][], t: number): number {
|
|
38
|
+
let amount = 0;
|
|
39
|
+
for (const [a, b] of spans) {
|
|
40
|
+
const edge = Math.min((t - a) / DUCK_FALL_SEC + 1, (b - t) / DUCK_RISE_SEC + 1);
|
|
41
|
+
amount = Math.max(amount, Math.max(0, Math.min(1, edge)));
|
|
42
|
+
}
|
|
43
|
+
return amount;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// The music's volume at a composition frame: `volume` in gaps, `duckTo` under a voice, and a fade to nothing over the last second.
|
|
47
|
+
// `gain` is the track's levelling (linear). Looping needs no handling here: the frame is the composition's, not the track's.
|
|
48
|
+
export function musicVolume(frame: number, o: { fps: number; totalFrames: number; volume: number; duckTo: number; spans: [number, number][]; gain?: number; dips?: MusicDip[] }): number {
|
|
49
|
+
const t = frame / o.fps;
|
|
50
|
+
const duck = duckAmount(o.spans, t);
|
|
51
|
+
let base = o.volume + (Math.min(o.duckTo, o.volume) - o.volume) * duck;
|
|
52
|
+
// A dip is a rest that the author asked for: inside it the track is held at the dip's volume, with a short ramp at each edge.
|
|
53
|
+
for (const d of o.dips ?? []) {
|
|
54
|
+
const near = Math.max(0, Math.min(1, Math.min(frame - d.from, d.to - frame) / DIP_RAMP_FRAMES + 1));
|
|
55
|
+
if (near > 0) base = Math.min(base, base + (d.volume - base) * near);
|
|
56
|
+
}
|
|
57
|
+
const fade = Math.max(0, Math.min(1, (o.totalFrames - frame) / (FADE_OUT_SEC * o.fps)));
|
|
58
|
+
return base * fade * (o.gain ?? 1);
|
|
59
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// three.js 0.186 prints a deprecation notice for THREE.Clock from inside @react-three/fiber, once per canvas, to the browser console. Renders forward
|
|
2
|
+
// every console line, so that one notice would fill the output of render, preview and check. This drops that exact warning and nothing else.
|
|
3
|
+
const NOTICE = "THREE.Clock: This module has been deprecated";
|
|
4
|
+
const GUARD = "__reelkitQuietThree";
|
|
5
|
+
const g = globalThis as unknown as Record<string, unknown>;
|
|
6
|
+
if (typeof console !== "undefined" && !g[GUARD]) {
|
|
7
|
+
g[GUARD] = true;
|
|
8
|
+
const warn = console.warn.bind(console);
|
|
9
|
+
console.warn = (...args: unknown[]) => { if (typeof args[0] === "string" && args[0].startsWith(NOTICE)) return; warn(...args); };
|
|
10
|
+
}
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { typeface } from "./Text3D";
|
|
2
|
+
import { centred, MAX_PIECES, MAX_POINTS } from "./three-fx-math";
|
|
3
|
+
import { hash01 } from "./seeded";
|
|
4
|
+
import type { Vec3 } from "./three-math";
|
|
5
|
+
|
|
6
|
+
type Poly = { x: number; y: number }[];
|
|
7
|
+
// The outline of every glyph of a word in the kit's typeface, outlines and holes together: a point is inside a letter when it is inside an odd number of them.
|
|
8
|
+
function outlines(text: string): { polys: Poly[]; box: { x0: number; y0: number; x1: number; y1: number } } {
|
|
9
|
+
const polys: Poly[] = [];
|
|
10
|
+
for (const shape of typeface().generateShapes(text, 1)) {
|
|
11
|
+
polys.push(shape.getPoints(10));
|
|
12
|
+
for (const hole of shape.holes) polys.push(hole.getPoints(10));
|
|
13
|
+
}
|
|
14
|
+
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
|
|
15
|
+
for (const poly of polys) for (const p of poly) { x0 = Math.min(x0, p.x); x1 = Math.max(x1, p.x); y0 = Math.min(y0, p.y); y1 = Math.max(y1, p.y); }
|
|
16
|
+
return { polys, box: { x0, y0, x1, y1 } };
|
|
17
|
+
}
|
|
18
|
+
function inside(polys: Poly[], x: number, y: number): boolean {
|
|
19
|
+
let hit = false;
|
|
20
|
+
for (const poly of polys) for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
|
|
21
|
+
const a = poly[i]!, b = poly[j]!;
|
|
22
|
+
if ((a.y > y) !== (b.y > y) && x < ((b.x - a.x) * (y - a.y)) / (b.y - a.y) + a.x) hit = !hit;
|
|
23
|
+
}
|
|
24
|
+
return hit;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// The slots of a short word, as blocks: cells of a grid that fall inside the letters, about `count` of them (never over 4000), centred, the word's width 1.
|
|
28
|
+
export function textSlots(text: string, count: number): Vec3[] {
|
|
29
|
+
const { polys, box } = outlines(text);
|
|
30
|
+
if (!polys.length) return [[0, 0, 0]];
|
|
31
|
+
const w = box.x1 - box.x0, h = box.y1 - box.y0, want = Math.max(8, Math.min(MAX_PIECES, Math.round(count)));
|
|
32
|
+
// The share of the box the letters fill, estimated on a coarse sample, gives the cell size at which about `want` cells land inside.
|
|
33
|
+
let hits = 0;
|
|
34
|
+
const probe = 40;
|
|
35
|
+
for (let a = 0; a < probe; a++) for (let b = 0; b < probe; b++) if (inside(polys, box.x0 + ((a + 0.5) / probe) * w, box.y0 + ((b + 0.5) / probe) * h)) hits++;
|
|
36
|
+
const fill = Math.max(0.05, hits / (probe * probe)), cell = Math.sqrt((w * h * fill) / want);
|
|
37
|
+
const out: Vec3[] = [];
|
|
38
|
+
for (let y = box.y0 + cell / 2; y < box.y1; y += cell) for (let x = box.x0 + cell / 2; x < box.x1; x += cell) if (inside(polys, x, y)) out.push([x, y, 0]);
|
|
39
|
+
const stride = Math.max(1, Math.ceil(out.length / MAX_PIECES));
|
|
40
|
+
return centred(stride > 1 ? out.filter((_, i) => i % stride === 0) : out);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// `count` seeded points inside the letters of a word, as a flat array, centred, the width 1, a little thickness in z.
|
|
44
|
+
export function textPoints(text: string, count: number, seed = 0): Float32Array {
|
|
45
|
+
const { polys, box } = outlines(text), n = Math.max(1, Math.min(MAX_POINTS, Math.round(count))), w = box.x1 - box.x0, h = box.y1 - box.y0;
|
|
46
|
+
const pts: Vec3[] = [];
|
|
47
|
+
for (let i = 0, k = 0; pts.length < n && k < n * 60; k++) {
|
|
48
|
+
const x = box.x0 + hash01(k, seed + 51) * w, y = box.y0 + hash01(k, seed + 52) * h;
|
|
49
|
+
if (inside(polys, x, y)) { pts.push([x, y, (hash01(i++, seed + 53) - 0.5) * 0.12 * w]); }
|
|
50
|
+
}
|
|
51
|
+
while (pts.length < n) pts.push(pts[pts.length % Math.max(1, pts.length)] ?? [0, 0, 0]);
|
|
52
|
+
const c = centred(pts), out = new Float32Array(n * 3);
|
|
53
|
+
c.forEach((p, i) => { out[i * 3] = p[0]; out[i * 3 + 1] = p[1]; out[i * 3 + 2] = p[2]; });
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { createContext } from "react";
|
|
2
|
+
import type { Camera3DKey } from "./three-math";
|
|
3
|
+
|
|
4
|
+
// The camera path of the Scene3D a 3D component sits in, so that Text3D and Orbit3D can size themselves to the closest the camera comes.
|
|
5
|
+
export const CameraKeysContext = createContext<readonly Camera3DKey[] | undefined>(undefined);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Seeded randomness for everything the kit draws or places: the same seed always gives the same numbers, so a frame can be drawn on its own. Never Math.random.
|
|
2
|
+
export function mulberry32(seed: number): () => number {
|
|
3
|
+
let a = seed >>> 0;
|
|
4
|
+
return () => {
|
|
5
|
+
a = (a + 0x6d2b79f5) >>> 0;
|
|
6
|
+
let t = a;
|
|
7
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
8
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
9
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
// One number in [0, 1) from two integers (an index and a seed), with no state.
|
|
13
|
+
export const hash01 = (i: number, seed = 0): number => mulberry32((Math.imul(i | 0, 2654435761) ^ Math.imul(seed | 0, 40503) ^ 0x9e3779b9) >>> 0)();
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// The pure parts of SoundCues: nothing here needs React or HyperFrames, so it can be tested and used in a composition's own timing code.
|
|
2
|
+
import { travel } from "./motion-math";
|
|
3
|
+
import { PEAKS_ON_BOUNDARY, transitionSpan, type TransitionKind } from "./transition-math";
|
|
4
|
+
import { punchEnvelope, type Punch } from "./motion-math";
|
|
5
|
+
import { springs } from "./theme";
|
|
6
|
+
|
|
7
|
+
// One sound to play: `at` is an absolute frame of the video, `sound` a key into the sounds map, `volume` is this cue's own level.
|
|
8
|
+
export type SoundCue = { at: number; sound: string; volume?: number };
|
|
9
|
+
|
|
10
|
+
// A cue on every `every`th beat of the beats that fall from `from` to `to` (both included, in frames), starting with the first of them.
|
|
11
|
+
// `every` below 1 or not a whole number is treated as 1 so that a slip never silently drops all the cues.
|
|
12
|
+
export function cuesOnBeats(
|
|
13
|
+
beatFrames: number[],
|
|
14
|
+
opts: { from: number; to: number; every: number; sound: string; volume?: number },
|
|
15
|
+
): SoundCue[] {
|
|
16
|
+
const every = Number.isFinite(opts.every) && opts.every >= 1 ? Math.floor(opts.every) : 1;
|
|
17
|
+
return beatFrames
|
|
18
|
+
.filter((b) => b >= opts.from && b <= opts.to)
|
|
19
|
+
.filter((_, i) => i % every === 0)
|
|
20
|
+
.map((b) => ({ at: b, sound: opts.sound, ...(opts.volume !== undefined ? { volume: opts.volume } : {}) }));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// The frame at which a sound `leadFrames` long must start so that it ENDS on `frame`: a riser or a whoosh into a cut or a reveal.
|
|
24
|
+
// Never before the first frame of the video.
|
|
25
|
+
export const cueBefore = (frame: number, leadFrames: number): number => Math.max(0, Math.round(frame - leadFrames));
|
|
26
|
+
|
|
27
|
+
// Where the eye sees a scene end. A SceneFrame transition is complete exactly on the boundary (the next scene's start), but the picture changes most
|
|
28
|
+
// in the middle of it, which is half the exit's frames before the boundary (4 frames for the default 8, 3 for `transitionFrames={6}`, 3 for "rgb-whip", whose default is 6); a "cut" has no
|
|
29
|
+
// such span, and neither do "color-push", "flash", "ring" and "flip", which are fullest on the boundary itself. A hit placed on the boundary lands after the change, which is why it reads as late. `exit` and `transitionFrames` are SceneFrame's own.
|
|
30
|
+
export function changeFrame(scene: { startFrame: number; durationFrames: number; exit?: TransitionKind; transitionFrames?: number }): number {
|
|
31
|
+
const boundary = scene.startFrame + scene.durationFrames;
|
|
32
|
+
// A cut, and the transitions that are fullest on the frame the next scene starts (a colour field covering the frame, a flash, a closed ring, an inverted frame), peak on the boundary.
|
|
33
|
+
if (scene.exit && PEAKS_ON_BOUNDARY.includes(scene.exit)) return boundary;
|
|
34
|
+
const frames = Math.min(transitionSpan(scene.exit, scene.transitionFrames), Math.floor(scene.durationFrames / 2));
|
|
35
|
+
return Math.max(scene.startFrame, Math.round(boundary - frames / 2));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// A cue for each scene change, where the eye sees it: an impact on the change's visual peak (changeFrame) and a whoosh that ENDS on that same frame.
|
|
39
|
+
// `scenes` are the manifest's scenes, each with its SceneFrame `exit` and `transitionFrames` if it has them, in order; the last scene has no change after
|
|
40
|
+
// it. `whoosh.frames` is the whoosh's length in frames (from the search line, seconds times 30). Leave out whichever of `impact` and `whoosh` you do not want.
|
|
41
|
+
// `skip` lists scene indices whose change gets no cue (a quiet one). Cues come out in order of frame.
|
|
42
|
+
export function cuesOnChanges(
|
|
43
|
+
scenes: { startFrame: number; durationFrames: number; exit?: TransitionKind; transitionFrames?: number }[],
|
|
44
|
+
opts: { impact?: { sound: string; volume?: number }; whoosh?: { sound: string; volume?: number; frames: number }; skip?: number[] },
|
|
45
|
+
): SoundCue[] {
|
|
46
|
+
const cues: SoundCue[] = [];
|
|
47
|
+
scenes.slice(0, -1).forEach((scene, i) => {
|
|
48
|
+
if (opts.skip?.includes(i)) return;
|
|
49
|
+
const at = changeFrame(scene);
|
|
50
|
+
if (opts.whoosh) cues.push({ at: cueBefore(at, opts.whoosh.frames), sound: opts.whoosh.sound, ...(opts.whoosh.volume !== undefined ? { volume: opts.whoosh.volume } : {}) });
|
|
51
|
+
if (opts.impact) cues.push({ at, sound: opts.impact.sound, ...(opts.impact.volume !== undefined ? { volume: opts.impact.volume } : {}) });
|
|
52
|
+
});
|
|
53
|
+
return cues.sort((a, b) => a.at - b.at);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Where the eye sees a camera move or a punch: the frame the picture changes fastest. A Camera key at `frame` is reached by a spring that starts
|
|
57
|
+
// `lead` frames earlier (12 unless set), so the fastest frame is about 9 frames before the key's own frame, not on it; a punch (`{ punch: true }`)
|
|
58
|
+
// rises over its first `frames` frames (5) and is fastest right after its frame. Place a hit there. `frame` is in the clock the Camera counts (inside a
|
|
59
|
+
// SceneFrame that is the scene's own frame: add the scene's startFrame for an absolute cue).
|
|
60
|
+
export function cueOnCamera(frame: number, o: { punch?: boolean; punchFrames?: number; lead?: number; stiffness?: number; damping?: number; fps?: number } = {}): number {
|
|
61
|
+
if (o.punch) {
|
|
62
|
+
const p: Punch = { frame, x: 0.5, y: 0.5, frames: o.punchFrames };
|
|
63
|
+
let best = 1, top = -1;
|
|
64
|
+
for (let k = 1; k <= (o.punchFrames ?? 5); k++) { const v = punchEnvelope(p, frame + k) - punchEnvelope(p, frame + k - 1); if (v > top) { top = v; best = k; } }
|
|
65
|
+
return frame + best;
|
|
66
|
+
}
|
|
67
|
+
const lead = Math.max(1, o.lead ?? 12), config = { stiffness: o.stiffness ?? springs.smooth.stiffness, damping: o.damping ?? springs.smooth.damping };
|
|
68
|
+
let best = 1, top = -1;
|
|
69
|
+
for (let k = 1; k <= lead; k++) { const v = travel(k / lead, o.fps ?? 30, config, lead) - travel((k - 1) / lead, o.fps ?? 30, config, lead); if (v > top) { top = v; best = k; } }
|
|
70
|
+
return frame - lead + best;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// What SoundCues plays: for each cue its start frame, its file and its final volume (cue volume times master times the file's levelling gain).
|
|
74
|
+
// A cue whose sound is not in `sounds` is a mistake in the composition and is reported with the names that exist.
|
|
75
|
+
export function resolveCues(args: {
|
|
76
|
+
cues: SoundCue[];
|
|
77
|
+
sounds: Record<string, string>;
|
|
78
|
+
volume?: number;
|
|
79
|
+
urls?: Record<string, string>;
|
|
80
|
+
gainFor: (src: string) => number;
|
|
81
|
+
}): { from: number; src: string; volume: number }[] {
|
|
82
|
+
const master = args.volume ?? 1;
|
|
83
|
+
return args.cues.map((c) => {
|
|
84
|
+
const src = args.sounds[c.sound];
|
|
85
|
+
if (src === undefined) throw new Error(`SoundCues: a cue uses the sound "${c.sound}", which is not in sounds. The sounds are: ${Object.keys(args.sounds).join(", ") || "none"}.`);
|
|
86
|
+
const file = args.urls?.[src] ?? src;
|
|
87
|
+
return { from: Math.max(0, Math.round(c.at)), src: file, volume: (c.volume ?? 0.35) * master * args.gainFor(src) };
|
|
88
|
+
});
|
|
89
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// Sound for interface events: turns the frames a component reports (promptFrames, lineFrames, clickFrames, settleFrame, ...) into SoundCues with a sensible
|
|
2
|
+
// sound role, level and density for the kind of event. Pure and deterministic: the same events always give the same cues. A cue's `sound` is a ROLE (like
|
|
3
|
+
// "type-tick"); the film's `sounds` map says which pulled file plays each role.
|
|
4
|
+
import { hash01 } from "./seeded";
|
|
5
|
+
import type { SoundCue } from "./sound-cues";
|
|
6
|
+
|
|
7
|
+
export const CUE_KINDS = ["type", "click", "snap", "pop", "count", "stream", "assemble", "transform", "resolve"] as const;
|
|
8
|
+
export type CueKind = (typeof CUE_KINDS)[number];
|
|
9
|
+
|
|
10
|
+
// `volume` is [without a narrator, under a narrator]: films with no voice use 0.35 to 0.6 for the main hits and less for small ticks; under a voice nothing is over 0.3.
|
|
11
|
+
export type CueRole = { search: string; seconds: number; volume: [number, number] };
|
|
12
|
+
export const CUE_TABLE: Record<CueKind, { about: string; per10s: string; roles: Record<string, CueRole> }> = {
|
|
13
|
+
type: {
|
|
14
|
+
about: "typed text: sparse key ticks, one every 2 to 3 characters, at most 12 a second, a little different in level each, and a thock on the return at the end",
|
|
15
|
+
per10s: "1 to 3 runs of 6 to 12 ticks",
|
|
16
|
+
roles: {
|
|
17
|
+
"type-tick": { search: "mouse click", seconds: 0.2, volume: [0.2, 0.1] },
|
|
18
|
+
"type-return": { search: "glass tap", seconds: 0.5, volume: [0.4, 0.2] },
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
click: { about: "a cursor click or a button press", per10s: "1 to 3", roles: { click: { search: "mouse click", seconds: 0.2, volume: [0.4, 0.22] } } },
|
|
22
|
+
snap: { about: "a card or a panel settling", per10s: "2 to 5", roles: { snap: { search: "soft impact", seconds: 0.7, volume: [0.45, 0.25] } } },
|
|
23
|
+
pop: { about: "a badge, a chip, a bubble appearing", per10s: "3 to 6", roles: { pop: { search: "bubble pop", seconds: 0.5, volume: [0.4, 0.22] } } },
|
|
24
|
+
count: {
|
|
25
|
+
about: "a number counting: a train of ticks that speeds up, ending on a settle chime on settleFrame",
|
|
26
|
+
per10s: "0 to 2 trains",
|
|
27
|
+
roles: { "count-tick": { search: "counter ticks", seconds: 0.1, volume: [0.2, 0.1] }, "count-settle": { search: "success chime", seconds: 1, volume: [0.45, 0.25] } },
|
|
28
|
+
},
|
|
29
|
+
stream: { about: "terminal lines or streamed text: one soft tick per line, at most 6 a second", per10s: "5 to 15", roles: { stream: { search: "glass tap", seconds: 0.5, volume: [0.18, 0.1] } } },
|
|
30
|
+
assemble: {
|
|
31
|
+
about: "pieces building up: a rising run of small hits that gets louder, ending on a lock hit",
|
|
32
|
+
per10s: "0 to 1",
|
|
33
|
+
roles: { "assemble-run": { search: "counter ticks", seconds: 0.1, volume: [0.25, 0.12] }, "assemble-lock": { search: "soft impact", seconds: 0.7, volume: [0.55, 0.3] } },
|
|
34
|
+
},
|
|
35
|
+
transform: {
|
|
36
|
+
about: "a brand or product moving into its readable pose: one sweep into the landing, then one lock hit",
|
|
37
|
+
per10s: "0 to 2, on changes that explain the product or reveal the brand",
|
|
38
|
+
roles: { "transform-sweep": { search: "soft ui whoosh", seconds: 0.5, volume: [0.3, 0.16] }, "transform-lock": { search: "soft impact", seconds: 0.7, volume: [0.45, 0.25] } },
|
|
39
|
+
},
|
|
40
|
+
resolve: {
|
|
41
|
+
about: "the logo or the end card: a riser that ends on a chime, with a clear second of nothing before the riser",
|
|
42
|
+
per10s: "1 per film, at the end",
|
|
43
|
+
roles: { "resolve-riser": { search: "whoosh buildup", seconds: 1.2, volume: [0.35, 0.2] }, "resolve-chime": { search: "sparkle shimmer", seconds: 1.5, volume: [0.55, 0.3] } },
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// The seconds a key tick may be spaced by at the closest: at most this many a second.
|
|
48
|
+
const MAX_TICKS_PER_SECOND = 12;
|
|
49
|
+
const MAX_STREAM_PER_SECOND = 6;
|
|
50
|
+
|
|
51
|
+
export type CueOptions = {
|
|
52
|
+
// The scene's startFrame (or any frame to add) when the events are in a scene's own clock; 0 for absolute frames.
|
|
53
|
+
offset?: number;
|
|
54
|
+
fps?: number;
|
|
55
|
+
// Under a narrator: the quieter levels.
|
|
56
|
+
voice?: boolean;
|
|
57
|
+
// A master level multiplied into every cue (default 1).
|
|
58
|
+
volume?: number;
|
|
59
|
+
seed?: number;
|
|
60
|
+
// "resolve" only: how long the riser is in frames (default 36: it ends on the chime).
|
|
61
|
+
riserFrames?: number;
|
|
62
|
+
// "transform" only: frames from the sweep's start to its audible landing (default 10). Measure the chosen sound's attack.
|
|
63
|
+
sweepFrames?: number;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const level = (kind: CueKind, role: string, o: CueOptions) => CUE_TABLE[kind].roles[role]!.volume[o.voice ? 1 : 0] * (o.volume ?? 1);
|
|
67
|
+
const r3 = (n: number) => Math.round(n * 1000) / 1000;
|
|
68
|
+
|
|
69
|
+
// Never two cues for one role on the same frame: the later is dropped. Frames are rounded, never below 0, and the cues come out in order.
|
|
70
|
+
function unique(cues: SoundCue[]): SoundCue[] {
|
|
71
|
+
const seen = new Set<string>();
|
|
72
|
+
return cues.filter((c) => { const k = `${c.sound}@${c.at}`; if (seen.has(k)) return false; seen.add(k); return true; }).sort((a, b) => a.at - b.at);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Drops events closer than `gap` frames to the one kept before.
|
|
76
|
+
const spaced = (frames: number[], gap: number): number[] => { const out: number[] = []; for (const f of frames) if (!out.length || f - out[out.length - 1]! >= gap) out.push(f); return out; };
|
|
77
|
+
|
|
78
|
+
// The cues for one kind of event. `events` are frames in the component's clock (add `offset` to make them absolute):
|
|
79
|
+
// "type": the frame of every typed character (promptFrames(...).type); ticks on every 2nd or 3rd, a return thock one frame after the last character is typed
|
|
80
|
+
// "click" | "snap" | "pop": the frames of the events (clickFrames, panelSettleFrame, chapterFrames(...).badges)
|
|
81
|
+
// "count": [startFrame, settleFrame] (rollStart and settleFrame of CounterRoll): a speeding tick train, then the chime ON settleFrame
|
|
82
|
+
// "stream": the frame of every line (terminalFrames(...).lines): one soft tick per line, at most 6 a second
|
|
83
|
+
// "assemble": [startFrame, lockFrame] (assembleProgress start and the frame the last piece lands): a rising run, then the lock ON lockFrame
|
|
84
|
+
// "transform": [startFrame, settleFrame]: a sweep leading by sweepFrames, then a lock ON settleFrame
|
|
85
|
+
// "resolve": [frame] the frame the logo lands: the riser ends on it and the chime is ON it (use clearBefore to keep the second before the riser silent)
|
|
86
|
+
export function cuesFor(events: readonly number[], kind: CueKind, o: CueOptions = {}): SoundCue[] {
|
|
87
|
+
if (!CUE_KINDS.includes(kind)) throw new Error(`cuesFor: unknown kind "${kind}". The kinds are: ${CUE_KINDS.join(", ")}.`);
|
|
88
|
+
const fps = o.fps ?? 30, off = o.offset ?? 0, seed = o.seed ?? 0;
|
|
89
|
+
const at = (f: number) => Math.max(0, Math.round(f + off));
|
|
90
|
+
const cue = (f: number, sound: string, volume: number): SoundCue => ({ at: at(f), sound, volume: r3(volume) });
|
|
91
|
+
const f = [...events].filter(Number.isFinite).sort((a, b) => a - b);
|
|
92
|
+
if (!f.length) return [];
|
|
93
|
+
switch (kind) {
|
|
94
|
+
case "type": {
|
|
95
|
+
// Every 2nd character, every 3rd if that would be over 12 a second, more if it is still too fast (the typing itself is faster than any key could be).
|
|
96
|
+
const span = Math.max(1, (f[f.length - 1]! - f[0]!) / fps), per = f.length / span;
|
|
97
|
+
let every = 2;
|
|
98
|
+
while (per / every > MAX_TICKS_PER_SECOND) every++;
|
|
99
|
+
const ticks = f.filter((_, i) => i % every === 0).map((fr, i) => cue(fr, "type-tick", level("type", "type-tick", o) * (0.8 + 0.4 * hash01(i, seed))));
|
|
100
|
+
return unique([...ticks, cue(f[f.length - 1]! + 1, "type-return", level("type", "type-return", o))]);
|
|
101
|
+
}
|
|
102
|
+
case "click": return unique(spaced(f, 3).map((fr) => cue(fr, "click", level("click", "click", o))));
|
|
103
|
+
case "snap": return unique(spaced(f, 3).map((fr) => cue(fr, "snap", level("snap", "snap", o))));
|
|
104
|
+
case "pop": return unique(spaced(f, 3).map((fr, i) => cue(fr, "pop", level("pop", "pop", o) * (0.9 + 0.2 * hash01(i, seed)))));
|
|
105
|
+
case "stream": return unique(spaced(f, Math.ceil(fps / MAX_STREAM_PER_SECOND)).map((fr) => cue(fr, "stream", level("stream", "stream", o))));
|
|
106
|
+
case "count": case "assemble": {
|
|
107
|
+
const role = kind === "count" ? ["count-tick", "count-settle"] : ["assemble-run", "assemble-lock"];
|
|
108
|
+
const start = f[0]!, end = f.length > 1 ? f[f.length - 1]! : f[0]! + fps;
|
|
109
|
+
const len = Math.max(1, end - start);
|
|
110
|
+
// The ticks bunch up toward the end (the roll eases out, the build gathers speed): positions (i/n)^0.6 of the way, no closer than the cap allows.
|
|
111
|
+
const n = Math.min(Math.floor((len / fps) * MAX_TICKS_PER_SECOND), 24);
|
|
112
|
+
const gap = Math.ceil(fps / MAX_TICKS_PER_SECOND);
|
|
113
|
+
const frames = spaced(Array.from({ length: n }, (_, i) => Math.round(start + len * ((i + 0.5) / n) ** (kind === "count" ? 0.6 : 1.4))), gap).filter((fr) => end - fr >= gap);
|
|
114
|
+
const base = level(kind, role[0]!, o);
|
|
115
|
+
const run = frames.map((fr, i) => cue(fr, role[0]!, kind === "assemble" ? base * (0.5 + 1.0 * (i / Math.max(1, frames.length - 1))) : base * (0.85 + 0.3 * hash01(i, seed))));
|
|
116
|
+
return unique([...run, cue(end, role[1]!, level(kind, role[1]!, o))]);
|
|
117
|
+
}
|
|
118
|
+
case "transform": {
|
|
119
|
+
const start = f[0]!, end = f[f.length - 1]!;
|
|
120
|
+
const sweep = Math.max(0, Math.round(o.sweepFrames ?? 10));
|
|
121
|
+
return unique([cue(Math.max(start, end - sweep), "transform-sweep", level("transform", "transform-sweep", o)), cue(end, "transform-lock", level("transform", "transform-lock", o))]);
|
|
122
|
+
}
|
|
123
|
+
case "resolve": {
|
|
124
|
+
const end = f[0]!, rise = o.riserFrames ?? 36;
|
|
125
|
+
return unique([cue(Math.max(0, end - rise), "resolve-riser", level("resolve", "resolve-riser", o)), cue(end, "resolve-chime", level("resolve", "resolve-chime", o))]);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Keeps the second of nothing before a resolve: removes every cue that starts in the `seconds` (default 1) before the riser does, and the riser's own span is kept.
|
|
131
|
+
// `frame` is the frame the logo lands (the chime); cues of the resolve itself are never removed.
|
|
132
|
+
export function clearBefore(cues: readonly SoundCue[], frame: number, o: { seconds?: number; fps?: number; riserFrames?: number } = {}): SoundCue[] {
|
|
133
|
+
const fps = o.fps ?? 30, riserStart = frame - (o.riserFrames ?? 36), from = riserStart - (o.seconds ?? 1) * fps;
|
|
134
|
+
return cues.filter((c) => c.sound.startsWith("resolve-") || c.at < from || c.at >= frame);
|
|
135
|
+
}
|