| 495 | } |
| 496 | |
| 497 | createTextSprite(text) { |
| 498 | const width = 512; |
| 499 | const height = 256; |
| 500 | const canvas = document.createElement("canvas"); |
| 501 | canvas.width = width; |
| 502 | canvas.height = height; |
| 503 | const ctx = canvas.getContext("2d"); |
| 504 | ctx.clearRect(0, 0, width, height); |
| 505 | ctx.font = "bold 140px 'Inter', 'Segoe UI', sans-serif"; |
| 506 | ctx.textAlign = "center"; |
| 507 | ctx.textBaseline = "middle"; |
| 508 | ctx.fillStyle = "#f0f6ff"; |
| 509 | ctx.shadowColor = "rgba(4, 6, 12, 0.85)"; |
| 510 | ctx.shadowBlur = 18; |
| 511 | ctx.lineWidth = 22; |
| 512 | ctx.strokeStyle = "rgba(4, 6, 12, 0.85)"; |
| 513 | const centerX = width / 2; |
| 514 | const centerY = height / 2; |
| 515 | ctx.strokeText(text, centerX, centerY); |
| 516 | ctx.shadowBlur = 0; |
| 517 | ctx.fillText(text, centerX, centerY); |
| 518 | |
| 519 | const texture = new THREE.CanvasTexture(canvas); |
| 520 | texture.anisotropy = this.renderer.capabilities.getMaxAnisotropy?.() ?? 1; |
| 521 | |
| 522 | texture.colorSpace = THREE.SRGBColorSpace; |
| 523 | texture.minFilter = THREE.LinearFilter; |
| 524 | texture.magFilter = THREE.LinearFilter; |
| 525 | texture.generateMipmaps = false; |
| 526 | texture.needsUpdate = true; |
| 527 | |
| 528 | const material = new THREE.SpriteMaterial({ |
| 529 | map: texture, |
| 530 | transparent: true, |
| 531 | depthTest: false, |
| 532 | depthWrite: false |
| 533 | }); |
| 534 | |
| 535 | const sprite = new THREE.Sprite(material); |
| 536 | sprite.scale.set(3.5, 1.8, 1); |
| 537 | sprite.userData.texture = texture; |
| 538 | sprite.renderOrder = 2; |
| 539 | return sprite; |
| 540 | } |
| 541 | |
| 542 | updateGridLabels(bounds, rawExtents, options = {}, labelScaleFactor = 1) { |
| 543 | this.clearGridLabels(); |