SPB Git forge

spb/spinza

Public
8commits 1branches 0releases
1.6 MBsize
maindefault branch
16 days agolast push
TypeScript 97.6% SQL 1.4% JavaScript 0.5%
35.1 KB · 684 lines typescript
Raw Blame History
1import { Container, Graphics, Text, TextStyle } from "pixi.js";2import type { SymbolStyle } from "@spinza/game-core/client";34/**5 * Procedural symbol artwork. Every Spinza symbol is drawn from primitives —6 * no external assets — so each game keeps an original look while sharing7 * one renderer. `size` is the tile edge in px; art is centred on (0,0).8 */910function hex(c: string): number {11  return parseInt(c.replace("#", ""), 16);12}1314function lighten(c: string, amt: number): number {15  const n = hex(c);16  const r = Math.min(255, ((n >> 16) & 255) + Math.round(255 * amt));17  const g = Math.min(255, ((n >> 8) & 255) + Math.round(255 * amt));18  const b = Math.min(255, (n & 255) + Math.round(255 * amt));19  return (r << 16) | (g << 8) | b;20}2122function darken(c: string, amt: number): number {23  const n = hex(c);24  const r = Math.max(0, ((n >> 16) & 255) - Math.round(255 * amt));25  const g = Math.max(0, ((n >> 8) & 255) - Math.round(255 * amt));26  const b = Math.max(0, (n & 255) - Math.round(255 * amt));27  return (r << 16) | (g << 8) | b;28}2930function polygon(g: Graphics, cx: number, cy: number, r: number, sides: number, rot = -Math.PI / 2): Graphics {31  const pts: number[] = [];32  for (let i = 0; i < sides; i++) {33    const a = rot + (i * Math.PI * 2) / sides;34    pts.push(cx + Math.cos(a) * r, cy + Math.sin(a) * r);35  }36  return g.poly(pts);37}3839function star(g: Graphics, cx: number, cy: number, outer: number, inner: number, points = 5): Graphics {40  const pts: number[] = [];41  for (let i = 0; i < points * 2; i++) {42    const r = i % 2 === 0 ? outer : inner;43    const a = -Math.PI / 2 + (i * Math.PI) / points;44    pts.push(cx + Math.cos(a) * r, cy + Math.sin(a) * r);45  }46  return g.poly(pts);47}4849export interface SymbolArtOptions {50  size: number;51  /** Tier drives frame richness. */52  tier: "low" | "mid" | "high" | "premium" | "special";53  kind: string;54  /** Game frame preset — drives the tile plate look so every game has its own tiles. */55  frame?: "metal" | "glass" | "gold" | "stone" | "ice" | "carbon" | "neon" | "obsidian";56}5758/** Tile plate behind the icon, styled per game frame preset. */59function drawPlate(size: number, style: SymbolStyle, opts: SymbolArtOptions): Graphics {60  const plate = new Graphics();61  const pad = size * 0.06;62  const x = -size / 2 + pad;63  const w = size - pad * 2;64  const premium = opts.tier === "premium" || opts.tier === "special";65  const main = hex(style.color);66  const accent = style.accent ? hex(style.accent) : lighten(style.color, 0.35);67  const line = Math.max(1, size * 0.012);68  switch (opts.frame) {69    case "glass":70      plate.roundRect(x, x, w, w, size * 0.2).fill({ color: 0xffffff, alpha: 0.07 });71      plate.roundRect(x, x, w, w, size * 0.2).stroke({ color: 0xffffff, alpha: premium ? 0.5 : 0.22, width: line });72      plate.roundRect(x + w * 0.08, x + w * 0.06, w * 0.84, w * 0.28, size * 0.12).fill({ color: 0xffffff, alpha: 0.06 });73      break;74    case "gold":75      plate.roundRect(x, x, w, w, size * 0.1).fill({ color: 0x14100a, alpha: 0.8 });76      plate.roundRect(x, x, w, w, size * 0.1).stroke({ color: premium ? 0xf3e2ad : 0xa88a4a, alpha: premium ? 0.9 : 0.5, width: line * 1.6 });77      plate.roundRect(x + line * 3, x + line * 3, w - line * 6, w - line * 6, size * 0.08).stroke({ color: 0xc9a961, alpha: 0.25, width: line });78      for (const [cx, cy] of [[x + size * 0.09, x + size * 0.09], [x + w - size * 0.09, x + size * 0.09], [x + size * 0.09, x + w - size * 0.09], [x + w - size * 0.09, x + w - size * 0.09]])79        plate.circle(cx, cy, size * 0.018).fill({ color: 0xf3e2ad, alpha: 0.8 });80      break;81    case "stone":82      plate.roundRect(x, x, w, w, size * 0.06).fill({ color: 0x1a1712, alpha: 0.85 });83      plate.roundRect(x, x, w, w, size * 0.06).stroke({ color: premium ? accent : 0x7a6f5a, alpha: premium ? 0.7 : 0.45, width: line * 1.4 });84      plate.moveTo(x + w * 0.15, x).lineTo(x + w * 0.22, x + w * 0.12).stroke({ color: 0x000000, alpha: 0.5, width: line });85      plate.moveTo(x + w, x + w * 0.7).lineTo(x + w * 0.85, x + w * 0.78).stroke({ color: 0x000000, alpha: 0.5, width: line });86      break;87    case "ice":88      plate.roundRect(x, x, w, w, size * 0.18).fill({ color: 0x9fdcff, alpha: 0.1 });89      plate.roundRect(x, x, w, w, size * 0.18).stroke({ color: 0xdff6ff, alpha: premium ? 0.8 : 0.35, width: line });90      plate.moveTo(x + w * 0.1, x + w * 0.9).lineTo(x + w * 0.35, x + w * 0.62).stroke({ color: 0xffffff, alpha: 0.35, width: line });91      plate.moveTo(x + w * 0.16, x + w * 0.9).lineTo(x + w * 0.4, x + w * 0.66).stroke({ color: 0xffffff, alpha: 0.2, width: line });92      break;93    case "carbon":94      plate.roundRect(x, x, w, w, size * 0.08).fill({ color: 0x0a0b0e, alpha: 0.92 });95      for (let i = 0; i < 6; i++) plate.moveTo(x, x + (i / 6) * w).lineTo(x + w, x + (i / 6) * w).stroke({ color: 0xffffff, alpha: 0.035, width: line });96      plate.roundRect(x, x, w, w, size * 0.08).stroke({ color: premium ? accent : 0x5c6270, alpha: premium ? 0.8 : 0.45, width: line * 1.3 });97      break;98    case "neon":99      plate.roundRect(x, x, w, w, size * 0.16).fill({ color: 0x05060c, alpha: 0.75 });100      plate.roundRect(x, x, w, w, size * 0.16).stroke({ color: main, alpha: premium ? 0.9 : 0.45, width: line * 1.4 });101      plate.roundRect(x - line * 2, x - line * 2, w + line * 4, w + line * 4, size * 0.17).stroke({ color: main, alpha: premium ? 0.25 : 0.1, width: line * 4 });102      break;103    case "obsidian":104      plate.roundRect(x, x, w, w, size * 0.04).fill({ color: 0x000000, alpha: 0.9 });105      plate.roundRect(x, x, w, w, size * 0.04).stroke({ color: premium ? 0xe8cf8f : 0xffffff, alpha: premium ? 0.7 : 0.12, width: line });106      break;107    case "metal":108    default:109      plate.roundRect(x, x, w, w, size * 0.14).fill({ color: 0x0b0d14, alpha: 0.75 });110      plate.roundRect(x, x, w, w, size * 0.14).stroke({ color: premium ? accent : 0x9aa3b5, alpha: premium ? 0.6 : 0.2, width: line });111      plate.roundRect(x + line * 2, x + line * 2, w - line * 4, w * 0.5, size * 0.1).fill({ color: 0xffffff, alpha: 0.025 });112      if (opts.frame === "metal") for (const [cx, cy] of [[x + size * 0.07, x + size * 0.07], [x + w - size * 0.07, x + size * 0.07], [x + size * 0.07, x + w - size * 0.07], [x + w - size * 0.07, x + w - size * 0.07]]) plate.circle(cx, cy, size * 0.014).fill({ color: 0xc8d0dc, alpha: 0.5 });113      break;114  }115  return plate;116}117118export function drawSymbol(style: SymbolStyle, opts: SymbolArtOptions): Container {119  const { size } = opts;120  const c = new Container();121  const main = hex(style.color);122  const accent = style.accent ? hex(style.accent) : lighten(style.color, 0.35);123  const glow = style.glow ?? 0.3;124  const r = size * 0.36; // icon radius125  const g = new Graphics();126127  // Tile plate (per game frame preset).128  c.addChild(drawPlate(size, style, opts));129130  // Glow halo.131  if (glow > 0) {132    const halo = new Graphics();133    halo.circle(0, 0, r * 1.25).fill({ color: main, alpha: 0.18 * glow });134    halo.circle(0, 0, r * 1.0).fill({ color: main, alpha: 0.16 * glow });135    c.addChild(halo);136  }137138  switch (style.shape) {139    case "gem": {140      g.poly([0, -r, r * 0.85, -r * 0.25, r * 0.55, r, -r * 0.55, r, -r * 0.85, -r * 0.25]).fill(main);141      g.poly([0, -r, r * 0.85, -r * 0.25, 0, -r * 0.1, -r * 0.85, -r * 0.25]).fill({ color: accent, alpha: 0.85 });142      g.poly([0, -r * 0.1, r * 0.85, -r * 0.25, r * 0.55, r]).fill({ color: darken(style.color, 0.15) });143      g.poly([-r * 0.2, -r * 0.7, r * 0.15, -r * 0.55, -r * 0.35, -r * 0.35]).fill({ color: 0xffffff, alpha: 0.65 });144      break;145    }146    case "hex": {147      polygon(g, 0, 0, r, 6).fill(main);148      polygon(g, 0, 0, r * 0.68, 6).fill({ color: darken(style.color, 0.25) });149      polygon(g, 0, 0, r * 0.42, 6).fill(accent);150      if (style.label) c.addChild(label(style.label, size * 0.26, 0x0b0d14));151      break;152    }153    case "star": {154      star(g, 0, 0, r, r * 0.45).fill(main);155      star(g, 0, 0, r * 0.55, r * 0.25).fill({ color: 0xffffff, alpha: 0.85 });156      break;157    }158    case "circle": {159      g.circle(0, 0, r).fill(main);160      g.circle(-r * 0.3, -r * 0.3, r * 0.35).fill({ color: 0xffffff, alpha: 0.35 });161      g.circle(0, 0, r).stroke({ color: accent, width: size * 0.02 });162      break;163    }164    case "diamond": {165      g.poly([0, -r, r, 0, 0, r, -r, 0]).fill(main);166      g.poly([0, -r, r, 0, 0, 0]).fill({ color: accent, alpha: 0.8 });167      g.poly([0, 0, 0, r, -r, 0]).fill({ color: darken(style.color, 0.2) });168      break;169    }170    case "shield": {171      g.poly([0, -r, r * 0.85, -r * 0.65, r * 0.75, r * 0.15, 0, r, -r * 0.75, r * 0.15, -r * 0.85, -r * 0.65]).fill(main);172      g.poly([0, -r * 0.7, r * 0.5, -r * 0.5, r * 0.45, r * 0.05, 0, r * 0.6, -r * 0.45, r * 0.05, -r * 0.5, -r * 0.5]).fill({ color: darken(style.color, 0.28) });173      g.rect(-r * 0.08, -r * 0.5, r * 0.16, r * 0.9).fill(accent);174      g.rect(-r * 0.38, -r * 0.25, r * 0.76, r * 0.16).fill(accent);175      break;176    }177    case "bolt": {178      g.poly([r * 0.15, -r, -r * 0.55, r * 0.1, -r * 0.05, r * 0.1, -r * 0.25, r, r * 0.6, -r * 0.15, r * 0.05, -r * 0.15]).fill(main);179      g.poly([r * 0.1, -r * 0.8, -r * 0.3, r * 0.0, r * 0.0, 0]).fill({ color: 0xffffff, alpha: 0.5 });180      break;181    }182    case "ring": {183      g.circle(0, 0, r).fill(main);184      g.circle(0, 0, r * 0.62).cut();185      g.circle(0, 0, r * 0.62).stroke({ color: accent, width: size * 0.02, alpha: 0.9 });186      g.arc(0, 0, r * 0.81, -Math.PI * 0.9, -Math.PI * 0.3).stroke({ color: 0xffffff, width: size * 0.04, alpha: 0.55 });187      break;188    }189    case "chip": {190      g.roundRect(-r, -r * 0.75, r * 2, r * 1.5, r * 0.25).fill(main);191      g.roundRect(-r * 0.75, -r * 0.5, r * 1.5, r, r * 0.15).fill({ color: darken(style.color, 0.35) });192      for (let i = -2; i <= 2; i++) {193        g.rect(-r - r * 0.18, i * r * 0.28 - r * 0.06, r * 0.18, r * 0.12).fill(accent);194        g.rect(r, i * r * 0.28 - r * 0.06, r * 0.18, r * 0.12).fill(accent);195      }196      if (style.label) c.addChild(label(style.label, size * 0.22, accent));197      break;198    }199    case "crystal": {200      g.poly([0, -r, r * 0.5, -r * 0.3, r * 0.35, r * 0.8, -r * 0.35, r * 0.8, -r * 0.5, -r * 0.3]).fill(main);201      g.poly([0, -r, r * 0.5, -r * 0.3, 0, r * 0.1]).fill({ color: accent, alpha: 0.75 });202      g.poly([0, r * 0.1, r * 0.35, r * 0.8, -r * 0.35, r * 0.8]).fill({ color: darken(style.color, 0.2) });203      break;204    }205    case "letter": {206      g.roundRect(-r * 0.95, -r * 0.95, r * 1.9, r * 1.9, r * 0.3).fill({ color: darken(style.color, 0.45), alpha: 0.9 });207      g.roundRect(-r * 0.95, -r * 0.95, r * 1.9, r * 1.9, r * 0.3).stroke({ color: main, width: size * 0.025 });208      c.addChild(label(style.label ?? "?", size * 0.4, main, true));209      break;210    }211    case "vault": {212      g.circle(0, 0, r).fill(darken(style.color, 0.4));213      g.circle(0, 0, r).stroke({ color: main, width: size * 0.03 });214      g.circle(0, 0, r * 0.7).stroke({ color: main, width: size * 0.02, alpha: 0.7 });215      for (let i = 0; i < 6; i++) {216        const a = (i * Math.PI) / 3;217        g.moveTo(Math.cos(a) * r * 0.25, Math.sin(a) * r * 0.25).lineTo(Math.cos(a) * r * 0.62, Math.sin(a) * r * 0.62).stroke({ color: accent, width: size * 0.035 });218      }219      g.circle(0, 0, r * 0.22).fill(accent);220      break;221    }222    case "orb": {223      g.circle(0, 0, r).fill(main);224      g.circle(0, 0, r * 0.75).fill({ color: accent, alpha: 0.35 });225      g.circle(-r * 0.25, -r * 0.3, r * 0.25).fill({ color: 0xffffff, alpha: 0.5 });226      g.circle(0, 0, r).stroke({ color: accent, width: size * 0.02, alpha: 0.9 });227      if (style.label) c.addChild(label(style.label, size * 0.36, accent, true));228      break;229    }230    case "coin": {231      g.circle(0, 0, r).fill(main);232      g.circle(0, 0, r * 0.8).stroke({ color: darken(style.color, 0.25), width: size * 0.03 });233      g.circle(0, 0, r).stroke({ color: accent, width: size * 0.02 });234      c.addChild(label(style.label ?? "SC", size * 0.24, darken(style.color, 0.5), true));235      break;236    }237    case "skull": {238      g.circle(0, -r * 0.15, r * 0.8).fill(main);239      g.roundRect(-r * 0.45, r * 0.3, r * 0.9, r * 0.55, r * 0.15).fill(main);240      g.circle(-r * 0.3, -r * 0.2, r * 0.22).fill(0x0b0d14);241      g.circle(r * 0.3, -r * 0.2, r * 0.22).fill(0x0b0d14);242      g.poly([0, r * 0.05, r * 0.12, r * 0.3, -r * 0.12, r * 0.3]).fill(0x0b0d14);243      break;244    }245    case "flame": {246      g.poly([0, -r, r * 0.55, -r * 0.2, r * 0.7, r * 0.45, r * 0.3, r, -r * 0.3, r, -r * 0.7, r * 0.45, -r * 0.55, -r * 0.2]).fill(main);247      g.poly([0, -r * 0.4, r * 0.3, r * 0.1, r * 0.25, r * 0.6, -r * 0.25, r * 0.6, -r * 0.3, r * 0.1]).fill(accent);248      break;249    }250    case "snow": {251      for (let i = 0; i < 3; i++) {252        const a = (i * Math.PI) / 3;253        g.moveTo(Math.cos(a) * r, Math.sin(a) * r).lineTo(-Math.cos(a) * r, -Math.sin(a) * r).stroke({ color: main, width: size * 0.035 });254        for (const s of [1, -1]) {255          const bx = Math.cos(a) * r * 0.6 * s;256          const by = Math.sin(a) * r * 0.6 * s;257          g.moveTo(bx, by).lineTo(bx + Math.cos(a + 0.6) * r * 0.3 * s, by + Math.sin(a + 0.6) * r * 0.3 * s).stroke({ color: accent, width: size * 0.025 });258          g.moveTo(bx, by).lineTo(bx + Math.cos(a - 0.6) * r * 0.3 * s, by + Math.sin(a - 0.6) * r * 0.3 * s).stroke({ color: accent, width: size * 0.025 });259        }260      }261      g.circle(0, 0, r * 0.14).fill(0xffffff);262      break;263    }264    case "eye": {265      g.ellipse(0, 0, r, r * 0.6).fill(main);266      g.circle(0, 0, r * 0.42).fill(darken(style.color, 0.5));267      g.circle(0, 0, r * 0.2).fill(accent);268      g.circle(r * 0.12, -r * 0.14, r * 0.07).fill(0xffffff);269      break;270    }271    case "moon": {272      g.circle(0, 0, r).fill(main);273      g.circle(r * 0.45, -r * 0.2, r * 0.78).fill({ color: 0x0b0d14, alpha: 0.85 });274      g.circle(-r * 0.4, r * 0.3, r * 0.12).fill({ color: darken(style.color, 0.2) });275      break;276    }277    case "leaf": {278      g.moveTo(0, r).bezierCurveTo(-r * 1.1, r * 0.2, -r * 0.6, -r * 0.9, 0, -r).bezierCurveTo(r * 0.6, -r * 0.9, r * 1.1, r * 0.2, 0, r).fill(main);279      g.moveTo(0, r * 0.9).lineTo(0, -r * 0.8).stroke({ color: accent, width: size * 0.02 });280      break;281    }282    case "atom": {283      g.circle(0, 0, r * 0.2).fill(accent);284      for (let i = 0; i < 3; i++) {285        const e = new Graphics();286        e.ellipse(0, 0, r, r * 0.38).stroke({ color: main, width: size * 0.025 });287        e.rotation = (i * Math.PI) / 3;288        c.addChild(e);289      }290      break;291    }292    case "crown": {293      g.poly([-r, r * 0.6, -r, -r * 0.4, -r * 0.5, r * 0.05, 0, -r, r * 0.5, r * 0.05, r, -r * 0.4, r, r * 0.6]).fill(main);294      g.rect(-r, r * 0.4, r * 2, r * 0.25).fill(darken(style.color, 0.3));295      g.circle(0, -r * 0.95, r * 0.12).fill(accent);296      g.circle(-r, -r * 0.4, r * 0.1).fill(accent);297      g.circle(r, -r * 0.4, r * 0.1).fill(accent);298      break;299    }300    case "wave": {301      for (let k = -1; k <= 1; k++) {302        const y = k * r * 0.5;303        g.moveTo(-r, y).bezierCurveTo(-r * 0.5, y - r * 0.4, -r * 0.2, y + r * 0.4, 0, y).bezierCurveTo(r * 0.2, y - r * 0.4, r * 0.5, y + r * 0.4, r, y).stroke({ color: k === 0 ? accent : main, width: size * 0.04, alpha: k === 0 ? 1 : 0.8 });304      }305      break;306    }307    case "cube": {308      g.poly([0, -r, r * 0.87, -r * 0.5, r * 0.87, r * 0.5, 0, r, -r * 0.87, r * 0.5, -r * 0.87, -r * 0.5]).fill(main);309      g.poly([0, -r, r * 0.87, -r * 0.5, 0, 0, -r * 0.87, -r * 0.5]).fill(accent);310      g.poly([0, 0, r * 0.87, -r * 0.5, r * 0.87, r * 0.5, 0, r]).fill(darken(style.color, 0.3));311      break;312    }313    /* ----------------------------------------------------------- themed set */314    case "lock": {315      g.roundRect(-r * 0.75, -r * 0.1, r * 1.5, r * 1.1, r * 0.2).fill(main);316      g.arc(0, -r * 0.15, r * 0.5, Math.PI, 0).stroke({ color: accent, width: size * 0.05 });317      g.circle(0, r * 0.35, r * 0.16).fill(0x0b0d14);318      g.rect(-r * 0.06, r * 0.35, r * 0.12, r * 0.35).fill(0x0b0d14);319      break;320    }321    case "key": {322      g.circle(-r * 0.45, -r * 0.35, r * 0.42).stroke({ color: main, width: size * 0.06 });323      g.moveTo(-r * 0.15, -r * 0.05).lineTo(r * 0.85, r * 0.95).stroke({ color: main, width: size * 0.06 });324      g.moveTo(r * 0.45, r * 0.55).lineTo(r * 0.7, r * 0.3).stroke({ color: accent, width: size * 0.05 });325      g.moveTo(r * 0.65, r * 0.75).lineTo(r * 0.9, r * 0.5).stroke({ color: accent, width: size * 0.05 });326      break;327    }328    case "code": {329      g.roundRect(-r, -r * 0.7, r * 2, r * 1.4, r * 0.15).fill({ color: darken(style.color, 0.45), alpha: 0.95 });330      g.roundRect(-r, -r * 0.7, r * 2, r * 1.4, r * 0.15).stroke({ color: main, width: size * 0.02 });331      c.addChild(label(style.label ?? "</>", size * 0.26, main, true));332      break;333    }334    case "badge": {335      g.roundRect(-r * 0.7, -r, r * 1.4, r * 2, r * 0.18).fill(main);336      g.circle(0, -r * 0.4, r * 0.28).fill(0x0b0d14);337      g.roundRect(-r * 0.45, r * 0.05, r * 0.9, r * 0.14, r * 0.05).fill(accent);338      g.roundRect(-r * 0.45, r * 0.35, r * 0.6, r * 0.14, r * 0.05).fill(accent);339      break;340    }341    case "mask": {342      g.ellipse(0, 0, r, r * 0.6).fill(main);343      g.ellipse(-r * 0.4, -r * 0.05, r * 0.28, r * 0.18).fill(0x0b0d14);344      g.ellipse(r * 0.4, -r * 0.05, r * 0.28, r * 0.18).fill(0x0b0d14);345      g.moveTo(-r * 0.95, -r * 0.1).lineTo(-r * 1.3, -r * 0.6).stroke({ color: accent, width: size * 0.025 });346      g.moveTo(r * 0.95, -r * 0.1).lineTo(r * 1.3, -r * 0.6).stroke({ color: accent, width: size * 0.025 });347      break;348    }349    case "glove": {350      g.roundRect(-r * 0.55, -r * 0.2, r * 1.1, r * 1.1, r * 0.25).fill(main);351      for (let i = 0; i < 4; i++) g.roundRect(-r * 0.55 + i * r * 0.28, -r, r * 0.22, r * 0.9, r * 0.1).fill(main);352      g.roundRect(-r * 0.9, -r * 0.1, r * 0.35, r * 0.7, r * 0.15).fill(main);353      g.roundRect(-r * 0.55, r * 0.6, r * 1.1, r * 0.3, r * 0.08).fill(accent);354      break;355    }356    case "rope": {357      g.circle(0, 0, r * 0.75).stroke({ color: main, width: size * 0.09 });358      g.circle(0, 0, r * 0.75).stroke({ color: accent, width: size * 0.02, alpha: 0.6 });359      for (let i = 0; i < 10; i++) {360        const a = (i / 10) * Math.PI * 2;361        g.moveTo(Math.cos(a) * r * 0.62, Math.sin(a) * r * 0.62).lineTo(Math.cos(a + 0.3) * r * 0.88, Math.sin(a + 0.3) * r * 0.88).stroke({ color: darken(style.color, 0.3), width: size * 0.02 });362      }363      break;364    }365    case "bag": {366      g.moveTo(-r * 0.75, r * 0.9).bezierCurveTo(-r * 1.1, -r * 0.3, -r * 0.3, -r * 0.6, 0, -r * 0.55).bezierCurveTo(r * 0.3, -r * 0.6, r * 1.1, -r * 0.3, r * 0.75, r * 0.9).lineTo(-r * 0.75, r * 0.9).fill(main);367      g.roundRect(-r * 0.3, -r * 0.95, r * 0.6, r * 0.35, r * 0.1).fill(accent);368      g.moveTo(-r * 0.35, r * 0.3).lineTo(r * 0.35, r * 0.3).stroke({ color: darken(style.color, 0.3), width: size * 0.03 });369      break;370    }371    case "lantern": {372      g.roundRect(-r * 0.7, -r * 0.6, r * 1.4, r * 1.2, r * 0.45).fill(main);373      g.rect(-r * 0.35, -r * 0.9, r * 0.7, r * 0.2).fill(accent);374      g.rect(-r * 0.35, r * 0.6, r * 0.7, r * 0.2).fill(accent);375      g.moveTo(0, r * 0.8).lineTo(0, r).stroke({ color: accent, width: size * 0.03 });376      for (let i = -1; i <= 1; i++) g.moveTo(i * r * 0.3, -r * 0.55).lineTo(i * r * 0.3, r * 0.55).stroke({ color: darken(style.color, 0.3), width: size * 0.015 });377      break;378    }379    case "fan": {380      for (let i = -3; i <= 3; i++) {381        const a = -Math.PI / 2 + i * 0.28;382        g.moveTo(0, r * 0.7).lineTo(Math.cos(a) * r * 1.05, r * 0.7 + Math.sin(a) * r * 1.05).stroke({ color: i % 2 ? main : accent, width: size * 0.07 });383      }384      g.circle(0, r * 0.7, r * 0.14).fill(0x0b0d14);385      break;386    }387    case "scroll": {388      g.roundRect(-r * 0.7, -r * 0.85, r * 1.4, r * 1.7, r * 0.1).fill({ color: accent, alpha: 0.9 });389      g.roundRect(-r * 0.85, -r, r * 1.7, r * 0.3, r * 0.15).fill(main);390      g.roundRect(-r * 0.85, r * 0.7, r * 1.7, r * 0.3, r * 0.15).fill(main);391      for (let i = 0; i < 3; i++) g.rect(-r * 0.45, -r * 0.5 + i * r * 0.32, r * 0.9, r * 0.08).fill({ color: darken(style.color, 0.4), alpha: 0.7 });392      break;393    }394    case "cherry": {395      g.moveTo(-r * 0.3, r * 0.3).bezierCurveTo(-r * 0.2, -r * 0.5, r * 0.2, -r * 0.9, r * 0.5, -r).stroke({ color: 0x3ddc97, width: size * 0.035 });396      g.moveTo(r * 0.45, r * 0.3).bezierCurveTo(r * 0.5, -r * 0.4, r * 0.5, -r * 0.8, r * 0.5, -r).stroke({ color: 0x3ddc97, width: size * 0.035 });397      g.circle(-r * 0.35, r * 0.45, r * 0.42).fill(main);398      g.circle(r * 0.45, r * 0.45, r * 0.42).fill(main);399      g.circle(-r * 0.5, r * 0.3, r * 0.12).fill({ color: 0xffffff, alpha: 0.6 });400      g.circle(r * 0.3, r * 0.3, r * 0.12).fill({ color: 0xffffff, alpha: 0.6 });401      break;402    }403    case "seven": {404      g.poly([-r * 0.8, -r, r * 0.8, -r, r * 0.8, -r * 0.55, -r * 0.05, r, -r * 0.55, r, r * 0.2, -r * 0.55, -r * 0.8, -r * 0.55]).fill(main);405      g.poly([-r * 0.8, -r, r * 0.8, -r, r * 0.8, -r * 0.55, -r * 0.05, r, -r * 0.55, r, r * 0.2, -r * 0.55, -r * 0.8, -r * 0.55]).stroke({ color: accent, width: size * 0.03 });406      break;407    }408    case "bell": {409      g.moveTo(-r * 0.75, r * 0.5).bezierCurveTo(-r * 0.75, -r * 0.5, -r * 0.3, -r * 0.9, 0, -r * 0.9).bezierCurveTo(r * 0.3, -r * 0.9, r * 0.75, -r * 0.5, r * 0.75, r * 0.5).lineTo(-r * 0.75, r * 0.5).fill(main);410      g.roundRect(-r * 0.9, r * 0.45, r * 1.8, r * 0.18, r * 0.09).fill(accent);411      g.circle(0, r * 0.8, r * 0.15).fill(accent);412      g.circle(0, -r * 0.95, r * 0.1).fill(accent);413      break;414    }415    case "joystick": {416      g.roundRect(-r * 0.9, r * 0.3, r * 1.8, r * 0.6, r * 0.15).fill(darken(style.color, 0.4));417      g.rect(-r * 0.08, -r * 0.4, r * 0.16, r * 0.8).fill(0x9aa3b5);418      g.circle(0, -r * 0.55, r * 0.35).fill(main);419      g.circle(-r * 0.12, -r * 0.65, r * 0.1).fill({ color: 0xffffff, alpha: 0.5 });420      g.circle(r * 0.5, r * 0.6, r * 0.12).fill(accent);421      break;422    }423    case "torii": {424      g.rect(-r, -r * 0.85, r * 2, r * 0.18).fill(main);425      g.rect(-r * 0.8, -r * 0.5, r * 1.6, r * 0.12).fill(main);426      g.rect(-r * 0.6, -r * 0.5, r * 0.16, r * 1.5).fill(main);427      g.rect(r * 0.44, -r * 0.5, r * 0.16, r * 1.5).fill(main);428      g.rect(-r * 0.08, -r * 0.5, r * 0.16, r * 0.35).fill(accent);429      break;430    }431    case "katana": {432      g.moveTo(-r * 0.9, r * 0.9).lineTo(r * 0.7, -r * 0.7).stroke({ color: 0xdfe6f0, width: size * 0.05 });433      g.moveTo(-r * 0.9, r * 0.9).lineTo(r * 0.7, -r * 0.7).stroke({ color: 0xffffff, width: size * 0.015, alpha: 0.7 });434      g.moveTo(-r * 0.55, r * 0.35).lineTo(-r * 0.25, r * 0.65).stroke({ color: accent, width: size * 0.05 });435      g.moveTo(-r * 0.9, r * 0.9).lineTo(-r * 0.6, r * 0.6).stroke({ color: main, width: size * 0.08 });436      break;437    }438    case "cassette": {439      g.roundRect(-r, -r * 0.65, r * 2, r * 1.3, r * 0.12).fill(main);440      g.roundRect(-r * 0.75, -r * 0.4, r * 1.5, r * 0.55, r * 0.08).fill(darken(style.color, 0.45));441      g.circle(-r * 0.4, -r * 0.12, r * 0.16).fill(0x0b0d14).circle(r * 0.4, -r * 0.12, r * 0.16).fill(0x0b0d14);442      g.rect(-r * 0.6, r * 0.3, r * 1.2, r * 0.14).fill(accent);443      break;444    }445    case "ankh": {446      g.ellipse(0, -r * 0.55, r * 0.35, r * 0.42).stroke({ color: main, width: size * 0.06 });447      g.rect(-r * 0.08, -r * 0.1, r * 0.16, r * 1.1).fill(main);448      g.rect(-r * 0.7, -r * 0.1, r * 1.4, r * 0.16).fill(main);449      g.circle(0, -r * 0.55, r * 0.08).fill(accent);450      break;451    }452    case "scarab": {453      g.ellipse(0, r * 0.15, r * 0.6, r * 0.7).fill(main);454      g.circle(0, -r * 0.6, r * 0.28).fill(darken(style.color, 0.25));455      g.moveTo(0, -r * 0.4).lineTo(0, r * 0.8).stroke({ color: accent, width: size * 0.02 });456      for (const sgn of [-1, 1]) {457        g.moveTo(sgn * r * 0.55, -r * 0.1).lineTo(sgn * r * 0.95, -r * 0.5).stroke({ color: accent, width: size * 0.035 });458        g.moveTo(sgn * r * 0.6, r * 0.3).lineTo(sgn * r * 1.0, r * 0.4).stroke({ color: accent, width: size * 0.035 });459      }460      break;461    }462    case "pyramid": {463      g.poly([0, -r, r, r * 0.75, -r, r * 0.75]).fill(main);464      g.poly([0, -r, r, r * 0.75, 0, r * 0.75]).fill(darken(style.color, 0.25));465      g.poly([0, -r, r * 0.25, -r * 0.55, -r * 0.25, -r * 0.55]).fill(accent);466      break;467    }468    case "warning": {469      g.poly([0, -r, r * 0.95, r * 0.75, -r * 0.95, r * 0.75]).fill(main);470      g.poly([0, -r * 0.6, r * 0.55, r * 0.55, -r * 0.55, r * 0.55]).fill(0x0b0d14);471      g.rect(-r * 0.08, -r * 0.3, r * 0.16, r * 0.5).fill(main);472      g.circle(0, r * 0.38, r * 0.09).fill(main);473      break;474    }475    case "valve": {476      g.circle(0, 0, r * 0.85).stroke({ color: main, width: size * 0.07 });477      for (let i = 0; i < 4; i++) {478        const a = (i / 4) * Math.PI * 2;479        g.moveTo(0, 0).lineTo(Math.cos(a) * r * 0.85, Math.sin(a) * r * 0.85).stroke({ color: main, width: size * 0.05 });480      }481      g.circle(0, 0, r * 0.25).fill(accent);482      break;483    }484    case "gauge": {485      g.arc(0, r * 0.2, r * 0.9, Math.PI, 0).stroke({ color: darken(style.color, 0.3), width: size * 0.09 });486      g.arc(0, r * 0.2, r * 0.9, Math.PI, Math.PI * 1.6).stroke({ color: main, width: size * 0.09 });487      g.moveTo(0, r * 0.2).lineTo(r * 0.45, -r * 0.5).stroke({ color: accent, width: size * 0.04 });488      g.circle(0, r * 0.2, r * 0.12).fill(accent);489      break;490    }491    case "barrel": {492      g.roundRect(-r * 0.65, -r * 0.9, r * 1.3, r * 1.8, r * 0.2).fill(main);493      g.rect(-r * 0.65, -r * 0.45, r * 1.3, r * 0.12).fill(darken(style.color, 0.4));494      g.rect(-r * 0.65, r * 0.33, r * 1.3, r * 0.12).fill(darken(style.color, 0.4));495      polygon(g, 0, 0, r * 0.28, 3, -Math.PI / 2).fill(accent);496      break;497    }498    case "wheel": {499      g.circle(0, 0, r * 0.95).fill(0x0b0d14);500      g.circle(0, 0, r * 0.95).stroke({ color: main, width: size * 0.05 });501      g.circle(0, 0, r * 0.6).stroke({ color: accent, width: size * 0.03 });502      for (let i = 0; i < 5; i++) {503        const a = (i / 5) * Math.PI * 2;504        g.moveTo(0, 0).lineTo(Math.cos(a) * r * 0.6, Math.sin(a) * r * 0.6).stroke({ color: accent, width: size * 0.035 });505      }506      g.circle(0, 0, r * 0.14).fill(main);507      break;508    }509    case "helmet": {510      g.arc(0, r * 0.1, r * 0.85, Math.PI, 0).fill(main);511      g.rect(-r * 0.85, r * 0.1, r * 1.7, r * 0.45).fill(main);512      g.roundRect(-r * 0.6, -r * 0.05, r * 1.4, r * 0.4, r * 0.1).fill(0x0b0d14);513      g.moveTo(-r * 0.3, -r * 0.7).lineTo(r * 0.3, -r * 0.7).stroke({ color: accent, width: size * 0.04 });514      break;515    }516    case "flag": {517      g.rect(-r * 0.85, -r, r * 0.1, r * 2).fill(0x9aa3b5);518      for (let i = 0; i < 4; i++) for (let j = 0; j < 3; j++) g.rect(-r * 0.75 + i * r * 0.4, -r + j * r * 0.33, r * 0.4, r * 0.33).fill((i + j) % 2 ? main : accent);519      break;520    }521    case "trophy": {522      g.moveTo(-r * 0.7, -r).lineTo(r * 0.7, -r).lineTo(r * 0.45, r * 0.15).lineTo(-r * 0.45, r * 0.15).fill(main);523      g.arc(-r * 0.75, -r * 0.55, r * 0.3, Math.PI * 0.5, Math.PI * 1.5).stroke({ color: main, width: size * 0.04 });524      g.arc(r * 0.75, -r * 0.55, r * 0.3, -Math.PI * 0.5, Math.PI * 0.5).stroke({ color: main, width: size * 0.04 });525      g.rect(-r * 0.1, r * 0.15, r * 0.2, r * 0.4).fill(main);526      g.roundRect(-r * 0.5, r * 0.55, r, r * 0.3, r * 0.06).fill(accent);527      break;528    }529    case "idol": {530      g.roundRect(-r * 0.55, -r * 0.9, r * 1.1, r * 1.8, r * 0.15).fill(main);531      g.rect(-r * 0.4, -r * 0.55, r * 0.8, r * 0.14).fill(0x0b0d14);532      g.rect(-r * 0.4, -r * 0.1, r * 0.8, r * 0.14).fill(0x0b0d14);533      g.poly([-r * 0.3, r * 0.35, r * 0.3, r * 0.35, 0, r * 0.7]).fill(accent);534      break;535    }536    case "totem": {537      for (let i = 0; i < 3; i++) {538        g.roundRect(-r * 0.55, -r + i * r * 0.68, r * 1.1, r * 0.6, r * 0.08).fill(i % 2 ? darken(style.color, 0.25) : main);539        g.circle(-r * 0.22, -r * 0.7 + i * r * 0.68, r * 0.08).fill(accent);540        g.circle(r * 0.22, -r * 0.7 + i * r * 0.68, r * 0.08).fill(accent);541      }542      break;543    }544    case "snake": {545      g.moveTo(-r * 0.8, r * 0.8).bezierCurveTo(-r * 0.9, -r * 0.2, r * 0.9, r * 0.6, r * 0.6, -r * 0.5).bezierCurveTo(r * 0.4, -r * 1.0, -r * 0.2, -r * 0.6, 0, -r * 0.3).stroke({ color: main, width: size * 0.08 });546      g.circle(0, -r * 0.3, r * 0.2).fill(main);547      g.circle(r * 0.06, -r * 0.36, r * 0.06).fill(accent);548      break;549    }550    case "rocket": {551      g.moveTo(0, -r).bezierCurveTo(r * 0.6, -r * 0.4, r * 0.5, r * 0.4, r * 0.35, r * 0.6).lineTo(-r * 0.35, r * 0.6).bezierCurveTo(-r * 0.5, r * 0.4, -r * 0.6, -r * 0.4, 0, -r).fill(main);552      g.poly([-r * 0.35, r * 0.2, -r * 0.75, r * 0.8, -r * 0.35, r * 0.6]).fill(accent);553      g.poly([r * 0.35, r * 0.2, r * 0.75, r * 0.8, r * 0.35, r * 0.6]).fill(accent);554      g.circle(0, -r * 0.25, r * 0.18).fill(0x0b0d14);555      g.poly([-r * 0.2, r * 0.6, r * 0.2, r * 0.6, 0, r]).fill(0xffb347);556      break;557    }558    case "anchor": {559      g.circle(0, -r * 0.75, r * 0.2).stroke({ color: main, width: size * 0.05 });560      g.rect(-r * 0.07, -r * 0.55, r * 0.14, r * 1.4).fill(main);561      g.rect(-r * 0.55, -r * 0.35, r * 1.1, r * 0.12).fill(main);562      g.arc(0, r * 0.15, r * 0.75, Math.PI * 0.15, Math.PI * 0.85).stroke({ color: main, width: size * 0.07 });563      g.circle(-r * 0.7, r * 0.4, r * 0.08).fill(accent).circle(r * 0.7, r * 0.4, r * 0.08).fill(accent);564      break;565    }566    case "chest": {567      g.roundRect(-r * 0.9, -r * 0.2, r * 1.8, r * 1.0, r * 0.1).fill(main);568      g.roundRect(-r * 0.9, -r * 0.75, r * 1.8, r * 0.6, r * 0.3).fill(darken(style.color, 0.2));569      g.rect(-r * 0.9, -r * 0.2, r * 1.8, r * 0.12).fill(accent);570      g.roundRect(-r * 0.15, -r * 0.2, r * 0.3, r * 0.35, r * 0.05).fill(accent);571      break;572    }573    case "trident": {574      g.rect(-r * 0.07, -r * 0.4, r * 0.14, r * 1.4).fill(main);575      for (const dx of [-0.5, 0, 0.5]) {576        g.moveTo(dx * r, -r * 0.3).lineTo(dx * r, -r).stroke({ color: main, width: size * 0.05 });577        g.poly([dx * r - r * 0.12, -r * 0.85, dx * r + r * 0.12, -r * 0.85, dx * r, -r * 1.1]).fill(accent);578      }579      g.rect(-r * 0.5, -r * 0.4, r, r * 0.12).fill(main);580      break;581    }582    case "shell": {583      g.arc(0, r * 0.4, r * 0.95, Math.PI, 0).fill(main);584      for (let i = -3; i <= 3; i++) g.moveTo(0, r * 0.4).lineTo(Math.cos(-Math.PI / 2 + i * 0.38) * r * 0.95, r * 0.4 + Math.sin(-Math.PI / 2 + i * 0.38) * r * 0.95).stroke({ color: darken(style.color, 0.25), width: size * 0.02 });585      g.rect(-r * 0.95, r * 0.4, r * 1.9, r * 0.18).fill(accent);586      break;587    }588    case "planet": {589      g.circle(0, 0, r * 0.7).fill(main);590      g.ellipse(0, r * 0.05, r * 1.05, r * 0.28).stroke({ color: accent, width: size * 0.04 });591      g.circle(-r * 0.25, -r * 0.25, r * 0.18).fill({ color: 0xffffff, alpha: 0.3 });592      g.ellipse(r * 0.15, r * 0.15, r * 0.3, r * 0.12).fill({ color: darken(style.color, 0.25), alpha: 0.8 });593      break;594    }595    case "comet": {596      g.moveTo(r * 0.5, -r * 0.5).lineTo(-r * 1.0, r * 0.15).lineTo(-r * 0.6, r * 0.6).lineTo(r * 0.5, -r * 0.5).fill({ color: accent, alpha: 0.5 });597      g.moveTo(r * 0.5, -r * 0.5).lineTo(-r * 0.7, r * 0.7).stroke({ color: accent, alpha: 0.4, width: size * 0.05 });598      g.circle(r * 0.5, -r * 0.5, r * 0.4).fill(main);599      g.circle(r * 0.4, -r * 0.6, r * 0.12).fill({ color: 0xffffff, alpha: 0.6 });600      break;601    }602    case "satellite": {603      g.roundRect(-r * 0.3, -r * 0.3, r * 0.6, r * 0.6, r * 0.1).fill(main);604      g.rect(-r * 1.0, -r * 0.18, r * 0.6, r * 0.36).fill(accent);605      g.rect(r * 0.4, -r * 0.18, r * 0.6, r * 0.36).fill(accent);606      for (const x of [-0.8, -0.6, 0.6, 0.8]) g.moveTo(x * r, -r * 0.18).lineTo(x * r, r * 0.18).stroke({ color: darken(style.color, 0.4), width: size * 0.012 });607      g.arc(0, -r * 0.55, r * 0.3, Math.PI, 0).stroke({ color: main, width: size * 0.03 });608      break;609    }610    case "pickaxe": {611      g.moveTo(-r * 0.7, r * 0.9).lineTo(r * 0.5, -r * 0.4).stroke({ color: 0x8a6d2e, width: size * 0.06 });612      g.moveTo(r * 0.0, -r * 0.9).bezierCurveTo(r * 0.7, -r * 0.9, r * 1.0, -r * 0.4, r * 0.95, r * 0.15).lineTo(r * 0.55, -r * 0.35).fill(main);613      g.moveTo(r * 0.0, -r * 0.9).lineTo(r * 0.55, -r * 0.35).stroke({ color: accent, width: size * 0.03 });614      break;615    }616    case "ore": {617      polygon(g, 0, r * 0.1, r * 0.9, 7).fill(0x3a3f4a);618      g.poly([-r * 0.3, -r * 0.1, 0, -r * 0.5, r * 0.35, -r * 0.05, r * 0.1, r * 0.35, -r * 0.25, r * 0.3]).fill(main);619      g.poly([-r * 0.3, -r * 0.1, 0, -r * 0.5, 0, 0]).fill(accent);620      break;621    }622    case "drill": {623      g.rect(-r * 0.4, -r * 0.9, r * 0.8, r * 0.7).fill(main);624      g.poly([-r * 0.4, -r * 0.2, r * 0.4, -r * 0.2, 0, r]).fill(0xc8d0dc);625      for (let i = 0; i < 4; i++) g.moveTo(-r * 0.32 + i * r * 0.1, -r * 0.2 + i * r * 0.28).lineTo(r * 0.32 - i * r * 0.1, -r * 0.2 + i * r * 0.28).stroke({ color: 0x5c6270, width: size * 0.015 });626      g.rect(-r * 0.15, -r * 0.6, r * 0.3, r * 0.15).fill(accent);627      break;628    }629    case "ingot": {630      g.poly([-r * 0.7, -r * 0.3, r * 0.7, -r * 0.3, r * 0.95, r * 0.5, -r * 0.95, r * 0.5]).fill(main);631      g.poly([-r * 0.7, -r * 0.3, r * 0.7, -r * 0.3, r * 0.5, 0, -r * 0.5, 0]).fill(accent);632      g.moveTo(-r * 0.5, r * 0.15).lineTo(r * 0.5, r * 0.15).stroke({ color: darken(style.color, 0.3), width: size * 0.02 });633      break;634    }635    case "hourglass": {636      g.poly([-r * 0.7, -r, r * 0.7, -r, r * 0.1, 0, r * 0.7, r, -r * 0.7, r, -r * 0.1, 0]).stroke({ color: main, width: size * 0.04 });637      g.poly([-r * 0.45, -r * 0.8, r * 0.45, -r * 0.8, 0, -r * 0.1]).fill({ color: accent, alpha: 0.8 });638      g.poly([-r * 0.55, r * 0.9, r * 0.55, r * 0.9, 0, r * 0.35]).fill({ color: accent, alpha: 0.8 });639      break;640    }641    case "igloo": {642      g.arc(0, r * 0.4, r * 0.95, Math.PI, 0).fill(main);643      g.rect(-r * 0.95, r * 0.4, r * 1.9, r * 0.2).fill(main);644      g.arc(-r * 0.3, r * 0.5, r * 0.35, Math.PI, 0).fill(0x0b0d14);645      for (let i = 0; i < 3; i++) g.arc(0, r * 0.4, r * (0.35 + i * 0.25), Math.PI, 0).stroke({ color: accent, width: size * 0.015, alpha: 0.6 });646      break;647    }648    case "paw": {649      g.ellipse(0, r * 0.35, r * 0.55, r * 0.45).fill(main);650      for (const [dx, dy] of [[-0.6, -0.25], [-0.22, -0.6], [0.22, -0.6], [0.6, -0.25]]) g.circle(dx * r, dy * r, r * 0.2).fill(main);651      g.ellipse(0, r * 0.4, r * 0.3, r * 0.22).fill({ color: accent, alpha: 0.6 });652      break;653    }654    case "fish": {655      g.ellipse(-r * 0.1, 0, r * 0.7, r * 0.42).fill(main);656      g.poly([r * 0.5, 0, r, -r * 0.45, r, r * 0.45]).fill(accent);657      g.circle(-r * 0.5, -r * 0.1, r * 0.08).fill(0x0b0d14);658      g.moveTo(-r * 0.2, -r * 0.4).bezierCurveTo(0, -r * 0.7, r * 0.2, -r * 0.5, r * 0.25, -r * 0.3).fill({ color: accent, alpha: 0.8 });659      break;660    }661    case "meteor": {662      polygon(g, r * 0.2, r * 0.2, r * 0.6, 7, 0.3).fill(main);663      g.circle(r * 0.35, 0, r * 0.12).fill({ color: darken(style.color, 0.3) });664      g.circle(0, r * 0.4, r * 0.1).fill({ color: darken(style.color, 0.3) });665      g.moveTo(-r * 0.2, -r * 0.3).lineTo(-r * 1.0, -r * 1.0).stroke({ color: accent, alpha: 0.6, width: size * 0.06 });666      g.moveTo(r * 0.1, -r * 0.55).lineTo(-r * 0.5, -r * 1.05).stroke({ color: accent, alpha: 0.4, width: size * 0.04 });667      break;668    }669  }670  c.addChild(g);671  return c;672}673674function label(text: string, fontSize: number, color: number, bold = true): Text {675  const t = new Text({676    text,677    style: new TextStyle({ fontFamily: "Geist, Inter, system-ui, sans-serif", fontSize, fontWeight: bold ? "800" : "600", fill: color, letterSpacing: 1 }),678  });679  t.anchor.set(0.5);680  return t;681}682683export { hex as hexColor, lighten, darken };684