javascript - attribute - Hacer que las coordenadas sean atraídas/rechazadas hacia/desde ciertas otras coordenadas
map area html generator (4)
Cambios realizados. Estoy utilizando el punto medio del trazo en lugar del principio, por lo que los puntos están más separados. Aunque no muestra el comienzo del trazo, ahora es más fácil ver con qué color / trazo está asociado el número.
-
const fontSize = 40
-
const startingPoints = item.medians.map(i=>({x:i[i.length % 2 == 0 ? Math.floor(i.length/2)-1 : Math.floor(i.length/2)][0],y:i[i.length % 2 == 0 ? Math.floor(i.length/2)-1 : Math.floor(i.length/2)][1]})) // use midpoint of stroke
-
const x = -20+startingPoints[i].x //change positioning
-
const y = 915+fontSize/2-startingPoints[i].y //change positioning
//#!/usr/bin/env node
//const fs = require(''fs-extra'')
//const readline = require(''readline'')
const strokeData = {"character":"龥","strokes":["M 128 445 Q 222 525 301 653 Q 319 685 334 709 L 343 725 Q 351 746 367 763 Q 376 772 374 782 Q 374 791 361 801 Q 327 827 298 819 Q 290 819 293 806 Q 310 732 219 604 L 171 539 Q 133 492 33 396 Q 26 392 35 390 Q 43 390 110 431 L 128 445 Z","M 334 709 Q 386 675 447 629 Q 461 617 472 615 Q 478 615 482 624 Q 488 634 474 663 Q 459 700 343 725 C 314 731 309 725 334 709 Z","M 253 553 Q 224 546 246 534 Q 276 517 325 531 Q 423 558 435 563 Q 439 567 439 572 Q 437 586 406 590 Q 388 593 316 566 L 253 553 Z","M 147 441 Q 143 445 128 445 C 101 446 101 446 110 431 Q 125 406 140 365 Q 153 331 162 323 Q 174 311 176 321 Q 178 329 175 343 L 171 363 Q 161 394 153 422 C 148 441 148 441 147 441 Z","M 255 464 Q 233 477 223 476 Q 213 475 180 456 Q 172 453 147 441 C 120 428 124 416 153 422 Q 161 423 181 431 Q 212 442 216 437 Q 221 433 216 383 C 213 353 241 353 245 383 Q 251 431 265 445 C 272 454 272 454 255 464 Z","M 175 343 Q 180 343 186 345 Q 216 357 254 367 Q 261 368 259 373 Q 259 377 245 383 L 216 383 Q 213 383 171 363 C 144 350 145 341 175 343 Z","M 285 468 Q 261 473 255 470 Q 255 468 255 464 C 255 459 255 469 265 445 Q 272 429 278 405 Q 288 371 295 363 Q 305 352 310 361 Q 310 367 310 371 L 308 392 Q 298 434 298 450 C 297 465 297 465 285 468 Z","M 374 410 Q 386 441 396 454 Q 404 463 406 466 C 413 475 413 475 398 484 L 374 498 Q 360 506 347 499 Q 327 482 285 468 C 257 458 270 438 298 450 Q 300 451 310 455 Q 351 468 355 462 Q 357 461 357 459 Q 359 445 342 406 C 330 378 363 382 374 410 Z","M 310 371 Q 323 378 385 390 Q 392 391 392 398 Q 390 402 374 410 C 368 413 368 413 342 406 Q 333 404 308 392 C 281 379 283 358 310 371 Z","M 406 466 Q 424 400 427 396 Q 437 382 441 392 L 443 406 L 441 425 Q 434 470 433 482 C 432 494 432 494 420 500 Q 419 501 416 502 Q 398 508 394 504 Q 390 500 398 484 L 406 466 Z","M 519 439 Q 529 478 541 490 Q 555 506 540 514 Q 514 526 506 528 Q 496 531 484 525 Q 454 510 420 500 C 391 491 403 477 433 482 Q 445 484 454 487 Q 490 496 495 490 Q 496 490 496 487 Q 500 472 490 439 C 482 410 511 410 519 439 Z","M 443 406 Q 449 406 455 407 Q 490 416 527 419 Q 534 420 534 425 Q 534 429 519 439 C 519 439 519 439 490 439 Q 488 441 441 425 C 413 415 413 405 443 406 Z","M 169 267 Q 151 272 139 272 Q 134 270 133 267 Q 132 264 139 246 Q 167 188 145 104 Q 129 91 142 62 Q 151 43 160 37 Q 166 27 173 32 Q 192 50 192 163 Q 192 202 192 242 C 192 261 192 261 169 267 Z","M 347 278 Q 402 290 419 284 Q 433 278 435 258 Q 439 207 429 114 Q 431 98 416 98 Q 396 98 386 100 Q 380 100 377 98 Q 369 94 398 69 Q 419 47 431 22 Q 441 15 451 20 Q 455 22 459 25 Q 480 58 482 158 Q 476 267 486 283 Q 494 292 488 302 Q 484 307 472 314 Q 437 333 419 323 Q 409 319 388 316 Q 271 292 169 267 C 140 260 164 232 192 242 Q 208 248 231 254 L 248 257 Q 272 265 323 274 L 347 278 Z","M 244 188 Q 235 186 214 178 Q 201 174 217 164 Q 226 158 246 163 L 274 170 L 332 184 L 367 190 Q 376 194 396 198 Q 402 199 406 202 Q 415 209 397 218 Q 379 227 368 223 L 332 216 Q 327 216 274 196 L 244 188 Z","M 231 254 Q 231 251 233 246 Q 241 216 244 188 L 246 163 Q 252 102 256 93 Q 265 78 270 87 Q 274 97 274 170 L 274 196 Q 274 208 275 218 Q 276 239 274 243 Q 271 250 248 257 C 229 263 229 263 231 254 Z","M 332 184 Q 332 80 342 71 Q 343 71 347 71 Q 351 73 354 81 Q 360 98 367 190 L 368 223 Q 368 229 372 243 Q 376 259 368 267 Q 355 276 347 278 C 319 288 317 289 323 274 Q 331 257 332 216 L 332 184 Z","M 698 651 L 862 688 Q 908 700 914 708 Q 923 714 918 723 Q 913 729 903 734 Q 864 751 825 733 Q 808 727 794 723 Q 696 689 582 674 Q 548 668 572 653 Q 606 634 659 641 L 698 651 Z","M 657 540 Q 668 553 678 566 Q 699 594 712 608 Q 734 624 698 651 C 674 669 660 671 659 641 Q 659 617 641 563 Q 631 549 630 535 C 626 505 638 517 657 540 Z","M 596 531 Q 594 533 591 535 Q 571 549 557 543 Q 555 541 555 536 Q 555 525 561 515 Q 582 472 572 295 Q 566 269 566 245 Q 566 198 586 180 Q 595 171 604 180 Q 611 192 612 201 L 613 229 Q 615 239 615 265 Q 613 294 613 308 L 613 331 L 613 406 L 613 431 Q 613 455 615 506 C 615 517 615 517 596 531 Z","M 809 223 Q 809 216 817 201 Q 831 173 841 174 Q 855 176 869 209 Q 874 219 876 225 Q 882 242 878 272 Q 868 363 866 451 Q 866 463 868 472 Q 871 489 885 517 Q 892 533 886 539 Q 861 561 817 579 Q 802 583 780 576 Q 774 574 657 540 L 630 535 Q 608 533 596 531 C 566 527 585 503 615 506 Q 627 506 642 510 Q 710 527 768 538 Q 789 542 795 531 Q 809 514 811 453 Q 817 290 811 249 L 809 223 Z","M 613 406 L 618 406 Q 686 417 747 423 Q 768 428 759 440 Q 749 452 727 456 Q 710 459 613 431 C 584 423 583 406 613 406 Z","M 613 308 L 623 308 Q 708 317 761 319 Q 783 323 774 334 Q 762 357 718 354 Q 690 352 613 331 C 584 323 583 308 613 308 Z","M 612 201 L 621 201 L 809 223 C 839 226 835 232 811 249 Q 800 259 777 258 Q 753 257 613 229 C 584 223 582 201 612 201 Z","M 623 161 Q 608 95 455 -29 Q 447 -35 447 -37 Q 446 -41 460 -39 Q 480 -37 504 -27 Q 562 -4 640 76 Q 686 123 690 126 Q 695 131 697 137 Q 704 157 671 167 Q 652 174 641 174 Q 627 174 623 161 Z","M 757 157 Q 757 153 757 151 Q 758 140 774 118 Q 829 47 864 -7 Q 877 -29 892 -36 Q 898 -37 903 -33 Q 923 -23 917 25 Q 914 77 768 163 Q 761 169 757 157 Z"],"medians":[[[306,809],[331,777],[299,694],[261,628],[204,549],[134,471],[36,394]],[[343,709],[352,714],[418,680],[449,658],[470,627]],[[247,544],[296,542],[396,571],[430,571]],[[119,429],[133,427],[170,326]],[[157,429],[195,452],[229,454],[238,443],[232,399],[223,394]],[[180,349],[185,359],[223,372],[254,372]],[[261,467],[274,459],[282,444],[303,366]],[[294,468],[302,463],[319,466],[353,482],[378,469],[365,424],[346,415]],[[312,376],[342,394],[386,397]],[[399,500],[418,476],[436,400]],[[428,499],[438,494],[495,510],[511,504],[517,496],[513,469],[509,453],[497,445]],[[449,411],[455,420],[472,424],[512,429],[528,425]],[[140,265],[165,240],[171,216],[174,151],[162,84],[168,40]],[[176,266],[196,259],[400,303],[434,303],[452,293],[458,266],[459,170],[452,94],[438,71],[383,94]],[[226,173],[325,199],[399,208]],[[236,251],[256,233],[264,91]],[[330,271],[351,252],[345,75]],[[574,665],[607,659],[648,661],[853,714],[907,718]],[[667,636],[685,620],[651,553],[636,541]],[[563,537],[592,497],[590,223],[595,187]],[[601,525],[632,521],[792,558],[815,551],[843,521],[837,466],[846,276],[841,185]],[[616,412],[628,424],[686,434],[729,439],[750,433]],[[618,314],[628,322],[706,335],[741,336],[766,328]],[[617,208],[629,219],[775,239],[792,239],[800,230]],[[680,143],[651,136],[615,87],[554,27],[503,-10],[454,-36]],[[766,154],[877,36],[893,-2],[894,-22]]]}
const colorSaturation = 100 // 0-255
const colorLightness = 40 // 0-255
const fontSize = 40
const fontBorderWidth = 6
function hslToRgb(h, s, l) { // To generate rainbow colors
h /= 360
s /= 100
l /= 100
let r, g, b
if (s === 0) {
r = g = b = l // achromatic
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1
if (t > 1) t -= 1
if (t < 1 / 6) return p + (q - p) * 6 * t
if (t < 1 / 2) return q
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6
return p
}
const q = l < 0.5 ? l * (1 + s) : l + s - l * s
const p = 2 * l - q
r = hue2rgb(p, q, h + 1 / 3)
g = hue2rgb(p, q, h)
b = hue2rgb(p, q, h - 1 / 3)
}
const toHex = x => {
const hex = Math.round(x * 255).toString(16)
return hex.length === 1 ? ''0'' + hex : hex
}
return `#${toHex(r)}${toHex(g)}${toHex(b)}`
}
//const lineReader = readline.createInterface({
// input: fs.createReadStream(''../graphics.txt'')
//})
//lineReader.on(''line'', line => {
//const item = JSON.parse(line)
const item = strokeData
const charCode = item.character.charCodeAt()
const startingPoints = item.medians.map(i=>({x:i[i.length % 2 == 0 ? Math.floor(i.length/2)-1 : Math.floor(i.length/2)][0],y:i[i.length % 2 == 0 ? Math.floor(i.length/2)-1 : Math.floor(i.length/2)][1]}))
let pathes = ``
let strokeStartPositions = []
for (const [i,stroke] of item.strokes.entries()) {
const strokeColor = hslToRgb(360/item.strokes.length*(i+1), colorSaturation, colorLightness)
pathes += ` <path d="${stroke}" fill="${strokeColor}"/>/n`
const x = -20+startingPoints[i].x
const y = 915+fontSize/2-startingPoints[i].y
strokeStartPositions[i] = [x, y]
}
function repositionAlgorithm(position, attractedTo, repulsedFrom) {
// ?
return position
}
let numberPositions = []
for (const [i,currentPosition] of strokeStartPositions.entries()) {
const oldX = currentPosition[0]
const oldY = currentPosition[1]
const repulsedFrom = strokeStartPositions.slice().splice(i,1) // all positions but the current one
const attractedTo = [currentPosition]
const newPosition = repositionAlgorithm(currentPosition, attractedTo, repulsedFrom)
numberPositions[i] = newPosition
const newX = newPosition[0]
const newY = newPosition[1]
//console.log(`Moved from (${oldX}|${oldY}) to (${newX}|${newY}).`)
}
let texts = ``
for (const [i,pos] of numberPositions.entries()) {
const textColor = "#FFFFFF" //const textColor = hslToRgb(360/pathEls.length*(i+1), colorSaturation, colorLightness)
const x = pos[0]
const y = pos[1]
texts += ` <text fill="${textColor}" x="${x}" y="${y}">${i+1}</text>/n`
}
const newSvgContent = `<svg version="1.1" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<style type="text/css">
text {
font-family: Helvetica;
font-size: ${fontSize}px;
paint-order: stroke;
stroke: #000000;
stroke-width: 6px;
stroke-linecap: butt;
stroke-linejoin: miter;
font-weight: 800;
}
</style>
<g stroke="lightgray" stroke-dasharray="1,1" stroke-width="1" transform="scale(4, 4)">
<line x1="0" y1="0" x2="256" y2="256"/>
<line x1="256" y1="0" x2="0" y2="256"/>
<line x1="128" y1="0" x2="128" y2="256"/>
<line x1="0" y1="128" x2="256" y2="128"/>
</g>
<g transform="scale(1, -1) translate(0, -900)">
${pathes} </g>
<g>
${texts} </g>
</svg>`
const svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg")
svgEl.innerHTML = newSvgContent
document.body.appendChild(svgEl)
//fs.outputFile(`../svgs-still/${charCode}-still.svg`, newSvgContent).then(() => {
// //console.log(`../svgs-still/${charCode}-still.svg written`) // logging severely reduces performance
//}).catch(e=>console.error(new Error(e)))
//})
html, body {
width: 100%;
height: 100%;
}
svg {
width: 100%;
height: 100%;
}
Estoy generando un par de imágenes svg como esta:
<svg version="1.1" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<style type="text/css">
text {
font-family: Helvetica;
font-size: 60px;
paint-order: stroke;
stroke: #000000;
stroke-width: 6px;
stroke-linecap: butt;
stroke-linejoin: miter;
font-weight: 800;
}
</style>
<g stroke="lightgray" stroke-dasharray="1,1" stroke-width="1" transform="scale(4, 4)">
<line x1="0" y1="0" x2="256" y2="256"/>
<line x1="256" y1="0" x2="0" y2="256"/>
<line x1="128" y1="0" x2="128" y2="256"/>
<line x1="0" y1="128" x2="256" y2="128"/>
</g>
<g transform="scale(1, -1) translate(0, -900)">
<path d="M 128 445 Q 222 525 301 653 Q 319 685 334 709 L 343 725 Q 351 746 367 763 Q 376 772 374 782 Q 374 791 361 801 Q 327 827 298 819 Q 290 819 293 806 Q 310 732 219 604 L 171 539 Q 133 492 33 396 Q 26 392 35 390 Q 43 390 110 431 L 128 445 Z" fill="#cc2f00"/>
<path d="M 334 709 Q 386 675 447 629 Q 461 617 472 615 Q 478 615 482 624 Q 488 634 474 663 Q 459 700 343 725 C 314 731 309 725 334 709 Z" fill="#cc5e00"/>
<path d="M 253 553 Q 224 546 246 534 Q 276 517 325 531 Q 423 558 435 563 Q 439 567 439 572 Q 437 586 406 590 Q 388 593 316 566 L 253 553 Z" fill="#cc8d00"/>
<path d="M 147 441 Q 143 445 128 445 C 101 446 101 446 110 431 Q 125 406 140 365 Q 153 331 162 323 Q 174 311 176 321 Q 178 329 175 343 L 171 363 Q 161 394 153 422 C 148 441 148 441 147 441 Z" fill="#ccbc00"/>
<path d="M 255 464 Q 233 477 223 476 Q 213 475 180 456 Q 172 453 147 441 C 120 428 124 416 153 422 Q 161 423 181 431 Q 212 442 216 437 Q 221 433 216 383 C 213 353 241 353 245 383 Q 251 431 265 445 C 272 454 272 454 255 464 Z" fill="#adcc00"/>
<path d="M 175 343 Q 180 343 186 345 Q 216 357 254 367 Q 261 368 259 373 Q 259 377 245 383 L 216 383 Q 213 383 171 363 C 144 350 145 341 175 343 Z" fill="#7ecc00"/>
<path d="M 285 468 Q 261 473 255 470 Q 255 468 255 464 C 255 459 255 469 265 445 Q 272 429 278 405 Q 288 371 295 363 Q 305 352 310 361 Q 310 367 310 371 L 308 392 Q 298 434 298 450 C 297 465 297 465 285 468 Z" fill="#4ecc00"/>
<path d="M 374 410 Q 386 441 396 454 Q 404 463 406 466 C 413 475 413 475 398 484 L 374 498 Q 360 506 347 499 Q 327 482 285 468 C 257 458 270 438 298 450 Q 300 451 310 455 Q 351 468 355 462 Q 357 461 357 459 Q 359 445 342 406 C 330 378 363 382 374 410 Z" fill="#1fcc00"/>
<path d="M 310 371 Q 323 378 385 390 Q 392 391 392 398 Q 390 402 374 410 C 368 413 368 413 342 406 Q 333 404 308 392 C 281 379 283 358 310 371 Z" fill="#00cc10"/>
<path d="M 406 466 Q 424 400 427 396 Q 437 382 441 392 L 443 406 L 441 425 Q 434 470 433 482 C 432 494 432 494 420 500 Q 419 501 416 502 Q 398 508 394 504 Q 390 500 398 484 L 406 466 Z" fill="#00cc3f"/>
<path d="M 519 439 Q 529 478 541 490 Q 555 506 540 514 Q 514 526 506 528 Q 496 531 484 525 Q 454 510 420 500 C 391 491 403 477 433 482 Q 445 484 454 487 Q 490 496 495 490 Q 496 490 496 487 Q 500 472 490 439 C 482 410 511 410 519 439 Z" fill="#00cc6e"/>
<path d="M 443 406 Q 449 406 455 407 Q 490 416 527 419 Q 534 420 534 425 Q 534 429 519 439 C 519 439 519 439 490 439 Q 488 441 441 425 C 413 415 413 405 443 406 Z" fill="#00cc9d"/>
<path d="M 169 267 Q 151 272 139 272 Q 134 270 133 267 Q 132 264 139 246 Q 167 188 145 104 Q 129 91 142 62 Q 151 43 160 37 Q 166 27 173 32 Q 192 50 192 163 Q 192 202 192 242 C 192 261 192 261 169 267 Z" fill="#00cccc"/>
<path d="M 347 278 Q 402 290 419 284 Q 433 278 435 258 Q 439 207 429 114 Q 431 98 416 98 Q 396 98 386 100 Q 380 100 377 98 Q 369 94 398 69 Q 419 47 431 22 Q 441 15 451 20 Q 455 22 459 25 Q 480 58 482 158 Q 476 267 486 283 Q 494 292 488 302 Q 484 307 472 314 Q 437 333 419 323 Q 409 319 388 316 Q 271 292 169 267 C 140 260 164 232 192 242 Q 208 248 231 254 L 248 257 Q 272 265 323 274 L 347 278 Z" fill="#009dcc"/>
<path d="M 244 188 Q 235 186 214 178 Q 201 174 217 164 Q 226 158 246 163 L 274 170 L 332 184 L 367 190 Q 376 194 396 198 Q 402 199 406 202 Q 415 209 397 218 Q 379 227 368 223 L 332 216 Q 327 216 274 196 L 244 188 Z" fill="#006ecc"/>
<path d="M 231 254 Q 231 251 233 246 Q 241 216 244 188 L 246 163 Q 252 102 256 93 Q 265 78 270 87 Q 274 97 274 170 L 274 196 Q 274 208 275 218 Q 276 239 274 243 Q 271 250 248 257 C 229 263 229 263 231 254 Z" fill="#003fcc"/>
<path d="M 332 184 Q 332 80 342 71 Q 343 71 347 71 Q 351 73 354 81 Q 360 98 367 190 L 368 223 Q 368 229 372 243 Q 376 259 368 267 Q 355 276 347 278 C 319 288 317 289 323 274 Q 331 257 332 216 L 332 184 Z" fill="#0010cc"/>
<path d="M 698 651 L 862 688 Q 908 700 914 708 Q 923 714 918 723 Q 913 729 903 734 Q 864 751 825 733 Q 808 727 794 723 Q 696 689 582 674 Q 548 668 572 653 Q 606 634 659 641 L 698 651 Z" fill="#1f00cc"/>
<path d="M 657 540 Q 668 553 678 566 Q 699 594 712 608 Q 734 624 698 651 C 674 669 660 671 659 641 Q 659 617 641 563 Q 631 549 630 535 C 626 505 638 517 657 540 Z" fill="#4e00cc"/>
<path d="M 596 531 Q 594 533 591 535 Q 571 549 557 543 Q 555 541 555 536 Q 555 525 561 515 Q 582 472 572 295 Q 566 269 566 245 Q 566 198 586 180 Q 595 171 604 180 Q 611 192 612 201 L 613 229 Q 615 239 615 265 Q 613 294 613 308 L 613 331 L 613 406 L 613 431 Q 613 455 615 506 C 615 517 615 517 596 531 Z" fill="#7e00cc"/>
<path d="M 809 223 Q 809 216 817 201 Q 831 173 841 174 Q 855 176 869 209 Q 874 219 876 225 Q 882 242 878 272 Q 868 363 866 451 Q 866 463 868 472 Q 871 489 885 517 Q 892 533 886 539 Q 861 561 817 579 Q 802 583 780 576 Q 774 574 657 540 L 630 535 Q 608 533 596 531 C 566 527 585 503 615 506 Q 627 506 642 510 Q 710 527 768 538 Q 789 542 795 531 Q 809 514 811 453 Q 817 290 811 249 L 809 223 Z" fill="#ad00cc"/>
<path d="M 613 406 L 618 406 Q 686 417 747 423 Q 768 428 759 440 Q 749 452 727 456 Q 710 459 613 431 C 584 423 583 406 613 406 Z" fill="#cc00bc"/>
<path d="M 613 308 L 623 308 Q 708 317 761 319 Q 783 323 774 334 Q 762 357 718 354 Q 690 352 613 331 C 584 323 583 308 613 308 Z" fill="#cc008d"/>
<path d="M 612 201 L 621 201 L 809 223 C 839 226 835 232 811 249 Q 800 259 777 258 Q 753 257 613 229 C 584 223 582 201 612 201 Z" fill="#cc005e"/>
<path d="M 623 161 Q 608 95 455 -29 Q 447 -35 447 -37 Q 446 -41 460 -39 Q 480 -37 504 -27 Q 562 -4 640 76 Q 686 123 690 126 Q 695 131 697 137 Q 704 157 671 167 Q 652 174 641 174 Q 627 174 623 161 Z" fill="#cc002f"/>
<path d="M 757 157 Q 757 153 757 151 Q 758 140 774 118 Q 829 47 864 -7 Q 877 -29 892 -36 Q 898 -37 903 -33 Q 923 -23 917 25 Q 914 77 768 163 Q 761 169 757 157 Z" fill="#cc0000"/>
</g>
<g>
<text fill="#FFFFFF" x="306" y="131">1</text>
<text fill="#FFFFFF" x="343" y="231">2</text>
<text fill="#FFFFFF" x="247" y="396">3</text>
<text fill="#FFFFFF" x="119" y="511">4</text>
<text fill="#FFFFFF" x="157" y="511">5</text>
<text fill="#FFFFFF" x="180" y="591">6</text>
<text fill="#FFFFFF" x="261" y="473">7</text>
<text fill="#FFFFFF" x="294" y="472">8</text>
<text fill="#FFFFFF" x="312" y="564">9</text>
<text fill="#FFFFFF" x="399" y="420">10</text>
<text fill="#FFFFFF" x="428" y="421">11</text>
<text fill="#FFFFFF" x="449" y="509">12</text>
<text fill="#FFFFFF" x="140" y="655">13</text>
<text fill="#FFFFFF" x="176" y="654">14</text>
<text fill="#FFFFFF" x="226" y="747">15</text>
<text fill="#FFFFFF" x="236" y="669">16</text>
<text fill="#FFFFFF" x="330" y="649">17</text>
<text fill="#FFFFFF" x="574" y="255">18</text>
<text fill="#FFFFFF" x="667" y="284">19</text>
<text fill="#FFFFFF" x="563" y="383">20</text>
<text fill="#FFFFFF" x="601" y="395">21</text>
<text fill="#FFFFFF" x="616" y="508">22</text>
<text fill="#FFFFFF" x="618" y="606">23</text>
<text fill="#FFFFFF" x="617" y="712">24</text>
<text fill="#FFFFFF" x="680" y="777">25</text>
<text fill="#FFFFFF" x="766" y="766">26</text>
</g>
</svg>
Estos svgs son diagramas de orden de trazo para caracteres chinos. Así que la imagen muestra cada trazo en un color diferente y en el punto de inicio de cada trazo se muestra un número para visualizar en qué orden se deben dibujar.
El problema con mi enfoque es que los números pueden superponerse o estar demasiado cerca unos de otros, lo que dificulta la lectura.
Me gustaría implementar un algoritmo que calcula mejores posiciones para cada coordenada. Estaba pensando en algo que te permitiría especificar una atracción hacia el punto de partida y una repulsión hacia todas las coordenadas de todos los demás números.
¿Es este algoritmo algo que ya existe y tiene un nombre? ¿Cómo podría hacerse en JavaScript?
Aquí hay un fragmento de código con las coordenadas para las posiciones de inicio del trazo en las que se dibujan los números actualmente:
let strokeStartPositions = [
[306, 131],
[343, 231],
[247, 396],
[119, 511],
[157, 511],
[180, 591],
[261, 473],
[294, 472],
[312, 564],
[399, 420],
[428, 421],
[449, 509],
[140, 655],
[176, 654],
[226, 747],
[236, 669],
[330, 649],
[574, 255],
[667, 284],
[563, 383],
[601, 395],
[616, 508],
[618, 606],
[617, 712],
[680, 777],
[766, 766]
]
function repositionAlgorithm(position, attractedTo, repulsedFrom) {
// ?
return position
}
let numberPositions = []
for (let [i,currentPosition] of strokeStartPositions.entries()) {
let oldX = currentPosition[0]
let oldY = currentPosition[1]
let repulsedFrom = strokeStartPositions.slice()
repulsedFrom.splice(i,1) // all positions but the current one
let attractedTo = [currentPosition]
let newPosition = repositionAlgorithm(currentPosition, attractedTo, repulsedFrom)
numberPositions[i] = newPosition
let newX = newPosition[0]
let newY = newPosition[1]
console.log(`Moved from (${oldX}|${oldY}) to (${newX}|${newY}).`)
}
Reducir el tamaño del texto de los números de dos dígitos ayudó un poco, pero aún se pueden superponer y realmente no puedo reducir el tamaño del texto porque se volverá ilegible.
Aquí hay un violín si quiere jugar con él: https://jsfiddle.net/t2o7p1ea/31/ La parte relevante comienza en la línea 62 y termina en la línea 78.
Edit: He encontrado una solución que funciona bien. Pero no es perfecto. Si se te ocurre algo mejor. Con mucho gusto te concederé la recompensa.
Después de días de juguetear, finalmente se me ocurrió una idea que de alguna manera funciona.
La idea era: tomar todos los puntos de inicio del trazo y simular que son círculos con un diámetro aproximadamente tan grande como el tamaño de fuente * número de dígitos.
while any circles intercept each other
iterate over all circles
iterate over all circles
if the circle of this iteration is the same as the circle of the outer iteration
skip this iteration (continue)
if circle of this and the outer iteration intercept
calculate a point that from the perspective of circle1
is about 1 pixel behind circle 2
and move circle2 to that new position
calculate a point that from the perspective of circle2
is about 1 pixel behind circle 1
and move circle1 to that new position
(Decidí moverlos píxel por píxel para que haya una iteración para que cada círculo reaccione después de cada cambio en caso de que se estén interceptando nuevos círculos ahora).
Después de ajustar y ajustar los parámetros, obtuve un resultado que se ve bastante bien. Todavía no es perfecto porque estoy tratando los números como si fueran círculos y si los hago demasiado grandes, los números se eliminan para siempre y el ciclo no termina.
Además, volví a usar números de colores y cambié los bordes a blanco en lugar de negro para mejorar la legibilidad.
//#!/usr/bin/env node
//const fs = require(''fs-extra'')
//const readline = require(''readline'')
const strokeData = {"character":"龥","strokes":["M 128 445 Q 222 525 301 653 Q 319 685 334 709 L 343 725 Q 351 746 367 763 Q 376 772 374 782 Q 374 791 361 801 Q 327 827 298 819 Q 290 819 293 806 Q 310 732 219 604 L 171 539 Q 133 492 33 396 Q 26 392 35 390 Q 43 390 110 431 L 128 445 Z","M 334 709 Q 386 675 447 629 Q 461 617 472 615 Q 478 615 482 624 Q 488 634 474 663 Q 459 700 343 725 C 314 731 309 725 334 709 Z","M 253 553 Q 224 546 246 534 Q 276 517 325 531 Q 423 558 435 563 Q 439 567 439 572 Q 437 586 406 590 Q 388 593 316 566 L 253 553 Z","M 147 441 Q 143 445 128 445 C 101 446 101 446 110 431 Q 125 406 140 365 Q 153 331 162 323 Q 174 311 176 321 Q 178 329 175 343 L 171 363 Q 161 394 153 422 C 148 441 148 441 147 441 Z","M 255 464 Q 233 477 223 476 Q 213 475 180 456 Q 172 453 147 441 C 120 428 124 416 153 422 Q 161 423 181 431 Q 212 442 216 437 Q 221 433 216 383 C 213 353 241 353 245 383 Q 251 431 265 445 C 272 454 272 454 255 464 Z","M 175 343 Q 180 343 186 345 Q 216 357 254 367 Q 261 368 259 373 Q 259 377 245 383 L 216 383 Q 213 383 171 363 C 144 350 145 341 175 343 Z","M 285 468 Q 261 473 255 470 Q 255 468 255 464 C 255 459 255 469 265 445 Q 272 429 278 405 Q 288 371 295 363 Q 305 352 310 361 Q 310 367 310 371 L 308 392 Q 298 434 298 450 C 297 465 297 465 285 468 Z","M 374 410 Q 386 441 396 454 Q 404 463 406 466 C 413 475 413 475 398 484 L 374 498 Q 360 506 347 499 Q 327 482 285 468 C 257 458 270 438 298 450 Q 300 451 310 455 Q 351 468 355 462 Q 357 461 357 459 Q 359 445 342 406 C 330 378 363 382 374 410 Z","M 310 371 Q 323 378 385 390 Q 392 391 392 398 Q 390 402 374 410 C 368 413 368 413 342 406 Q 333 404 308 392 C 281 379 283 358 310 371 Z","M 406 466 Q 424 400 427 396 Q 437 382 441 392 L 443 406 L 441 425 Q 434 470 433 482 C 432 494 432 494 420 500 Q 419 501 416 502 Q 398 508 394 504 Q 390 500 398 484 L 406 466 Z","M 519 439 Q 529 478 541 490 Q 555 506 540 514 Q 514 526 506 528 Q 496 531 484 525 Q 454 510 420 500 C 391 491 403 477 433 482 Q 445 484 454 487 Q 490 496 495 490 Q 496 490 496 487 Q 500 472 490 439 C 482 410 511 410 519 439 Z","M 443 406 Q 449 406 455 407 Q 490 416 527 419 Q 534 420 534 425 Q 534 429 519 439 C 519 439 519 439 490 439 Q 488 441 441 425 C 413 415 413 405 443 406 Z","M 169 267 Q 151 272 139 272 Q 134 270 133 267 Q 132 264 139 246 Q 167 188 145 104 Q 129 91 142 62 Q 151 43 160 37 Q 166 27 173 32 Q 192 50 192 163 Q 192 202 192 242 C 192 261 192 261 169 267 Z","M 347 278 Q 402 290 419 284 Q 433 278 435 258 Q 439 207 429 114 Q 431 98 416 98 Q 396 98 386 100 Q 380 100 377 98 Q 369 94 398 69 Q 419 47 431 22 Q 441 15 451 20 Q 455 22 459 25 Q 480 58 482 158 Q 476 267 486 283 Q 494 292 488 302 Q 484 307 472 314 Q 437 333 419 323 Q 409 319 388 316 Q 271 292 169 267 C 140 260 164 232 192 242 Q 208 248 231 254 L 248 257 Q 272 265 323 274 L 347 278 Z","M 244 188 Q 235 186 214 178 Q 201 174 217 164 Q 226 158 246 163 L 274 170 L 332 184 L 367 190 Q 376 194 396 198 Q 402 199 406 202 Q 415 209 397 218 Q 379 227 368 223 L 332 216 Q 327 216 274 196 L 244 188 Z","M 231 254 Q 231 251 233 246 Q 241 216 244 188 L 246 163 Q 252 102 256 93 Q 265 78 270 87 Q 274 97 274 170 L 274 196 Q 274 208 275 218 Q 276 239 274 243 Q 271 250 248 257 C 229 263 229 263 231 254 Z","M 332 184 Q 332 80 342 71 Q 343 71 347 71 Q 351 73 354 81 Q 360 98 367 190 L 368 223 Q 368 229 372 243 Q 376 259 368 267 Q 355 276 347 278 C 319 288 317 289 323 274 Q 331 257 332 216 L 332 184 Z","M 698 651 L 862 688 Q 908 700 914 708 Q 923 714 918 723 Q 913 729 903 734 Q 864 751 825 733 Q 808 727 794 723 Q 696 689 582 674 Q 548 668 572 653 Q 606 634 659 641 L 698 651 Z","M 657 540 Q 668 553 678 566 Q 699 594 712 608 Q 734 624 698 651 C 674 669 660 671 659 641 Q 659 617 641 563 Q 631 549 630 535 C 626 505 638 517 657 540 Z","M 596 531 Q 594 533 591 535 Q 571 549 557 543 Q 555 541 555 536 Q 555 525 561 515 Q 582 472 572 295 Q 566 269 566 245 Q 566 198 586 180 Q 595 171 604 180 Q 611 192 612 201 L 613 229 Q 615 239 615 265 Q 613 294 613 308 L 613 331 L 613 406 L 613 431 Q 613 455 615 506 C 615 517 615 517 596 531 Z","M 809 223 Q 809 216 817 201 Q 831 173 841 174 Q 855 176 869 209 Q 874 219 876 225 Q 882 242 878 272 Q 868 363 866 451 Q 866 463 868 472 Q 871 489 885 517 Q 892 533 886 539 Q 861 561 817 579 Q 802 583 780 576 Q 774 574 657 540 L 630 535 Q 608 533 596 531 C 566 527 585 503 615 506 Q 627 506 642 510 Q 710 527 768 538 Q 789 542 795 531 Q 809 514 811 453 Q 817 290 811 249 L 809 223 Z","M 613 406 L 618 406 Q 686 417 747 423 Q 768 428 759 440 Q 749 452 727 456 Q 710 459 613 431 C 584 423 583 406 613 406 Z","M 613 308 L 623 308 Q 708 317 761 319 Q 783 323 774 334 Q 762 357 718 354 Q 690 352 613 331 C 584 323 583 308 613 308 Z","M 612 201 L 621 201 L 809 223 C 839 226 835 232 811 249 Q 800 259 777 258 Q 753 257 613 229 C 584 223 582 201 612 201 Z","M 623 161 Q 608 95 455 -29 Q 447 -35 447 -37 Q 446 -41 460 -39 Q 480 -37 504 -27 Q 562 -4 640 76 Q 686 123 690 126 Q 695 131 697 137 Q 704 157 671 167 Q 652 174 641 174 Q 627 174 623 161 Z","M 757 157 Q 757 153 757 151 Q 758 140 774 118 Q 829 47 864 -7 Q 877 -29 892 -36 Q 898 -37 903 -33 Q 923 -23 917 25 Q 914 77 768 163 Q 761 169 757 157 Z"],"medians":[[[306,809],[331,777],[299,694],[261,628],[204,549],[134,471],[36,394]],[[343,709],[352,714],[418,680],[449,658],[470,627]],[[247,544],[296,542],[396,571],[430,571]],[[119,429],[133,427],[170,326]],[[157,429],[195,452],[229,454],[238,443],[232,399],[223,394]],[[180,349],[185,359],[223,372],[254,372]],[[261,467],[274,459],[282,444],[303,366]],[[294,468],[302,463],[319,466],[353,482],[378,469],[365,424],[346,415]],[[312,376],[342,394],[386,397]],[[399,500],[418,476],[436,400]],[[428,499],[438,494],[495,510],[511,504],[517,496],[513,469],[509,453],[497,445]],[[449,411],[455,420],[472,424],[512,429],[528,425]],[[140,265],[165,240],[171,216],[174,151],[162,84],[168,40]],[[176,266],[196,259],[400,303],[434,303],[452,293],[458,266],[459,170],[452,94],[438,71],[383,94]],[[226,173],[325,199],[399,208]],[[236,251],[256,233],[264,91]],[[330,271],[351,252],[345,75]],[[574,665],[607,659],[648,661],[853,714],[907,718]],[[667,636],[685,620],[651,553],[636,541]],[[563,537],[592,497],[590,223],[595,187]],[[601,525],[632,521],[792,558],[815,551],[843,521],[837,466],[846,276],[841,185]],[[616,412],[628,424],[686,434],[729,439],[750,433]],[[618,314],[628,322],[706,335],[741,336],[766,328]],[[617,208],[629,219],[775,239],[792,239],[800,230]],[[680,143],[651,136],[615,87],[554,27],[503,-10],[454,-36]],[[766,154],[877,36],[893,-2],[894,-22]]]}
const colorSaturation = 100 // 0-255
const colorLightness = 40 // 0-255
const fontSize = 80
const fontBorderWidth = 8
// helper function
function hslToRgb(h, s, l) { // To generate rainbow colors
h /= 360
s /= 100
l /= 100
let r, g, b
if (s === 0) {
r = g = b = l // achromatic
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1
if (t > 1) t -= 1
if (t < 1 / 6) return p + (q - p) * 6 * t
if (t < 1 / 2) return q
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6
return p
}
const q = l < 0.5 ? l * (1 + s) : l + s - l * s
const p = 2 * l - q
r = hue2rgb(p, q, h + 1 / 3)
g = hue2rgb(p, q, h)
b = hue2rgb(p, q, h - 1 / 3)
}
const toHex = x => {
const hex = Math.round(x * 255).toString(16)
return hex.length === 1 ? ''0'' + hex : hex
}
return `#${toHex(r)}${toHex(g)}${toHex(b)}`
}
// helper function
function circlesIntersect(circle1, circle2) {
const deltaX = circle1.x - circle2.x
const deltaY = circle1.y - circle2.y
const rSum = circle1.r + circle2.r
return deltaX*deltaX + deltaY*deltaY <= rSum * rSum
}
// helper function
function anyCircleIntersects(circleArr) {
for (const circle1 of circleArr) {
for (const circle2 of circleArr) {
if (circle1 !== circle2) {
if (circlesIntersect(circle1,circle2))
return true
}
}
}
}
// helper function
function calculate_point_on_other_side_of_p2(p1, p2, distance_p2_to_p3) {
const deltaX = p1[0]-p2[0]
const deltaY = p1[1]-p2[1]
const distance_p1_to_p2 = Math.sqrt(deltaX*deltaX + deltaY*deltaY)
const scale = distance_p2_to_p3 / distance_p1_to_p2
let p3 = []
p3[0] = p2[0] - deltaX * scale
p3[1] = p2[1] - deltaY * scale
return p3
}
//const lineReader = readline.createInterface({
// input: fs.createReadStream(''../graphics.txt'')
//})
//lineReader.on(''line'', line => {
//const item = JSON.parse(line)
const item = strokeData
const charCode = item.character.charCodeAt()
const startingPoints = item.medians.map(i=>({x:i[0][0],y:i[0][1]}))
let pathes = ``
let strokeStartPositions = []
for (const [i,stroke] of item.strokes.entries()) {
const strokeColor = hslToRgb(360/item.strokes.length*(i+1), colorSaturation, colorLightness)
pathes += ` <path d="${stroke}" fill="${strokeColor}"/>/n`
let x = startingPoints[i].x
x = i<9 ? x-fontSize/4 : x-fontSize/2
//x = x-fontSize/2
let y = 900+fontSize/2-startingPoints[i].y
//y = y-fontSize/2
strokeStartPositions[i] = [x, y]
}
const t0 = Date.now()
// while any circles intercept each other
while (anyCircleIntersects(strokeStartPositions.map((pos,i)=>{return {r:i<9 ? fontSize/2.5 : fontSize/1.75, x:pos[0], y:pos[1]}}))) {
if (Date.now() > t0+5000) {
alert("Timeout!")
break
}
// iterate over all circles
for (const [i,p1] of strokeStartPositions.entries()) {
const p1X = p1[0]
const p1Y = p1[1]
const p1Radius = i<9 ? fontSize/2.5 : fontSize/1.75
// iterate over all circles
for (const [j,p2] of strokeStartPositions.entries()) {
if (i === j) // if the circle of this iteration is the same as the circle of the outer iteration
continue // skip this iteration (continue)
const p2X = p2[0]
const p2Y = p2[1]
// If for some reason 2 circles are on the exact same position, move one of them a little
if (p1X === p2X && p1Y === p2Y)
p2[0] = p2[0]+1 // p2[1] = p2[1]+1
const p2Radius = j<9 ? fontSize/2.5 : fontSize/1.75
// if the circle of this and the outer iteration intercept
if (circlesIntersect({r:p1Radius, x:p1X, y:p1Y}, {r:p2Radius, x:p2X, y:p2Y})) {
// calculate a point that from the perspective of circle1
// is about 1 pixel behind circle 2
let newP2 = calculate_point_on_other_side_of_p2(p1, p2, 1)
// calculate a point that from the perspective of circle2
// is about 1 pixel behind circle 1
let newP1 = calculate_point_on_other_side_of_p2(p2, p1, 1)
// and move circle2 to the newP2 position
strokeStartPositions[i][0] = Math.round(newP1[0])
strokeStartPositions[i][1] = Math.round(newP1[1])
// and move circle1 to the newP1 position
strokeStartPositions[j][0] = Math.round(newP2[0])
strokeStartPositions[j][1] = Math.round(newP2[1])
}
}
}
}
let texts = ``
for (const [i,pos] of strokeStartPositions.entries()) {
const textColor = hslToRgb(360/strokeStartPositions.length*(i+1), colorSaturation, colorLightness)
//const textColor = ''#FFFFFF''
const x = pos[0]
const y = pos[1]
texts += ` <text fill="${textColor}" x="${x}" y="${y}">${i+1}</text>/n`
}
const newSvgContent = `<svg version="1.1" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<style type="text/css">
text {
font-family: Helvetica;
font-size: ${fontSize}px;
paint-order: stroke;
stroke: #FFFFFF;
stroke-width: ${fontBorderWidth}px;
stroke-linecap: butt;
stroke-linejoin: miter;
font-weight: bold;
}
</style>
<g stroke="lightgray" stroke-dasharray="1,1" stroke-width="1" transform="scale(4, 4)">
<line x1="0" y1="0" x2="256" y2="256"/>
<line x1="256" y1="0" x2="0" y2="256"/>
<line x1="128" y1="0" x2="128" y2="256"/>
<line x1="0" y1="128" x2="256" y2="128"/>
</g>
<g transform="scale(1, -1) translate(0, -900)">
${pathes} </g>
<g>
${texts} </g>
</svg>`
const svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg")
svgEl.innerHTML = newSvgContent
document.body.appendChild(svgEl)
//fs.outputFile(`../svgs-still/${charCode}-still.svg`, newSvgContent).then(() => {
// //console.log(`../svgs-still/${charCode}-still.svg written`) // logging severely reduces performance
//}).catch(e=>console.error(new Error(e)))
//})
html, body {
width: 100%;
height: 100%;
}
svg {
width: 100%;
height: 100%;
}
Quizás sería útil colorear cada número con el mismo color del trazo respectivo:
const strokeData = {"character":"龥","strokes":["M 128 445 Q 222 525 301 653 Q 319 685 334 709 L 343 725 Q 351 746 367 763 Q 376 772 374 782 Q 374 791 361 801 Q 327 827 298 819 Q 290 819 293 806 Q 310 732 219 604 L 171 539 Q 133 492 33 396 Q 26 392 35 390 Q 43 390 110 431 L 128 445 Z","M 334 709 Q 386 675 447 629 Q 461 617 472 615 Q 478 615 482 624 Q 488 634 474 663 Q 459 700 343 725 C 314 731 309 725 334 709 Z","M 253 553 Q 224 546 246 534 Q 276 517 325 531 Q 423 558 435 563 Q 439 567 439 572 Q 437 586 406 590 Q 388 593 316 566 L 253 553 Z","M 147 441 Q 143 445 128 445 C 101 446 101 446 110 431 Q 125 406 140 365 Q 153 331 162 323 Q 174 311 176 321 Q 178 329 175 343 L 171 363 Q 161 394 153 422 C 148 441 148 441 147 441 Z","M 255 464 Q 233 477 223 476 Q 213 475 180 456 Q 172 453 147 441 C 120 428 124 416 153 422 Q 161 423 181 431 Q 212 442 216 437 Q 221 433 216 383 C 213 353 241 353 245 383 Q 251 431 265 445 C 272 454 272 454 255 464 Z","M 175 343 Q 180 343 186 345 Q 216 357 254 367 Q 261 368 259 373 Q 259 377 245 383 L 216 383 Q 213 383 171 363 C 144 350 145 341 175 343 Z","M 285 468 Q 261 473 255 470 Q 255 468 255 464 C 255 459 255 469 265 445 Q 272 429 278 405 Q 288 371 295 363 Q 305 352 310 361 Q 310 367 310 371 L 308 392 Q 298 434 298 450 C 297 465 297 465 285 468 Z","M 374 410 Q 386 441 396 454 Q 404 463 406 466 C 413 475 413 475 398 484 L 374 498 Q 360 506 347 499 Q 327 482 285 468 C 257 458 270 438 298 450 Q 300 451 310 455 Q 351 468 355 462 Q 357 461 357 459 Q 359 445 342 406 C 330 378 363 382 374 410 Z","M 310 371 Q 323 378 385 390 Q 392 391 392 398 Q 390 402 374 410 C 368 413 368 413 342 406 Q 333 404 308 392 C 281 379 283 358 310 371 Z","M 406 466 Q 424 400 427 396 Q 437 382 441 392 L 443 406 L 441 425 Q 434 470 433 482 C 432 494 432 494 420 500 Q 419 501 416 502 Q 398 508 394 504 Q 390 500 398 484 L 406 466 Z","M 519 439 Q 529 478 541 490 Q 555 506 540 514 Q 514 526 506 528 Q 496 531 484 525 Q 454 510 420 500 C 391 491 403 477 433 482 Q 445 484 454 487 Q 490 496 495 490 Q 496 490 496 487 Q 500 472 490 439 C 482 410 511 410 519 439 Z","M 443 406 Q 449 406 455 407 Q 490 416 527 419 Q 534 420 534 425 Q 534 429 519 439 C 519 439 519 439 490 439 Q 488 441 441 425 C 413 415 413 405 443 406 Z","M 169 267 Q 151 272 139 272 Q 134 270 133 267 Q 132 264 139 246 Q 167 188 145 104 Q 129 91 142 62 Q 151 43 160 37 Q 166 27 173 32 Q 192 50 192 163 Q 192 202 192 242 C 192 261 192 261 169 267 Z","M 347 278 Q 402 290 419 284 Q 433 278 435 258 Q 439 207 429 114 Q 431 98 416 98 Q 396 98 386 100 Q 380 100 377 98 Q 369 94 398 69 Q 419 47 431 22 Q 441 15 451 20 Q 455 22 459 25 Q 480 58 482 158 Q 476 267 486 283 Q 494 292 488 302 Q 484 307 472 314 Q 437 333 419 323 Q 409 319 388 316 Q 271 292 169 267 C 140 260 164 232 192 242 Q 208 248 231 254 L 248 257 Q 272 265 323 274 L 347 278 Z","M 244 188 Q 235 186 214 178 Q 201 174 217 164 Q 226 158 246 163 L 274 170 L 332 184 L 367 190 Q 376 194 396 198 Q 402 199 406 202 Q 415 209 397 218 Q 379 227 368 223 L 332 216 Q 327 216 274 196 L 244 188 Z","M 231 254 Q 231 251 233 246 Q 241 216 244 188 L 246 163 Q 252 102 256 93 Q 265 78 270 87 Q 274 97 274 170 L 274 196 Q 274 208 275 218 Q 276 239 274 243 Q 271 250 248 257 C 229 263 229 263 231 254 Z","M 332 184 Q 332 80 342 71 Q 343 71 347 71 Q 351 73 354 81 Q 360 98 367 190 L 368 223 Q 368 229 372 243 Q 376 259 368 267 Q 355 276 347 278 C 319 288 317 289 323 274 Q 331 257 332 216 L 332 184 Z","M 698 651 L 862 688 Q 908 700 914 708 Q 923 714 918 723 Q 913 729 903 734 Q 864 751 825 733 Q 808 727 794 723 Q 696 689 582 674 Q 548 668 572 653 Q 606 634 659 641 L 698 651 Z","M 657 540 Q 668 553 678 566 Q 699 594 712 608 Q 734 624 698 651 C 674 669 660 671 659 641 Q 659 617 641 563 Q 631 549 630 535 C 626 505 638 517 657 540 Z","M 596 531 Q 594 533 591 535 Q 571 549 557 543 Q 555 541 555 536 Q 555 525 561 515 Q 582 472 572 295 Q 566 269 566 245 Q 566 198 586 180 Q 595 171 604 180 Q 611 192 612 201 L 613 229 Q 615 239 615 265 Q 613 294 613 308 L 613 331 L 613 406 L 613 431 Q 613 455 615 506 C 615 517 615 517 596 531 Z","M 809 223 Q 809 216 817 201 Q 831 173 841 174 Q 855 176 869 209 Q 874 219 876 225 Q 882 242 878 272 Q 868 363 866 451 Q 866 463 868 472 Q 871 489 885 517 Q 892 533 886 539 Q 861 561 817 579 Q 802 583 780 576 Q 774 574 657 540 L 630 535 Q 608 533 596 531 C 566 527 585 503 615 506 Q 627 506 642 510 Q 710 527 768 538 Q 789 542 795 531 Q 809 514 811 453 Q 817 290 811 249 L 809 223 Z","M 613 406 L 618 406 Q 686 417 747 423 Q 768 428 759 440 Q 749 452 727 456 Q 710 459 613 431 C 584 423 583 406 613 406 Z","M 613 308 L 623 308 Q 708 317 761 319 Q 783 323 774 334 Q 762 357 718 354 Q 690 352 613 331 C 584 323 583 308 613 308 Z","M 612 201 L 621 201 L 809 223 C 839 226 835 232 811 249 Q 800 259 777 258 Q 753 257 613 229 C 584 223 582 201 612 201 Z","M 623 161 Q 608 95 455 -29 Q 447 -35 447 -37 Q 446 -41 460 -39 Q 480 -37 504 -27 Q 562 -4 640 76 Q 686 123 690 126 Q 695 131 697 137 Q 704 157 671 167 Q 652 174 641 174 Q 627 174 623 161 Z","M 757 157 Q 757 153 757 151 Q 758 140 774 118 Q 829 47 864 -7 Q 877 -29 892 -36 Q 898 -37 903 -33 Q 923 -23 917 25 Q 914 77 768 163 Q 761 169 757 157 Z"],"medians":[[[306,809],[331,777],[299,694],[261,628],[204,549],[134,471],[36,394]],[[343,709],[352,714],[418,680],[449,658],[470,627]],[[247,544],[296,542],[396,571],[430,571]],[[119,429],[133,427],[170,326]],[[157,429],[195,452],[229,454],[238,443],[232,399],[223,394]],[[180,349],[185,359],[223,372],[254,372]],[[261,467],[274,459],[282,444],[303,366]],[[294,468],[302,463],[319,466],[353,482],[378,469],[365,424],[346,415]],[[312,376],[342,394],[386,397]],[[399,500],[418,476],[436,400]],[[428,499],[438,494],[495,510],[511,504],[517,496],[513,469],[509,453],[497,445]],[[449,411],[455,420],[472,424],[512,429],[528,425]],[[140,265],[165,240],[171,216],[174,151],[162,84],[168,40]],[[176,266],[196,259],[400,303],[434,303],[452,293],[458,266],[459,170],[452,94],[438,71],[383,94]],[[226,173],[325,199],[399,208]],[[236,251],[256,233],[264,91]],[[330,271],[351,252],[345,75]],[[574,665],[607,659],[648,661],[853,714],[907,718]],[[667,636],[685,620],[651,553],[636,541]],[[563,537],[592,497],[590,223],[595,187]],[[601,525],[632,521],[792,558],[815,551],[843,521],[837,466],[846,276],[841,185]],[[616,412],[628,424],[686,434],[729,439],[750,433]],[[618,314],[628,322],[706,335],[741,336],[766,328]],[[617,208],[629,219],[775,239],[792,239],[800,230]],[[680,143],[651,136],[615,87],[554,27],[503,-10],[454,-36]],[[766,154],[877,36],[893,-2],[894,-22]]]}
const colorSaturation = 100 // 0-255
const colorLightness = 40 // 0-255
const fontSize = 45
const fontBorderWidth = 6
function hslToRgb(h, s, l) { // To generate rainbow colors
h /= 360
s /= 100
l /= 100
let r, g, b
if (s === 0) {
r = g = b = l // achromatic
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1
if (t > 1) t -= 1
if (t < 1 / 6) return p + (q - p) * 6 * t
if (t < 1 / 2) return q
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6
return p
}
const q = l < 0.5 ? l * (1 + s) : l + s - l * s
const p = 2 * l - q
r = hue2rgb(p, q, h + 1 / 3)
g = hue2rgb(p, q, h)
b = hue2rgb(p, q, h - 1 / 3)
}
const toHex = x => {
const hex = Math.round(x * 255).toString(16)
return hex.length === 1 ? ''0'' + hex : hex
}
return `#${toHex(r)}${toHex(g)}${toHex(b)}`
}
const item = strokeData
const charCode = item.character.charCodeAt()
const startingPoints = item.medians.map(i=>({x:i[0][0],y:i[0][1]}))
let pathes = ``
let strokeStartPositions = []
for (const [i,stroke] of item.strokes.entries()) {
const strokeColor = hslToRgb(360/item.strokes.length*(i+1), colorSaturation, colorLightness)
pathes += ` <path d="${stroke}" fill="${strokeColor}"/>/n`
const x = startingPoints[i].x
const y = 900+fontSize/2-startingPoints[i].y
strokeStartPositions[i] = [x, y]
}
let texts = ``
for (const [i,currentPosition] of strokeStartPositions.entries()) {
const textColor = hslToRgb(360/strokeStartPositions.length*(i+1), colorSaturation, colorLightness)
const posX = currentPosition[0]
const posY = currentPosition[1]
texts += ` <text fill="${textColor}" x="${posX-20}" y="${posY-20}">${i+1}</text>/n`
}
const newSvgContent = `<svg version="1.1" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<style type="text/css">
text {
font-family: Helvetica;
font-size: ${fontSize}px;
paint-order: stroke;
stroke: #000000;
stroke-width: 6px;
stroke-linecap: butt;
stroke-linejoin: miter;
font-weight: 800;
}
</style>
<g stroke="lightgray" stroke-dasharray="1,1" stroke-width="1" transform="scale(4, 4)">
<line x1="0" y1="0" x2="256" y2="256"/>
<line x1="256" y1="0" x2="0" y2="256"/>
<line x1="128" y1="0" x2="128" y2="256"/>
<line x1="0" y1="128" x2="256" y2="128"/>
</g>
<g transform="scale(1, -1) translate(0, -900)">
${pathes} </g>
<g>
${texts} </g>
</svg>`
const svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg")
svgEl.innerHTML = newSvgContent
document.body.appendChild(svgEl)
html, body {
width: 100%;
height: 100%;
}
svg {
width: 100%;
height: 100%;
}
Una imagen ideal, donde muestres cómo debería ser el resultado final, sería un buen punto de partida para reproducirlo con un código.
Editar # 1
Para generar colores al azar, prueba este repositorio :
//#!/usr/bin/env node
//const fs = require(''fs-extra'')
//const readline = require(''readline'')
const strokeData = {"character":"龥","strokes":["M 128 445 Q 222 525 301 653 Q 319 685 334 709 L 343 725 Q 351 746 367 763 Q 376 772 374 782 Q 374 791 361 801 Q 327 827 298 819 Q 290 819 293 806 Q 310 732 219 604 L 171 539 Q 133 492 33 396 Q 26 392 35 390 Q 43 390 110 431 L 128 445 Z","M 334 709 Q 386 675 447 629 Q 461 617 472 615 Q 478 615 482 624 Q 488 634 474 663 Q 459 700 343 725 C 314 731 309 725 334 709 Z","M 253 553 Q 224 546 246 534 Q 276 517 325 531 Q 423 558 435 563 Q 439 567 439 572 Q 437 586 406 590 Q 388 593 316 566 L 253 553 Z","M 147 441 Q 143 445 128 445 C 101 446 101 446 110 431 Q 125 406 140 365 Q 153 331 162 323 Q 174 311 176 321 Q 178 329 175 343 L 171 363 Q 161 394 153 422 C 148 441 148 441 147 441 Z","M 255 464 Q 233 477 223 476 Q 213 475 180 456 Q 172 453 147 441 C 120 428 124 416 153 422 Q 161 423 181 431 Q 212 442 216 437 Q 221 433 216 383 C 213 353 241 353 245 383 Q 251 431 265 445 C 272 454 272 454 255 464 Z","M 175 343 Q 180 343 186 345 Q 216 357 254 367 Q 261 368 259 373 Q 259 377 245 383 L 216 383 Q 213 383 171 363 C 144 350 145 341 175 343 Z","M 285 468 Q 261 473 255 470 Q 255 468 255 464 C 255 459 255 469 265 445 Q 272 429 278 405 Q 288 371 295 363 Q 305 352 310 361 Q 310 367 310 371 L 308 392 Q 298 434 298 450 C 297 465 297 465 285 468 Z","M 374 410 Q 386 441 396 454 Q 404 463 406 466 C 413 475 413 475 398 484 L 374 498 Q 360 506 347 499 Q 327 482 285 468 C 257 458 270 438 298 450 Q 300 451 310 455 Q 351 468 355 462 Q 357 461 357 459 Q 359 445 342 406 C 330 378 363 382 374 410 Z","M 310 371 Q 323 378 385 390 Q 392 391 392 398 Q 390 402 374 410 C 368 413 368 413 342 406 Q 333 404 308 392 C 281 379 283 358 310 371 Z","M 406 466 Q 424 400 427 396 Q 437 382 441 392 L 443 406 L 441 425 Q 434 470 433 482 C 432 494 432 494 420 500 Q 419 501 416 502 Q 398 508 394 504 Q 390 500 398 484 L 406 466 Z","M 519 439 Q 529 478 541 490 Q 555 506 540 514 Q 514 526 506 528 Q 496 531 484 525 Q 454 510 420 500 C 391 491 403 477 433 482 Q 445 484 454 487 Q 490 496 495 490 Q 496 490 496 487 Q 500 472 490 439 C 482 410 511 410 519 439 Z","M 443 406 Q 449 406 455 407 Q 490 416 527 419 Q 534 420 534 425 Q 534 429 519 439 C 519 439 519 439 490 439 Q 488 441 441 425 C 413 415 413 405 443 406 Z","M 169 267 Q 151 272 139 272 Q 134 270 133 267 Q 132 264 139 246 Q 167 188 145 104 Q 129 91 142 62 Q 151 43 160 37 Q 166 27 173 32 Q 192 50 192 163 Q 192 202 192 242 C 192 261 192 261 169 267 Z","M 347 278 Q 402 290 419 284 Q 433 278 435 258 Q 439 207 429 114 Q 431 98 416 98 Q 396 98 386 100 Q 380 100 377 98 Q 369 94 398 69 Q 419 47 431 22 Q 441 15 451 20 Q 455 22 459 25 Q 480 58 482 158 Q 476 267 486 283 Q 494 292 488 302 Q 484 307 472 314 Q 437 333 419 323 Q 409 319 388 316 Q 271 292 169 267 C 140 260 164 232 192 242 Q 208 248 231 254 L 248 257 Q 272 265 323 274 L 347 278 Z","M 244 188 Q 235 186 214 178 Q 201 174 217 164 Q 226 158 246 163 L 274 170 L 332 184 L 367 190 Q 376 194 396 198 Q 402 199 406 202 Q 415 209 397 218 Q 379 227 368 223 L 332 216 Q 327 216 274 196 L 244 188 Z","M 231 254 Q 231 251 233 246 Q 241 216 244 188 L 246 163 Q 252 102 256 93 Q 265 78 270 87 Q 274 97 274 170 L 274 196 Q 274 208 275 218 Q 276 239 274 243 Q 271 250 248 257 C 229 263 229 263 231 254 Z","M 332 184 Q 332 80 342 71 Q 343 71 347 71 Q 351 73 354 81 Q 360 98 367 190 L 368 223 Q 368 229 372 243 Q 376 259 368 267 Q 355 276 347 278 C 319 288 317 289 323 274 Q 331 257 332 216 L 332 184 Z","M 698 651 L 862 688 Q 908 700 914 708 Q 923 714 918 723 Q 913 729 903 734 Q 864 751 825 733 Q 808 727 794 723 Q 696 689 582 674 Q 548 668 572 653 Q 606 634 659 641 L 698 651 Z","M 657 540 Q 668 553 678 566 Q 699 594 712 608 Q 734 624 698 651 C 674 669 660 671 659 641 Q 659 617 641 563 Q 631 549 630 535 C 626 505 638 517 657 540 Z","M 596 531 Q 594 533 591 535 Q 571 549 557 543 Q 555 541 555 536 Q 555 525 561 515 Q 582 472 572 295 Q 566 269 566 245 Q 566 198 586 180 Q 595 171 604 180 Q 611 192 612 201 L 613 229 Q 615 239 615 265 Q 613 294 613 308 L 613 331 L 613 406 L 613 431 Q 613 455 615 506 C 615 517 615 517 596 531 Z","M 809 223 Q 809 216 817 201 Q 831 173 841 174 Q 855 176 869 209 Q 874 219 876 225 Q 882 242 878 272 Q 868 363 866 451 Q 866 463 868 472 Q 871 489 885 517 Q 892 533 886 539 Q 861 561 817 579 Q 802 583 780 576 Q 774 574 657 540 L 630 535 Q 608 533 596 531 C 566 527 585 503 615 506 Q 627 506 642 510 Q 710 527 768 538 Q 789 542 795 531 Q 809 514 811 453 Q 817 290 811 249 L 809 223 Z","M 613 406 L 618 406 Q 686 417 747 423 Q 768 428 759 440 Q 749 452 727 456 Q 710 459 613 431 C 584 423 583 406 613 406 Z","M 613 308 L 623 308 Q 708 317 761 319 Q 783 323 774 334 Q 762 357 718 354 Q 690 352 613 331 C 584 323 583 308 613 308 Z","M 612 201 L 621 201 L 809 223 C 839 226 835 232 811 249 Q 800 259 777 258 Q 753 257 613 229 C 584 223 582 201 612 201 Z","M 623 161 Q 608 95 455 -29 Q 447 -35 447 -37 Q 446 -41 460 -39 Q 480 -37 504 -27 Q 562 -4 640 76 Q 686 123 690 126 Q 695 131 697 137 Q 704 157 671 167 Q 652 174 641 174 Q 627 174 623 161 Z","M 757 157 Q 757 153 757 151 Q 758 140 774 118 Q 829 47 864 -7 Q 877 -29 892 -36 Q 898 -37 903 -33 Q 923 -23 917 25 Q 914 77 768 163 Q 761 169 757 157 Z"],"medians":[[[306,809],[331,777],[299,694],[261,628],[204,549],[134,471],[36,394]],[[343,709],[352,714],[418,680],[449,658],[470,627]],[[247,544],[296,542],[396,571],[430,571]],[[119,429],[133,427],[170,326]],[[157,429],[195,452],[229,454],[238,443],[232,399],[223,394]],[[180,349],[185,359],[223,372],[254,372]],[[261,467],[274,459],[282,444],[303,366]],[[294,468],[302,463],[319,466],[353,482],[378,469],[365,424],[346,415]],[[312,376],[342,394],[386,397]],[[399,500],[418,476],[436,400]],[[428,499],[438,494],[495,510],[511,504],[517,496],[513,469],[509,453],[497,445]],[[449,411],[455,420],[472,424],[512,429],[528,425]],[[140,265],[165,240],[171,216],[174,151],[162,84],[168,40]],[[176,266],[196,259],[400,303],[434,303],[452,293],[458,266],[459,170],[452,94],[438,71],[383,94]],[[226,173],[325,199],[399,208]],[[236,251],[256,233],[264,91]],[[330,271],[351,252],[345,75]],[[574,665],[607,659],[648,661],[853,714],[907,718]],[[667,636],[685,620],[651,553],[636,541]],[[563,537],[592,497],[590,223],[595,187]],[[601,525],[632,521],[792,558],[815,551],[843,521],[837,466],[846,276],[841,185]],[[616,412],[628,424],[686,434],[729,439],[750,433]],[[618,314],[628,322],[706,335],[741,336],[766,328]],[[617,208],[629,219],[775,239],[792,239],[800,230]],[[680,143],[651,136],[615,87],[554,27],[503,-10],[454,-36]],[[766,154],[877,36],[893,-2],[894,-22]]]}
const fontSize = 45
const fontBorderWidth = 6
//const lineReader = readline.createInterface({
// input: fs.createReadStream(''../graphics.txt'')
//})
//lineReader.on(''line'', line => {
//const item = JSON.parse(line)
const item = strokeData
const charCode = item.character.charCodeAt()
const startingPoints = item.medians.map(i=>({x:i[0][0],y:i[0][1]}))
let pathes = ``
let strokeStartPositions = []
let randomColors = []
for (const [i,stroke] of item.strokes.entries()) {
randomColors.push(randomColor())
const strokeColor = randomColors[i]
pathes += ` <path d="${stroke}" fill="${strokeColor}"/>/n`
let x = startingPoints[i].x
x = i<9 ? x-fontSize/4 : x-fontSize/2
//x = x-fontSize/2
let y = 900+fontSize/2-startingPoints[i].y
//y = y-fontSize/2
strokeStartPositions[i] = [x, y]
}
function circlesIntersect(circle1, circle2) {
const deltaX = circle1.x - circle2.x
const deltaY = circle1.y - circle2.y
const rSum = circle1.r + circle2.r
return deltaX*deltaX + deltaY*deltaY <= rSum * rSum
}
function anyCircleIntersects(circleArr) {
for (const circle1 of circleArr) {
for (const circle2 of circleArr) {
if (circle1 !== circle2) {
if (circlesIntersect(circle1,circle2))
return true
}
}
}
}
function calculate_point_on_other_side_of_p2(p1, p2, distance_p2_to_p3) {
const deltaX = p1[0]-p2[0]
const deltaY = p1[1]-p2[1]
const distance_p1_to_p2 = Math.sqrt(deltaX*deltaX + deltaY*deltaY)
const scale = distance_p2_to_p3 / distance_p1_to_p2
let p3 = []
p3[0] = p2[0] - deltaX * scale
p3[1] = p2[1] - deltaY * scale
return p3
}
const t0 = Date.now()
while (anyCircleIntersects(strokeStartPositions.map(pos=>{return {r:fontSize/2, x:pos[0], y:pos[1]}}))) {
if (Date.now() > t0+1000) {
;
break
}
for (const [i,p1] of strokeStartPositions.entries()) {
const p1X = p1[0]
const p1Y = p1[1]
for (const [j,p2] of strokeStartPositions.entries()) {
if (i === j)
continue
const p2X = p2[0]
const p2Y = p2[1]
if (p1X === p2X)
p2[0] = p2[0]+1
if (p1Y === p2Y)
p2[1] = p2[1]+1
const p1Radius = i<9 ? fontSize/2.5 : fontSize/1.75
const p2Radius = j<9 ? fontSize/2.5 : fontSize/1.75
if (circlesIntersect({r:p1Radius, x:p1X, y:p1Y}, {r:p2Radius, x:p2X, y:p2Y})) {
let newP2 = calculate_point_on_other_side_of_p2(p1, p2, 1)
let newP1 = calculate_point_on_other_side_of_p2(p2, p1, 1)
strokeStartPositions[i][0] = Math.round(newP1[0])
strokeStartPositions[i][1] = Math.round(newP1[1])
strokeStartPositions[j][0] = Math.round(newP2[0])
strokeStartPositions[j][1] = Math.round(newP2[1])
}
}
}
}
let texts = ``
for (const [i,pos] of strokeStartPositions.entries()) {
//const textColor = "#FFFFFF"
const textColor = randomColors[i];
const x = pos[0]
const y = pos[1]
texts += ` <text fill="${textColor}" x="${x}" y="${y}">${i+1}</text>/n`
}
const newSvgContent = `<svg version="1.1" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<style type="text/css">
@import url(''https://fonts.googleapis.com/css?family=Roboto'');
text {
font-family: ''Roboto'', sans-serif;
font-size: ${fontSize}px;
paint-order: stroke;
stroke: #000000;
stroke-width: 6px;
stroke-linecap: butt;
stroke-linejoin: miter;
font-weight: 800;
}
</style>
<g stroke="lightgray" stroke-dasharray="1,1" stroke-width="1" transform="scale(4, 4)">
<line x1="0" y1="0" x2="256" y2="256"/>
<line x1="256" y1="0" x2="0" y2="256"/>
<line x1="128" y1="0" x2="128" y2="256"/>
<line x1="0" y1="128" x2="256" y2="128"/>
</g>
<g transform="scale(1, -1) translate(0, -900)">
${pathes}
</g>
<g>
${texts}
</g>
</svg>`
const svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg")
svgEl.innerHTML = newSvgContent
document.body.appendChild(svgEl)
//fs.outputFile(`../svgs-still/${charCode}-still.svg`, newSvgContent).then(() => {
// //console.log(`../svgs-still/${charCode}-still.svg written`) // logging severely reduces performance
//}).catch(e=>console.error(new Error(e)))
//})
html, body {
width: 100%;
height: 100%;
}
svg {
width: 100%;
height: 100%;
}
<script src="https://cdn.rawgit.com/davidmerfield/randomColor/master/randomColor.js"></script>
Editar # 2
¡Echa un vistazo a this pluma!
Aclamaciones
He hecho cosas similares en el pasado y es bastante complicado porque esto es básicamente como construir tu propio marco de UI.
Aquí está mi opinión sobre ello. Desafortunadamente no pude completarlo pero la idea básica es esta:
- Obtener el punto inicial y final de cada trazo
- Calcular la dirección del trazo (norte, noreste, oeste, etc ...)
- Supongamos que cada elemento de texto que muestra el índice es un rectángulo
- Compruebe si el índice que está a punto de agregar se superpone con cualquiera de los índices anteriores (Ej: índice 5, ya que se superpone con 4)
- Mueva el nuevo índice de trazo en la dirección del trazo hasta que ya no se superponga con el rectángulo de índice anterior.
La razón por la que el ejemplo a continuación no funciona es porque necesita el punto de inicio y finalización precisos de cada uno de los trazos. Inicialmente, estaba usando el primer valor de la mediana de cada trazo como punto de inicio (lo que proporcionó) y el último valor de la mediana como el punto de finalización (no parece exacto)
Si conectara los puntos reales de inicio y final de cada golpe, debería obtener un resultado mucho mejor.
const strokeData = {"character":"?","strokes":["M 128 445 Q 222 525 301 653 Q 319 685 334 709 L 343 725 Q 351 746 367 763 Q 376 772 374 782 Q 374 791 361 801 Q 327 827 298 819 Q 290 819 293 806 Q 310 732 219 604 L 171 539 Q 133 492 33 396 Q 26 392 35 390 Q 43 390 110 431 L 128 445 Z","M 334 709 Q 386 675 447 629 Q 461 617 472 615 Q 478 615 482 624 Q 488 634 474 663 Q 459 700 343 725 C 314 731 309 725 334 709 Z","M 253 553 Q 224 546 246 534 Q 276 517 325 531 Q 423 558 435 563 Q 439 567 439 572 Q 437 586 406 590 Q 388 593 316 566 L 253 553 Z","M 147 441 Q 143 445 128 445 C 101 446 101 446 110 431 Q 125 406 140 365 Q 153 331 162 323 Q 174 311 176 321 Q 178 329 175 343 L 171 363 Q 161 394 153 422 C 148 441 148 441 147 441 Z","M 255 464 Q 233 477 223 476 Q 213 475 180 456 Q 172 453 147 441 C 120 428 124 416 153 422 Q 161 423 181 431 Q 212 442 216 437 Q 221 433 216 383 C 213 353 241 353 245 383 Q 251 431 265 445 C 272 454 272 454 255 464 Z","M 175 343 Q 180 343 186 345 Q 216 357 254 367 Q 261 368 259 373 Q 259 377 245 383 L 216 383 Q 213 383 171 363 C 144 350 145 341 175 343 Z","M 285 468 Q 261 473 255 470 Q 255 468 255 464 C 255 459 255 469 265 445 Q 272 429 278 405 Q 288 371 295 363 Q 305 352 310 361 Q 310 367 310 371 L 308 392 Q 298 434 298 450 C 297 465 297 465 285 468 Z","M 374 410 Q 386 441 396 454 Q 404 463 406 466 C 413 475 413 475 398 484 L 374 498 Q 360 506 347 499 Q 327 482 285 468 C 257 458 270 438 298 450 Q 300 451 310 455 Q 351 468 355 462 Q 357 461 357 459 Q 359 445 342 406 C 330 378 363 382 374 410 Z","M 310 371 Q 323 378 385 390 Q 392 391 392 398 Q 390 402 374 410 C 368 413 368 413 342 406 Q 333 404 308 392 C 281 379 283 358 310 371 Z","M 406 466 Q 424 400 427 396 Q 437 382 441 392 L 443 406 L 441 425 Q 434 470 433 482 C 432 494 432 494 420 500 Q 419 501 416 502 Q 398 508 394 504 Q 390 500 398 484 L 406 466 Z","M 519 439 Q 529 478 541 490 Q 555 506 540 514 Q 514 526 506 528 Q 496 531 484 525 Q 454 510 420 500 C 391 491 403 477 433 482 Q 445 484 454 487 Q 490 496 495 490 Q 496 490 496 487 Q 500 472 490 439 C 482 410 511 410 519 439 Z","M 443 406 Q 449 406 455 407 Q 490 416 527 419 Q 534 420 534 425 Q 534 429 519 439 C 519 439 519 439 490 439 Q 488 441 441 425 C 413 415 413 405 443 406 Z","M 169 267 Q 151 272 139 272 Q 134 270 133 267 Q 132 264 139 246 Q 167 188 145 104 Q 129 91 142 62 Q 151 43 160 37 Q 166 27 173 32 Q 192 50 192 163 Q 192 202 192 242 C 192 261 192 261 169 267 Z","M 347 278 Q 402 290 419 284 Q 433 278 435 258 Q 439 207 429 114 Q 431 98 416 98 Q 396 98 386 100 Q 380 100 377 98 Q 369 94 398 69 Q 419 47 431 22 Q 441 15 451 20 Q 455 22 459 25 Q 480 58 482 158 Q 476 267 486 283 Q 494 292 488 302 Q 484 307 472 314 Q 437 333 419 323 Q 409 319 388 316 Q 271 292 169 267 C 140 260 164 232 192 242 Q 208 248 231 254 L 248 257 Q 272 265 323 274 L 347 278 Z","M 244 188 Q 235 186 214 178 Q 201 174 217 164 Q 226 158 246 163 L 274 170 L 332 184 L 367 190 Q 376 194 396 198 Q 402 199 406 202 Q 415 209 397 218 Q 379 227 368 223 L 332 216 Q 327 216 274 196 L 244 188 Z","M 231 254 Q 231 251 233 246 Q 241 216 244 188 L 246 163 Q 252 102 256 93 Q 265 78 270 87 Q 274 97 274 170 L 274 196 Q 274 208 275 218 Q 276 239 274 243 Q 271 250 248 257 C 229 263 229 263 231 254 Z","M 332 184 Q 332 80 342 71 Q 343 71 347 71 Q 351 73 354 81 Q 360 98 367 190 L 368 223 Q 368 229 372 243 Q 376 259 368 267 Q 355 276 347 278 C 319 288 317 289 323 274 Q 331 257 332 216 L 332 184 Z","M 698 651 L 862 688 Q 908 700 914 708 Q 923 714 918 723 Q 913 729 903 734 Q 864 751 825 733 Q 808 727 794 723 Q 696 689 582 674 Q 548 668 572 653 Q 606 634 659 641 L 698 651 Z","M 657 540 Q 668 553 678 566 Q 699 594 712 608 Q 734 624 698 651 C 674 669 660 671 659 641 Q 659 617 641 563 Q 631 549 630 535 C 626 505 638 517 657 540 Z","M 596 531 Q 594 533 591 535 Q 571 549 557 543 Q 555 541 555 536 Q 555 525 561 515 Q 582 472 572 295 Q 566 269 566 245 Q 566 198 586 180 Q 595 171 604 180 Q 611 192 612 201 L 613 229 Q 615 239 615 265 Q 613 294 613 308 L 613 331 L 613 406 L 613 431 Q 613 455 615 506 C 615 517 615 517 596 531 Z","M 809 223 Q 809 216 817 201 Q 831 173 841 174 Q 855 176 869 209 Q 874 219 876 225 Q 882 242 878 272 Q 868 363 866 451 Q 866 463 868 472 Q 871 489 885 517 Q 892 533 886 539 Q 861 561 817 579 Q 802 583 780 576 Q 774 574 657 540 L 630 535 Q 608 533 596 531 C 566 527 585 503 615 506 Q 627 506 642 510 Q 710 527 768 538 Q 789 542 795 531 Q 809 514 811 453 Q 817 290 811 249 L 809 223 Z","M 613 406 L 618 406 Q 686 417 747 423 Q 768 428 759 440 Q 749 452 727 456 Q 710 459 613 431 C 584 423 583 406 613 406 Z","M 613 308 L 623 308 Q 708 317 761 319 Q 783 323 774 334 Q 762 357 718 354 Q 690 352 613 331 C 584 323 583 308 613 308 Z","M 612 201 L 621 201 L 809 223 C 839 226 835 232 811 249 Q 800 259 777 258 Q 753 257 613 229 C 584 223 582 201 612 201 Z","M 623 161 Q 608 95 455 -29 Q 447 -35 447 -37 Q 446 -41 460 -39 Q 480 -37 504 -27 Q 562 -4 640 76 Q 686 123 690 126 Q 695 131 697 137 Q 704 157 671 167 Q 652 174 641 174 Q 627 174 623 161 Z","M 757 157 Q 757 153 757 151 Q 758 140 774 118 Q 829 47 864 -7 Q 877 -29 892 -36 Q 898 -37 903 -33 Q 923 -23 917 25 Q 914 77 768 163 Q 761 169 757 157 Z"],"medians":[[[300,809],[331,777],[299,694],[261,628],[204,549],[134,471],[36,394]],[[343,709],[352,714],[418,680],[449,658],[470,627]],[[247,544],[296,542],[396,571],[430,571]],[[119,429],[133,427],[170,326]],[[157,429],[195,452],[229,454],[238,443],[232,399],[223,394]],[[180,349],[185,359],[223,372],[254,372]],[[261,467],[274,459],[282,444],[303,366]],[[294,468],[302,463],[319,466],[353,482],[378,469],[365,424],[346,415]],[[312,376],[342,394],[386,397]],[[399,500],[418,476],[436,400]],[[428,499],[438,494],[495,510],[511,504],[517,496],[513,469],[509,453],[497,445]],[[449,411],[455,420],[472,424],[512,429],[528,425]],[[140,265],[165,240],[171,216],[174,151],[162,84],[168,40]],[[176,266],[196,259],[400,303],[434,303],[452,293],[458,266],[459,170],[452,94],[438,71],[383,94]],[[226,173],[325,199],[399,208]],[[236,251],[256,233],[264,91]],[[330,271],[351,252],[345,75]],[[574,665],[607,659],[648,661],[853,714],[907,718]],[[667,636],[685,620],[651,553],[636,541]],[[563,537],[592,497],[590,223],[595,187]],[[601,525],[632,521],[792,558],[815,551],[843,521],[837,466],[846,276],[841,185]],[[616,412],[628,424],[686,434],[729,439],[750,433]],[[618,314],[628,322],[706,335],[741,336],[766,328]],[[617,208],[629,219],[775,239],[792,239],[800,230]],[[680,143],[651,136],[615,87],[554,27],[503,-10],[454,-36]],[[766,154],[877,36],[893,-2],[894,-22]]]}
const colorSaturation = 100 // 0-255
const colorLightness = 40 // 0-255
const fontSize = 60
const fontBorderWidth = 6
function hslToRgb(h, s, l) { // To generate rainbow colors
h /= 360
s /= 100
l /= 100
let r, g, b
if (s === 0) {
r = g = b = l // achromatic
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1
if (t > 1) t -= 1
if (t < 1 / 6) return p + (q - p) * 6 * t
if (t < 1 / 2) return q
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6
return p
}
const q = l < 0.5 ? l * (1 + s) : l + s - l * s
const p = 2 * l - q
r = hue2rgb(p, q, h + 1 / 3)
g = hue2rgb(p, q, h)
b = hue2rgb(p, q, h - 1 / 3)
}
const toHex = x => {
const hex = Math.round(x * 255).toString(16)
return hex.length === 1 ? ''0'' + hex : hex
}
return `#${toHex(r)}${toHex(g)}${toHex(b)}`
}
const item = strokeData
const charCode = item.character.charCodeAt()
const startingPoints = item.medians.map(i=>({x:i[0][0],y:i[0][1]}))
const endingPoints = item.medians.map(i=>({x:i[i.length - 1][0],y:i[i.length - 1][1]}))
let pathes = ``
let strokeStartPositions = []
for (const [i,stroke] of item.strokes.entries()) {
const strokeColor = hslToRgb(360/item.strokes.length*(i+1), colorSaturation, colorLightness)
pathes += ` <path d="${stroke}" fill="${strokeColor}"/>/n`
const x = startingPoints[i].x
const y = 900+fontSize/2-startingPoints[i].y
strokeStartPositions[i] = [x, y]
}
function repositionAlgorithm(position, finalPosition, strokeStartPositions, currentPositionIndex) {
var strokeDirection = getStrokeDirection({x: position[0], y: position[1]}, finalPosition);
var boxWidth = fontSize;
if (currentPositionIndex.toString().length == 2)
boxWidth = boxWidth * 1.5; //if number has 2 digits
else if (currentPositionIndex.toString().length == 3)
boxWidth = boxWidth * 2; //if number has 3 digits
var currentPositionRectangle = {
x1: position[0],
x2: position[0] + boxWidth,
y1: position[1],
y2: position[1] + fontSize
};
//make sure position doesn''t overlap with bounding box of other PAST positions
for(var i = 0; i < currentPositionIndex; i++) {
var otherPositionRectangle = {
x1: strokeStartPositions[i][0],
x2: strokeStartPositions[i][0] + boxWidth,
y1: strokeStartPositions[i][1],
y2: strokeStartPositions[i][1] + fontSize
};
var maxLoop = 5000; // prevent endless loop
while (currentPositionRectangle.x1 < otherPositionRectangle.x2
&& currentPositionRectangle.x2 > otherPositionRectangle.x1
&& currentPositionRectangle.y2 > otherPositionRectangle.y1
&& currentPositionRectangle.y1 < otherPositionRectangle.y2
&& maxLoop-- > 0) { //Thanks to: https://.com/questions/306316/determine-if-two-rectangles-overlap-each-other
//while overlapping, move the text position according to the stroke direction
switch(strokeDirection) {
case "ne":
currentPositionRectangle.x1 += 2;
currentPositionRectangle.x2 += 2;
currentPositionRectangle.y1--;
currentPositionRectangle.y2--;
break;
case "nw":
currentPositionRectangle.x1 -= 2;
currentPositionRectangle.x2 -= 2;
currentPositionRectangle.y1--;
currentPositionRectangle.y2--;
break;
case "se":
currentPositionRectangle.x1 += 2;
currentPositionRectangle.x2 += 2;
currentPositionRectangle.y1++;
currentPositionRectangle.y2++;
break;
case "sw":
currentPositionRectangle.x1 -= 2;
currentPositionRectangle.x2 -= 2;
currentPositionRectangle.y1++;
currentPositionRectangle.y2++;
break;
case "n":
currentPositionRectangle.y1--;
currentPositionRectangle.y2--;
break;
case "s":
currentPositionRectangle.y1++;
currentPositionRectangle.y2++;
break;
case "w":
currentPositionRectangle.x1--;
currentPositionRectangle.x2--;
break;
case "e":
currentPositionRectangle.x1++;
currentPositionRectangle.x2++;
break;
default:
throw new Error("Unknown strokeDirection");
}
}
}
position[0] = currentPositionRectangle.x1;
position[1] = currentPositionRectangle.y1;
return position
}
function getStrokeDirection(p1, p2) {
//thanks to: https://.com/questions/35104991/relative-cardinal-direction-of-two-coordinates
//Y increases towards the bottom in SVG so we replace p2.Y with p1.Y
var angle = Math.atan2(p1.y - p2.y, p2.x - p1.x);
angle += Math.PI;
angle /= Math.PI / 4;
var halfQuarter = parseInt(angle);
halfQuarter %= 8;
//we also need to switch North with South because Y increases towards the south
switch (halfQuarter) {
case 0:
return "s";
break;
case 4:
return "n";
break;
case 6:
return "e";
break;
case 2:
return "w";
break;
case 7:
return "se";
break;
case 1:
return "sw";
break;
case 5:
return "ne";
break;
case 3:
return "nw";
break;
default:
return "unknown";
break;
}
}
let numberPositions = []
for (const [i,currentPosition] of strokeStartPositions.entries()) {
const oldX = currentPosition[0]
const oldY = currentPosition[1]
var finalPosition = endingPoints[i];
const repulsedFrom = strokeStartPositions.slice().splice(i,1) // all positions but the current one
const attractedTo = [currentPosition]
const newPosition = repositionAlgorithm(currentPosition, finalPosition, strokeStartPositions, i)
numberPositions[i] = newPosition
const newX = newPosition[0]
const newY = newPosition[1]
console.log(`Moved from (${oldX}|${oldY}) to (${newX}|${newY}).`)
}
let texts = ``
for (const [i,pos] of numberPositions.entries()) {
const textColor = "#FFFFFF" //const textColor = hslToRgb(360/pathEls.length*(i+1), colorSaturation, colorLightness)
const x = pos[0]
const y = pos[1]
texts += ` <text fill="${textColor}" x="${x}" y="${y}">${i+1}</text>/n`
}
const newSvgContent = `<svg version="1.1" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<style type="text/css">
text {
font-family: Helvetica;
font-size: ${fontSize}px;
paint-order: stroke;
stroke: #000000;
stroke-width: 6px;
stroke-linecap: butt;
stroke-linejoin: miter;
font-weight: 800;
}
</style>
<g stroke="lightgray" stroke-dasharray="1,1" stroke-width="1" transform="scale(4, 4)">
<line x1="0" y1="0" x2="256" y2="256"/>
<line x1="256" y1="0" x2="0" y2="256"/>
<line x1="128" y1="0" x2="128" y2="256"/>
<line x1="0" y1="128" x2="256" y2="128"/>
</g>
<g transform="scale(1, -1) translate(0, -900)">
${pathes} </g>
<g>
${texts} </g>
</svg>`
const svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg")
svgEl.innerHTML = newSvgContent
document.body.appendChild(svgEl)
//fs.outputFile(`../svgs-still/${charCode}-still.svg`, newSvgContent).then(() => {
// //console.log(`../svgs-still/${charCode}-still.svg written`) // logging severely reduces performance
//}).catch(e=>console.error(new Error(e)))
//})
html, body {
width: 100%;
height: 100%;
}
svg {
width: 100%;
height: 100%;
}