add mono font to ticket, finish textures and ogs

This commit is contained in:
Francesco Sansalvadore committed 2024-11-19 14:42:44 -04:00
1 parent 8d75d73611
commit 87abc5680c
11 files changed
+128 -269

No files matched your search

+16 -15
View File
@@ -1,7 +1,7 @@
import React from 'react'
import { ImageResponse } from '@vercel/og'
import { createClient } from '@supabase/supabase-js'
import { themes } from '~/components/LaunchWeek/12/Ticket/ticketThemes'
import { themes } from '~/components/LaunchWeek/13/Ticket/ticketThemes'
export const runtime = 'edge' // 'nodejs' is the default
export const dynamic = 'force-dynamic' // defaults to auto
@@ -97,22 +97,22 @@ export async function GET(req: Request, res: Response) {
const OG_WIDTH = 1200
const OG_HEIGHT = 628
const USERNAME_LEFT = 400
const USERNAME_TOP = 440
const USERNAME_BOTTOM = 60
const USERNAME_WIDTH = 400
const DISPLAY_NAME = name || username
const BACKGROUND = (isDark?: boolean) => ({
regular: {
LOGO: `${STORAGE_URL}/assets/supabase/supabase-logo-icon.png?v1`,
BACKGROUND_IMG: `${STORAGE_URL}/assets/ticket-og-bg-regular-${isDark ? 'dark' : 'light'}.png?v1`,
LOGO: `${STORAGE_URL}/assets/supabase/supabase-logo-icon.png?v4`,
BACKGROUND_IMG: `${STORAGE_URL}/assets/ticket-og-bg-regular-${isDark ? 'dark' : 'light'}.png?v4`,
},
platinum: {
LOGO: `${STORAGE_URL}/assets/supabase/supabase-logo-icon.png?v1`,
BACKGROUND_IMG: `${STORAGE_URL}/assets/ticket-og-bg-platinum.png?v1`,
LOGO: `${STORAGE_URL}/assets/supabase/supabase-logo-icon.png?v4`,
BACKGROUND_IMG: `${STORAGE_URL}/assets/ticket-og-bg-platinum.png?v4`,
},
secret: {
LOGO: `${STORAGE_URL}/assets/supabase/supabase-logo-icon-white.png?v1`,
BACKGROUND_IMG: `${STORAGE_URL}/assets/ticket-og-bg.png?v1`,
LOGO: `${STORAGE_URL}/assets/supabase/supabase-logo-icon.png?v4`,
BACKGROUND_IMG: `${STORAGE_URL}/assets/ticket-og-bg-secret.png?v4`,
},
})
@@ -135,14 +135,14 @@ export async function GET(req: Request, res: Response) {
>
{/* Background */}
<img
width="1202"
height="632"
width="1204"
height="634"
style={{
position: 'absolute',
top: '-1px',
left: '-1px',
bottom: '-1px',
right: '-1px',
top: '-2px',
left: '-2px',
bottom: '-2px',
right: '-2px',
zIndex: '0',
backgroundSize: 'cover',
}}
@@ -157,13 +157,14 @@ export async function GET(req: Request, res: Response) {
justifyContent: 'center',
flexDirection: 'column',
position: 'absolute',
top: USERNAME_TOP,
bottom: USERNAME_BOTTOM,
left: USERNAME_LEFT,
width: USERNAME_WIDTH,
height: 'auto',
overflow: 'hidden',
textOverflow: 'clip',
textAlign: 'left',
letterSpacing: '-0.5px',
marginBottom: '10px',
}}
>
@@ -1,11 +0,0 @@
export const themes = (isDark?: boolean) => ({
regular: {
TICKET_FOREGROUND: isDark ? '#1F1F1F' : '#FAFAFA',
},
platinum: {
TICKET_FOREGROUND: '#1F1F1F',
},
secret: {
TICKET_FOREGROUND: '#EDEDED',
},
})
+106 -85
View File
@@ -30,6 +30,10 @@ const ThreeCanvas: React.FC<{
const LINE_HEIGHT = 1.4
const MIN_CANVAS_HEIGHT = 600
const positionRight = ticketPosition === 'right'
const isPlatinum = ticketType === 'platinum'
const isSecret = ticketType === 'secret'
console.log('ticketType', ticketType)
const CONFIG = {
regular: {
@@ -41,7 +45,7 @@ const ThreeCanvas: React.FC<{
ticketForeground: 0x0f0f0f,
},
secret: {
ticketColor: 0x9ea1a1,
ticketColor: isDarkTheme ? 0xe7a938 : 0xf2c66d,
ticketForeground: 0x0f0f0f,
},
}
@@ -69,6 +73,7 @@ const ThreeCanvas: React.FC<{
)
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
renderer.setSize(initialCanvasWidth, initialCanvasHeight)
renderer.setPixelRatio(window.devicePixelRatio)
renderer.shadowMap.enabled = true
canvasRef.current.appendChild(renderer.domElement)
@@ -78,33 +83,32 @@ const ThreeCanvas: React.FC<{
// Rest of your existing setup code (loader, materials, etc.)
const loader = new GLTFLoader()
const metalTexture = new THREE.TextureLoader().load('/images/launchweek/13/ticket/2000px.avif')
// Texture from Freepik: https://www.freepik.com/free-photo/golden-wall-background_1213228.htm
const metalTexture = new THREE.TextureLoader().load(
'/images/launchweek/13/ticket/metal-texture.jpg'
)
const goldTexture = new THREE.TextureLoader().load(
'/images/launchweek/13/ticket/gold-texture.jpg'
)
const metalMaterial = new THREE.MeshStandardMaterial({
color: CONFIG[ticketType].ticketColor,
map: ticketType === 'platinum' ? metalTexture : undefined,
bumpMap: ticketType === 'platinum' ? metalTexture : undefined,
metalnessMap: metalTexture,
roughnessMap: metalTexture,
metalness: ticketType === 'platinum' ? 0.9 : isDarkTheme ? 0.2 : 0.9,
roughness: ticketType === 'platinum' ? 0.19 : isDarkTheme ? 0.2 : 0.5,
bumpScale: ticketType === 'platinum' ? 0.25 : undefined,
})
// Environment Map
const rgbeLoader = new RGBELoader()
rgbeLoader.load('/images/launchweek/13/ticket/canary_wharf_1k.hdr', (texture) => {
texture.mapping = THREE.EquirectangularReflectionMapping
scene.environment = texture
metalMaterial.envMap = texture
metalMaterial.envMapIntensity = isDarkTheme ? 2 : 1
metalMaterial.blending = THREE.AdditiveBlending
map: isSecret ? goldTexture : isPlatinum ? metalTexture : undefined,
bumpMap: isSecret ? goldTexture : isPlatinum ? metalTexture : undefined,
metalnessMap: isSecret ? goldTexture : metalTexture,
roughnessMap: isSecret ? goldTexture : metalTexture,
metalness: isSecret ? 1 : isPlatinum ? 0.9 : isDarkTheme ? 0.2 : 0.9,
roughness: isSecret ? 0.2 : isPlatinum ? 0.19 : isDarkTheme ? 0.2 : 0.5,
bumpScale: isSecret ? 0.85 : isPlatinum ? 0.45 : undefined,
})
// Load Ticket model, fonts and textures
let ticket3DImport: THREE.Mesh
const ticketGroup = new THREE.Group()
ticketGroup.position.x = getTicketXPosition(window.innerWidth, positionRight)
const ticketScale = getTicketScale(window.innerWidth)
ticketGroup.scale.set(ticketScale, ticketScale, ticketScale)
ticketGroup.position.x = getTicketXPosition(window.innerWidth, positionRight)
loader.load('/images/launchweek/13/ticket/3D-ticket.glb', (gltf) => {
ticket3DImport = gltf.scene.children[0] as THREE.Mesh
@@ -123,74 +127,88 @@ const ThreeCanvas: React.FC<{
scene.add(ticketGroup)
ticketRef.current = ticket3DImport
camera.lookAt(ticket3DImport.position)
})
// Load font and add text geometry
const fontLoader = new FontLoader()
fontLoader.load('/images/launchweek/13/ticket/Inter_Regular.json', (font) => {
const textMaterial = new THREE.MeshStandardMaterial({
color: CONFIG[ticketType].ticketForeground,
metalness: 0.2,
roughness: 0.35,
const textMaterial = new THREE.MeshStandardMaterial({
color: CONFIG[ticketType].ticketForeground,
metalness: 0.2,
roughness: 0.35,
})
const PADDING_LEFT = -6.4
// Load font and add text geometry
const fontLoader = new FontLoader()
// Load Inter font
fontLoader.load('/images/launchweek/13/ticket/Inter_Regular.json', (font) => {
textLines.map((text, index) => {
const textGeometry = new TextGeometry(text, {
font,
size: 1.0,
height: 0.4,
})
ticket3DImport.updateMatrix()
const PADDING_LEFT = -6.4
textLines.map((text, index) => {
const textGeometry = new TextGeometry(text, {
font,
size: 1.0,
height: 0.4,
})
const textMesh = new THREE.Mesh(textGeometry, textMaterial)
textMesh.updateMatrix()
// textMesh.position.set(-5.8, -5 + LINE_HEIGHT * (index + 1), -0.2)
textMesh.position.set(PADDING_LEFT, -5 + LINE_HEIGHT * (index + 1), -0.2)
textMesh.castShadow = true
ticketGroup.add(textMesh)
})
const footerContent = [
{
text: 'LAUNCH WEEK 13',
position: { x: PADDING_LEFT, y: -6.8, z: -0.2 },
size: 0.59,
},
{
text: '2-6 DEC 2024',
position: { x: PADDING_LEFT, y: -7.7, z: -0.2 },
size: 0.55,
},
]
footerContent.map((line) => {
const textGeometry = new TextGeometry(line.text, {
font,
size: line.size,
height: 0.4,
})
const textMesh = new THREE.Mesh(textGeometry, textMaterial)
textMesh.updateMatrix()
textMesh.position.set(line.position.x, line.position.y, line.position.z)
textMesh.castShadow = true
ticketGroup.add(textMesh)
})
camera.position.x = 0
const textMesh = new THREE.Mesh(textGeometry, textMaterial)
textMesh.updateMatrix()
// textMesh.position.set(-5.8, -5 + LINE_HEIGHT * (index + 1), -0.2)
textMesh.position.set(PADDING_LEFT, -5 + LINE_HEIGHT * (index + 1), -0.2)
textMesh.castShadow = true
ticketGroup.add(textMesh)
})
})
camera.position.z = 30
// Lights
fontLoader.load('/images/launchweek/13/ticket/SourceCodePro_Regular.json', (font) => {
const footerContent = [
{
text: 'LAUNCH WEEK 13',
position: { x: PADDING_LEFT, y: -6.8, z: -0.2 },
size: 0.59,
},
{
text: '2-6 DEC 2024',
position: { x: PADDING_LEFT, y: -7.7, z: -0.2 },
size: 0.55,
},
]
const ambientLight = new THREE.AmbientLight(0xffffff, ticketType === 'platinum' ? 8 : 2)
footerContent.map((line) => {
const textGeometry = new TextGeometry(line.text, {
font,
size: line.size,
height: 0.4,
})
const textMesh = new THREE.Mesh(textGeometry, textMaterial)
textMesh.updateMatrix()
textMesh.position.set(line.position.x, line.position.y, line.position.z)
textMesh.castShadow = true
ticketGroup.add(textMesh)
})
})
ticketGroup.updateMatrix()
camera.position.x = 0
camera.position.z = 30
// Environment Map
const envMapLoader = new THREE.TextureLoader()
envMapLoader.load('/images/launchweek/13/ticket/env-map.jpg', (texture) => {
texture.mapping = THREE.EquirectangularReflectionMapping
scene.environment = texture
metalMaterial.envMap = texture
metalMaterial.envMapIntensity = isDarkTheme ? 3 : 0.5
// metalMaterial.blending = THREE.NormalBlending
})
// Lights
const ambientLight = new THREE.AmbientLight(
0xffffff,
isSecret ? (isDarkTheme ? 4 : 5) : isPlatinum ? (isDarkTheme ? 10 : 8) : 2
)
scene.add(ambientLight)
const spotLight1 = new THREE.SpotLight(0xffffff, 20)
spotLight1.position.z = ticketGroup.position.z - 3
spotLight1.position.x = ticketGroup.position.x - 6
spotLight1.angle = Math.PI / 2
const spotLight1 = new THREE.SpotLight(0xffffff, isDarkTheme ? 20 : 4)
spotLight1.position.z = ticketGroup.position.z + 4
spotLight1.position.x = ticketGroup.position.x - 10
spotLight1.angle = (Math.PI / 2) * 0.5
spotLight1.lookAt(ticketGroup.position)
spotLight1.castShadow = true
spotLight1.shadow.mapSize.set(1024, 1024)
@@ -198,11 +216,14 @@ const ThreeCanvas: React.FC<{
spotLight1.shadow.camera.far = 15
scene.add(spotLight1)
const spotLight2 = new THREE.SpotLight(0xffffff, 20)
spotLight2.position.z = ticketGroup.position.z - 2
spotLight2.position.x = ticketGroup.position.x + 6
spotLight2.angle = Math.PI / 2
const spotLight2 = new THREE.SpotLight(0xffffff, isDarkTheme ? 20 : 4)
spotLight2.position.z = ticketGroup.position.z + 4
spotLight2.position.x = ticketGroup.position.x + 10
spotLight2.angle = (Math.PI / 2) * 0.5
spotLight2.castShadow = true
spotLight2.shadow.mapSize.set(1024, 1024)
spotLight2.shadow.camera.near = 5
spotLight2.shadow.camera.far = 15
spotLight2.lookAt(ticketGroup.position)
scene.add(spotLight2)
@@ -312,7 +333,7 @@ const ThreeCanvas: React.FC<{
className
)}
>
<div ref={canvasRef} className="w-full lg:h-full" />
<div ref={canvasRef} className="w-full lg:h-full !cursor-none" />
</div>
)
}
@@ -1,164 +1,11 @@
export const themes = {
export const themes = (isDark?: boolean) => ({
regular: {
OG_BACKGROUND: '#121212',
TICKET_BORDER: '#242424',
TICKET_FOREGROUND: '#FAFAFA',
TICKET_FOREGROUND_LIGHT: '#B4B4B4',
TICKET_BACKGROUND: '#1F1F1F',
TICKET_BACKGROUND_CODE: '#171717',
CODE_HIGHLIGHT_BACKGROUND: '#292929',
CODE_HIGHLIGHT_BORDER: '',
CODE_LINE_NUMBER: '#4D4D4D',
CODE_THEME: {
hljs: {
color: '#fff', // corresponds to --ray-foreground
},
'hljs-keyword': {
color: '#bda4ff', // corresponds to --ray-token-keyword
},
'hljs-number': {
color: '#ffffff', // corresponds to --ray-token-constant
},
'hljs-string': {
color: '#ffcda1', // corresponds to --ray-token-string
fontWeight: 'bold',
},
'hljs-comment': {
color: 'purple', // corresponds to --ray-token-comment
},
'hljs-params': {
color: '#ffffff', // corresponds to --ray-token-parameter
},
'hljs-function': {
color: '3ecf8e', // corresponds to --ray-token-function
},
'hljs-variable': {
color: '#ffcda1', // corresponds to --ray-token-string-expression
},
'hljs-punctuation': {
color: '#ffffff', // corresponds to --ray-token-punctuation
},
'hljs-link': {
color: '#ffffff', // corresponds to --ray-token-link
},
'hljs-literal': {
color: '#ffffff', // corresponds to --ray-token-number
},
'hljs-attr': {
color: '#3ecf8e', // corresponds to --ray-token-property
},
},
TICKET_FOREGROUND: isDark ? '#1F1F1F' : '#FAFAFA',
},
platinum: {
OG_BACKGROUND: '#121212',
TICKET_BORDER: '#242424',
TICKET_FOREGROUND: '#FAFAFA',
TICKET_FOREGROUND_LIGHT: '#888888',
TICKET_BACKGROUND: '#212427',
TICKET_BACKGROUND_CODE: '#24292D',
CODE_HIGHLIGHT_BACKGROUND: '#272B2E',
CODE_HIGHLIGHT_BORDER: '',
CODE_LINE_NUMBER: '#4D4D4D',
CODE_THEME: {
hljs: {
color: '#abb2bf', // General text color
},
'hljs-keyword': {
color: '#c678dd', // Keywords like "await"
},
'hljs-string': {
color: '#98c379', // Strings
fontWeight: 'bold',
},
'hljs-title': {
color: '#e06c75', // Titles (like function names)
},
'hljs-variable': {
color: '#e06c75', // Variables
},
'hljs-attr': {
color: '#d19a66', // Attributes
},
'hljs-number': {
color: '#d19a66', // Numbers
},
'hljs-comment': {
color: '#5c6370', // Comments
},
'hljs-function': {
color: '#61afef', // Function names
},
'hljs-params': {
color: '#abb2bf', // Parameters
},
'hljs-punctuation': {
color: '#abb2bf', // Punctuation
},
'hljs-meta': {
color: '#abb2bf', // Meta
},
'hljs-literal': {
color: '#56b6c2', // Literal values
},
'hljs-link': {
color: '#61afef', // Links
},
},
TICKET_FOREGROUND: '#1F1F1F',
},
secret: {
OG_BACKGROUND: '#0F2BE6',
TICKET_BORDER: '#3059F2',
TICKET_FOREGROUND: '#EDEDED',
TICKET_FOREGROUND_LIGHT: '#EDEDED',
TICKET_BACKGROUND: '#0F2BE6',
TICKET_BACKGROUND_CODE: '#0000B4',
CODE_HIGHLIGHT_BACKGROUND: '#3059F2',
CODE_HIGHLIGHT_BORDER: '#73B2FA',
CODE_LINE_NUMBER: '#5F7BF6',
CODE_THEME: {
hljs: {
color: '#fff', // General text color
},
'hljs-keyword': {
color: '#fff', // Keywords like "await"
},
'hljs-string': {
color: 'white', // Strings
fontWeight: 'bold',
},
'hljs-title': {
color: '#e06c75', // Titles (like function names)
},
'hljs-variable': {
color: '#e06c75', // Variables
},
'hljs-attr': {
color: '#fff', // Attributes
},
'hljs-number': {
color: '#d19a66', // Numbers
},
'hljs-comment': {
color: '#5c6370', // Comments
},
'hljs-function': {
color: '#61afef', // Function names
},
'hljs-params': {
color: '#abb2bf', // Parameters
},
'hljs-punctuation': {
color: '#abb2bf', // Punctuation
},
'hljs-meta': {
color: '#abb2bf', // Meta
},
'hljs-literal': {
color: '#56b6c2', // Literal values
},
'hljs-link': {
color: '#61afef', // Links
},
},
TICKET_FOREGROUND: '#1F1F1F',
},
}
})
Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

@@ -0,0 +1 @@
{"glyphs":{"0":{"ha":833,"x_min":99,"x_max":735,"o":"m 417 -17 q 183 103 268 -17 q 99 446 99 224 q 183 786 99 669 q 417 903 268 903 q 650 786 565 903 q 735 446 735 669 q 650 103 735 224 q 417 -17 565 -17 m 417 75 q 569 165 510 75 q 628 446 628 256 q 613 610 628 542 q 569 724 597 679 q 501 790 540 769 q 417 811 463 811 q 332 790 371 811 q 265 724 293 769 q 221 610 236 679 q 206 446 206 542 q 265 165 206 256 q 417 75 324 75 m 417 363 q 356 387 382 363 q 331 456 331 411 q 356 523 331 499 q 417 547 382 547 q 477 523 451 547 q 503 456 503 499 q 477 387 503 411 q 417 363 451 363 z "},"1":{"ha":833,"x_min":135,"x_max":736,"o":"m 135 0 l 135 94 l 390 94 l 390 747 l 192 747 l 192 821 q 324 847 271 832 q 419 886 376 863 l 504 886 l 504 94 l 736 94 l 736 0 l 135 0 z "},"2":{"ha":833,"x_min":96,"x_max":725,"o":"m 101 0 l 101 68 q 301 249 214 168 q 447 397 388 329 q 536 522 506 464 q 567 636 567 581 q 519 761 567 713 q 374 810 472 810 q 258 778 311 810 q 161 703 204 747 l 96 768 q 224 866 156 829 q 388 903 293 903 q 508 884 454 903 q 599 831 561 865 q 656 749 636 797 q 676 642 676 700 q 647 516 676 579 q 563 385 617 453 q 433 245 508 318 q 268 90 358 172 q 351 96 310 93 q 433 99 393 99 l 725 99 l 725 0 l 101 0 z "},"3":{"ha":833,"x_min":79,"x_max":719,"o":"m 399 -17 q 206 22 283 -17 q 79 108 128 60 l 138 183 q 244 110 182 142 q 392 78 306 78 q 476 90 438 78 q 544 122 515 101 q 588 173 572 143 q 604 240 604 203 q 587 312 604 279 q 531 368 569 344 q 431 404 492 392 q 282 417 369 417 l 282 504 q 416 517 361 504 q 506 551 471 529 q 556 604 540 574 q 572 669 572 635 q 523 772 572 735 q 392 810 474 810 q 274 785 329 810 q 174 719 218 760 l 113 792 q 239 872 168 840 q 396 903 310 903 q 512 888 458 903 q 605 844 565 872 q 667 774 644 815 q 689 678 689 732 q 636 546 689 597 q 500 467 583 494 l 500 461 q 584 433 544 451 q 654 385 624 414 q 702 319 685 357 q 719 236 719 282 q 694 131 719 178 q 626 51 669 83 q 524 1 582 18 q 399 -17 465 -17 z "},"4":{"ha":833,"x_min":54,"x_max":757,"o":"m 511 0 l 511 244 l 54 244 l 54 321 l 497 886 l 619 886 l 619 336 l 757 336 l 757 244 l 619 244 l 619 0 l 511 0 m 181 336 l 511 336 l 511 611 q 514 690 513 647 q 518 768 515 732 l 511 768 q 467 703 490 736 q 421 639 443 671 l 181 336 z "},"5":{"ha":833,"x_min":78,"x_max":725,"o":"m 397 -17 q 290 -6 338 -17 q 203 21 242 4 q 134 59 165 38 q 78 104 103 81 l 135 179 q 182 141 157 160 q 237 108 207 122 q 303 86 267 94 q 385 78 340 78 q 473 92 432 78 q 544 133 514 107 q 592 197 575 160 q 610 281 610 235 q 551 424 610 372 q 392 475 492 475 q 297 461 336 475 q 211 421 258 447 l 150 460 l 179 886 l 681 886 l 681 788 l 281 788 l 257 525 q 333 551 296 542 q 421 561 369 561 q 540 544 485 561 q 637 494 596 528 q 701 408 678 460 q 725 283 725 356 q 698 156 725 211 q 625 62 671 100 q 520 3 579 24 q 397 -17 461 -17 z "},"6":{"ha":833,"x_min":107,"x_max":742,"o":"m 446 -17 q 312 10 374 -17 q 204 90 250 36 q 133 223 158 143 q 107 410 107 303 q 138 637 107 543 q 219 790 168 731 q 338 876 271 849 q 478 903 406 903 q 620 876 560 903 q 725 808 681 849 l 661 738 q 581 788 626 769 q 485 807 535 807 q 383 788 432 807 q 298 724 335 768 q 238 606 261 679 q 214 426 215 533 q 333 510 268 479 q 465 542 399 542 q 577 524 526 542 q 665 473 628 507 q 722 388 701 439 q 742 268 742 336 q 717 152 742 204 q 653 63 693 100 q 559 4 613 25 q 446 -17 506 -17 m 446 74 q 519 88 485 74 q 579 127 554 101 q 619 188 604 153 q 633 268 633 224 q 585 408 633 360 q 444 456 536 456 q 331 428 392 456 q 218 336 271 400 q 290 140 231 207 q 446 74 349 74 z "},"7":{"ha":833,"x_min":97,"x_max":739,"o":"m 314 0 q 338 230 319 126 q 390 426 356 333 q 478 607 425 519 q 608 788 532 694 l 97 788 l 97 886 l 739 886 l 739 815 q 594 624 651 715 q 503 437 538 532 q 453 236 468 342 q 433 0 438 131 l 314 0 z "},"8":{"ha":833,"x_min":94,"x_max":738,"o":"m 419 -17 q 284 1 344 -17 q 182 52 224 19 q 117 129 140 85 q 94 226 94 174 q 111 308 94 272 q 154 374 128 344 q 215 424 181 403 q 285 463 249 446 l 285 468 q 190 553 229 503 q 150 675 150 604 q 170 768 150 726 q 227 840 190 810 q 314 886 264 869 q 424 903 364 903 q 540 885 489 903 q 626 837 590 868 q 680 762 661 806 q 699 667 699 718 q 659 549 699 604 q 564 456 619 494 l 564 450 q 632 411 600 432 q 688 364 664 390 q 724 302 711 338 q 738 219 738 267 q 716 126 738 169 q 654 51 694 83 q 555 1 614 19 q 419 -17 496 -17 m 489 483 q 600 661 600 564 q 554 773 600 728 q 421 818 508 818 q 300 779 346 818 q 254 675 254 740 q 272 606 254 635 q 322 556 290 578 q 397 516 354 533 q 489 483 439 499 m 422 68 q 575 111 522 68 q 628 224 628 154 q 606 301 628 269 q 547 354 585 332 q 459 394 510 376 q 350 431 408 411 q 242 349 285 394 q 199 238 199 304 q 259 115 199 163 q 422 68 319 68 z "},"9":{"ha":833,"x_min":93,"x_max":728,"o":"m 390 431 q 503 458 443 431 q 617 549 563 486 q 545 746 603 679 q 389 813 488 813 q 315 799 349 813 q 256 759 281 785 q 216 697 231 733 q 201 618 201 661 q 249 478 201 526 q 390 431 297 431 m 357 -17 q 215 10 275 -17 q 110 76 154 36 l 174 149 q 253 97 207 117 qLine truncated
Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 KiB