File size: 4,894 Bytes
9032518
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// pro_ui.js β€” injects a professional monochrome SVG icon set into the Car
// Studio panel and toolbar (COMSOL/ANSYS-style), and strips emoji from button
// labels. Pure presentation: no behaviour is touched.

(function () {
  const P = {   // 24x24 stroke paths, keyed by icon name
    car:      'M4 15l1.5-4.5A2 2 0 0 1 7.4 9h9.2a2 2 0 0 1 1.9 1.5L20 15M4 15h16v3h-2M4 15v3h2m0 0a2 2 0 1 0 4 0m-4 0a2 2 0 1 1 4 0m4 0h4m0 0a2 2 0 1 0 4 0 2 2 0 0 0-4 0z',
    roofDown: 'M12 3v10m0 0l-4-4m4 4l4-4M4 20h16',
    roofUp:   'M12 21V11m0 0l-4 4m4-4l4 4M4 4h16',
    resizeH:  'M3 12h18M7 8l-4 4 4 4M17 8l4 4-4 4',
    resizeV:  'M12 3v18M8 7l4-4 4 4M8 17l4 4 4-4',
    eye:      'M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6-10-6-10-6z M12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6z',
    search:   'M11 4a7 7 0 1 0 0 14 7 7 0 0 0 0-14zM21 21l-4.5-4.5',
    wind:     'M3 8h9a3 3 0 1 0-3-3M3 12h13a3 3 0 1 1-3 3M3 16h7',
    gauge:    'M12 4a8 8 0 0 0-8 8h4m8-8a8 8 0 0 1 8 8h-4M12 12l3-4M12 12a1.5 1.5 0 1 0 .01 0z',
    layers:   'M12 3l9 5-9 5-9-5 9-5zM3 13l9 5 9-5M3 17l9 5 9-5',
    sound:    'M4 10v4h4l5 4V6l-5 4H4zM16 9a4 4 0 0 1 0 6M18.5 6.5a8 8 0 0 1 0 11',
    shield:   'M12 3l8 3v6c0 4.5-3.5 8-8 9-4.5-1-8-4.5-8-9V6l8-3z',
    map:      'M9 4l6 2 6-2v14l-6 2-6-2-6 2V6l6-2zM9 4v14M15 6v14',
    sliders:  'M4 8h10M18 8h2M4 16h4M12 16h8M14 5v6M8 13v6',
    morph:    'M4 18c0-6 4-10 8-10s8 4 8 10M4 18h16M8 8a4 4 0 1 1 8 0',
    play:     'M6 4l14 8-14 8V4z',
    waves:    'M2 8c2-2 4-2 6 0s4 2 6 0 4-2 6 0M2 14c2-2 4-2 6 0s4 2 6 0 4-2 6 0',
    camera:   'M4 8h3l2-3h6l2 3h3v11H4V8zM12 11a3.5 3.5 0 1 0 0 7 3.5 3.5 0 0 0 0-7z',
    wand:     'M6 18L18 6M14 6h4v4M4 10l1-2 2-1-2-1-1-2-1 2-2 1 2 1 1 2zM19 15l.7-1.4L21 13l-1.3-.6L19 11l-.7 1.4L17 13l1.3.6L19 15z',
    dots:     'M6 6h.01M12 6h.01M18 6h.01M6 12h.01M12 12h.01M18 12h.01M6 18h.01M12 18h.01M18 18h.01',
    cube:     'M12 2l9 5v10l-9 5-9-5V7l9-5zM12 12l9-5M12 12v10M12 12L3 7',
    grid:     'M4 4h16v16H4zM4 10h16M4 16h16M10 4v16M16 4v16',
    xray:     'M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18zM12 7v10M8 9.5h8M9 14h6',
    hand:     'M14 6.5a2 2 0 1 1 4 0V11M10 6a2 2 0 1 1 4 0v5M6 8a2 2 0 1 1 4 0v4M6 12v3c0 3.5 2.5 6 6 6s6-2.5 6-6v-4',
    wrench:   'M14.7 6.3a4.5 4.5 0 0 0-6.4 6.4l-4.6 4.6a1.5 1.5 0 0 0 2.1 2.1l4.6-4.6a4.5 4.5 0 0 0 6.4-6.4l-2.8 2.8-2.1-2.1 2.8-2.8z',
    pencil:   'M4 20l1-4L17 4l3 3-12 12-4 1zM14 7l3 3',
    crash:    'M12 3l2.1 4.7 5.1.5-3.8 3.4 1.1 5-4.5-2.6-4.5 2.6 1.1-5L4.8 8.2l5.1-.5L12 3z',
    live:     'M3 12h4l3-8 4 16 3-8h4',
  };
  const svg = (name) => {
    const s = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    s.setAttribute('viewBox', '0 0 24 24'); s.classList.add('pro-ic');
    const p = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    p.setAttribute('d', P[name]); s.appendChild(p); return s;
  };

  // Car Studio buttons: match by label text (first match wins)
  const RULES = [
    [/^(sedan|coupe|suv|wagon)$/i, 'car'],
    [/lower roof/i, 'roofDown'], [/raise roof/i, 'roofUp'],
    [/longer|shorter|wider|narrower/i, 'resizeH'],
    [/taller|^lower$/i, 'resizeV'],
    [/interior|exterior/i, 'eye'], [/inspect/i, 'search'],
    [/drag from image/i, 'camera'], [/drag/i, 'wind'],
    [/pressure/i, 'gauge'], [/wall shear/i, 'layers'],
    [/nvh/i, 'sound'], [/crash/i, 'crash'],
    [/retrieve/i, 'search'], [/interpolate/i, 'sliders'],
    [/design map/i, 'map'], [/deepsdf/i, 'morph'],
    [/real cfd|openfoam/i, 'play'], [/flow field|wind flow/i, 'waves'],
    [/live aero/i, 'live'],
    [/photoreal/i, 'camera'], [/style render/i, 'wand'],
    [/point cloud/i, 'dots'], [/solid mesh/i, 'cube'],
    [/wireframe/i, 'grid'], [/x-?ray/i, 'xray'], [/shaded/i, 'cube'],
  ];
  const EMOJI = /^[\s\u{1F000}-\u{1FAFF}\u{2600}-\u{27BF}\u{2B00}-\u{2BFF}\u{FE0F}\u{200D}βœοΈπŸ–πŸ”§πŸš—πŸ“·βœ¦πŸ’₯πŸŒ€πŸŽ¨]+/u;

  function decorate() {
    document.querySelectorAll('#carStudio .cs-btn').forEach(b => {
      if (b.querySelector('svg.pro-ic')) return;
      const label = b.textContent.trim();
      for (const [re, ic] of RULES) {
        if (re.test(label)) {
          b.textContent = label.replace(EMOJI, '');
          b.prepend(svg(ic));
          break;
        }
      }
    });
    // toolbar buttons that still use emoji -> proper icons
    const TB = { gestureBtn: 'hand', disasmBtn: 'wrench',
                 carSketchBtn: 'car', freehandPadBtn: 'pencil' };
    for (const [id, ic] of Object.entries(TB)) {
      const b = document.getElementById(id);
      if (!b || b.querySelector('svg.pro-ic')) continue;
      b.textContent = b.textContent.replace(EMOJI, '').trim();
      b.prepend(svg(ic));
    }
  }
  if (document.readyState === 'loading')
    document.addEventListener('DOMContentLoaded', decorate);
  else decorate();
  // re-decorate when dynamic UI (e.g. parts panel) rebuilds buttons
  setInterval(decorate, 4000);
})();