@wavegrid/canvas 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/esm/ui.js +17 -93
  2. package/package.json +2 -2
  3. package/ui.js +17 -93
package/esm/ui.js CHANGED
@@ -239,19 +239,6 @@ export function getCanvasHTML(numCannons = 49, gridColumns = 7) {
239
239
  display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--text2);
240
240
  }
241
241
 
242
- /* ─── Symmetry Tools ─── */
243
- .symmetry-tools {
244
- display: flex; gap: 6px;
245
- }
246
- .sym-btn {
247
- width: 48px; height: 48px; border-radius: 12px; font-size: 18px;
248
- display: flex; align-items: center; justify-content: center;
249
- background: var(--surface2); border: 1px solid var(--border);
250
- color: var(--text2); cursor: pointer; transition: all 0.2s;
251
- }
252
- .sym-btn:hover { border-color: var(--text2); }
253
- .sym-btn.active { border-color: var(--accent); color: var(--accent); background: rgba(74,124,255,0.1); }
254
-
255
242
  /* ─── Drops Mode ─── */
256
243
  .drops-controls {
257
244
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
@@ -332,13 +319,12 @@ export function getCanvasHTML(numCannons = 49, gridColumns = 7) {
332
319
  <div class="mode-tabs" id="mode-tabs">
333
320
  <div class="mode-tab active" data-mode="paint">Paint</div>
334
321
  <div class="mode-tab" data-mode="gradient">Gradient</div>
335
- <div class="mode-tab" data-mode="brush">Brush</div>
336
322
  <div class="mode-tab" data-mode="energy">Energy</div>
337
323
  <div class="mode-tab" data-mode="scenes">Scenes</div>
338
324
  <div class="mode-tab" data-mode="animations">Animations</div>
339
325
  <div class="mode-tab" data-mode="motion">Motion</div>
340
326
  <div class="mode-tab" data-mode="drops">Drops</div>
341
- <div class="mode-tab" data-mode="symmetry">Symmetry</div>
327
+
342
328
  </div>
343
329
  <div class="tool-area">
344
330
 
@@ -353,6 +339,16 @@ export function getCanvasHTML(numCannons = 49, gridColumns = 7) {
353
339
  <div class="brightness-thumb" id="bright-thumb" style="bottom:80%"></div>
354
340
  </div>
355
341
  <div class="color-preview" id="color-preview" style="background:#4a7cff"></div>
342
+ <div class="brush-controls" style="margin-left:8px">
343
+ <div class="brush-size-wrap">
344
+ <div class="brush-preview">
345
+ <div class="brush-dot" id="brush-dot" style="width:20px;height:20px"></div>
346
+ </div>
347
+ <span class="brush-label">Size</span>
348
+ <input type="range" class="brush-slider" id="brush-size" min="1" max="5" value="1">
349
+ </div>
350
+ <div class="toggle-pill" id="brush-falloff">Soft edge</div>
351
+ </div>
356
352
  </div>
357
353
  </div>
358
354
 
@@ -366,26 +362,6 @@ export function getCanvasHTML(numCannons = 49, gridColumns = 7) {
366
362
  </div>
367
363
  </div>
368
364
 
369
- <!-- Brush Mode -->
370
- <div class="tool-panel" id="panel-brush">
371
- <div class="color-section">
372
- <div id="brush-color-wheel-wrap" style="position:relative;width:80px;height:80px;flex-shrink:0">
373
- <canvas id="brush-color-wheel" width="80" height="80" style="width:80px;height:80px;border-radius:50%;cursor:crosshair;touch-action:none"></canvas>
374
- <div id="brush-wheel-cursor" style="position:absolute;width:12px;height:12px;border:2px solid #fff;border-radius:50%;pointer-events:none;transform:translate(-50%,-50%);box-shadow:0 0 6px rgba(0,0,0,0.5);left:40px;top:40px"></div>
375
- </div>
376
- <div class="brush-controls">
377
- <div class="brush-size-wrap">
378
- <div class="brush-preview">
379
- <div class="brush-dot" id="brush-dot" style="width:20px;height:20px"></div>
380
- </div>
381
- <span class="brush-label">Size</span>
382
- <input type="range" class="brush-slider" id="brush-size" min="1" max="5" value="1">
383
- </div>
384
- <div class="toggle-pill" id="brush-falloff">Soft edge</div>
385
- </div>
386
- </div>
387
- </div>
388
-
389
365
  <!-- Energy Mode -->
390
366
  <div class="tool-panel" id="panel-energy">
391
367
  <div style="flex:1;display:flex;flex-direction:column;align-items:center;gap:8px">
@@ -445,15 +421,7 @@ export function getCanvasHTML(numCannons = 49, gridColumns = 7) {
445
421
  </div>
446
422
  </div>
447
423
 
448
- <!-- Symmetry -->
449
- <div class="tool-panel" id="panel-symmetry">
450
- <div class="symmetry-tools">
451
- <div class="sym-btn" data-sym="h" title="Mirror left/right">↔</div>
452
- <div class="sym-btn" data-sym="v" title="Mirror top/bottom">↕</div>
453
- <div class="sym-btn" data-sym="radial" title="Radial symmetry">✦</div>
454
- <div class="sym-btn" data-sym="kaleidoscope" title="Kaleidoscope">❋</div>
455
- </div>
456
- </div>
424
+
457
425
  </div>
458
426
  </div>
459
427
  </div>
@@ -474,7 +442,7 @@ let brushSize = 1;
474
442
  let brushFalloff = false;
475
443
  let activeScene = 'civic';
476
444
  let activeAnimation = null;
477
- let symmetry = { h: false, v: false, radial: false, kaleidoscope: false };
445
+
478
446
 
479
447
  // Motion painter state
480
448
  let motionPath = [];
@@ -649,7 +617,7 @@ function getAffectedCannons(centerIdx) {
649
617
  const result = [{ idx: centerIdx, falloff: 1 }];
650
618
 
651
619
  // Brush size > 1: include neighbors
652
- if (brushSize > 1 && (currentMode === 'brush' || currentMode === 'paint')) {
620
+ if (brushSize > 1 && currentMode === 'paint') {
653
621
  const reach = brushSize - 1;
654
622
  for (let dr = -reach; dr <= reach; dr++) {
655
623
  for (let dc = -reach; dc <= reach; dc++) {
@@ -664,34 +632,9 @@ function getAffectedCannons(centerIdx) {
664
632
  }
665
633
  }
666
634
 
667
- // Symmetry: mirror all affected cannons
668
- const mirrored = [];
669
- for (const { idx, falloff } of result) {
670
- mirrored.push({ idx, falloff });
671
- const r = Math.floor(idx / GRID), c = idx % GRID;
672
- if (symmetry.h) mirrored.push({ idx: r * GRID + (GRID - 1 - c), falloff });
673
- if (symmetry.v) mirrored.push({ idx: (GRID - 1 - r) * GRID + c, falloff });
674
- if (symmetry.h && symmetry.v) mirrored.push({ idx: (GRID - 1 - r) * GRID + (GRID - 1 - c), falloff });
675
- if (symmetry.radial) {
676
- // 4-fold rotational
677
- mirrored.push({ idx: c * GRID + (GRID - 1 - r), falloff });
678
- mirrored.push({ idx: (GRID - 1 - c) * GRID + r, falloff });
679
- }
680
- if (symmetry.kaleidoscope) {
681
- // 8-fold
682
- mirrored.push({ idx: r * GRID + (GRID - 1 - c), falloff });
683
- mirrored.push({ idx: (GRID - 1 - r) * GRID + c, falloff });
684
- mirrored.push({ idx: (GRID - 1 - r) * GRID + (GRID - 1 - c), falloff });
685
- mirrored.push({ idx: c * GRID + r, falloff });
686
- mirrored.push({ idx: c * GRID + (GRID - 1 - r), falloff });
687
- mirrored.push({ idx: (GRID - 1 - c) * GRID + r, falloff });
688
- mirrored.push({ idx: (GRID - 1 - c) * GRID + (GRID - 1 - r), falloff });
689
- }
690
- }
691
-
692
635
  // Deduplicate
693
636
  const seen = new Set();
694
- return mirrored.filter(m => {
637
+ return result.filter(m => {
695
638
  if (m.idx < 0 || m.idx >= NUM || seen.has(m.idx)) return false;
696
639
  seen.add(m.idx);
697
640
  return true;
@@ -759,7 +702,7 @@ function handleSculptureStart(e) {
759
702
  return;
760
703
  }
761
704
 
762
- if (idx >= 0 && (currentMode === 'paint' || currentMode === 'brush')) {
705
+ if (idx >= 0 && currentMode === 'paint') {
763
706
  const affected = getAffectedCannons(idx);
764
707
  affected.forEach(a => paintCannon(a.idx, a.falloff));
765
708
  lastPaintedIdx = idx;
@@ -809,7 +752,7 @@ function handleSculptureMove(e) {
809
752
  return;
810
753
  }
811
754
 
812
- if (currentMode === 'paint' || currentMode === 'brush') {
755
+ if (currentMode === 'paint') {
813
756
  const affected = getAffectedCannons(idx);
814
757
  affected.forEach(a => paintCannon(a.idx, a.falloff));
815
758
  lastPaintedIdx = idx;
@@ -896,14 +839,6 @@ setupColorWheel('color-wheel', 'wheel-cursor', (h, s) => {
896
839
  updateColorPreview();
897
840
  });
898
841
 
899
- setupColorWheel('brush-color-wheel', 'brush-wheel-cursor', (h, s) => {
900
- currentHue = h;
901
- currentSat = s;
902
- updateColorPreview();
903
- const dot = document.getElementById('brush-dot');
904
- dot.style.background = hsl(h, s, 50);
905
- });
906
-
907
842
  // Brightness bar
908
843
  function setupBrightnessBar() {
909
844
  const bar = document.getElementById('bright-bar');
@@ -1119,17 +1054,6 @@ function playMotionStep() {
1119
1054
  motionTimer = setTimeout(playMotionStep, 300 - speed * 25);
1120
1055
  }
1121
1056
 
1122
- // ═══════════════════════════════════════════════════
1123
- // Symmetry
1124
- // ═══════════════════════════════════════════════════
1125
- document.querySelectorAll('.sym-btn').forEach(btn => {
1126
- btn.addEventListener('click', function() {
1127
- const key = this.dataset.sym;
1128
- symmetry[key] = !symmetry[key];
1129
- this.classList.toggle('active', symmetry[key]);
1130
- });
1131
- });
1132
-
1133
1057
  // ═══════════════════════════════════════════════════
1134
1058
  // Gradient bar rendering
1135
1059
  // ═══════════════════════════════════════════════════
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wavegrid/canvas",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "author": "Dan Lynch <pyramation@gmail.com>",
5
5
  "description": "Artist-facing creative canvas for painting with light on the 7×7 grid",
6
6
  "main": "index.js",
@@ -43,5 +43,5 @@
43
43
  "@types/ws": "^8.5.13",
44
44
  "makage": "^0.3.0"
45
45
  },
46
- "gitHead": "1fc162ccd34d4b7e3594d26ee20043fb24a14ec6"
46
+ "gitHead": "169d6edc27c6869d60729127a3b820ba60469240"
47
47
  }
package/ui.js CHANGED
@@ -242,19 +242,6 @@ function getCanvasHTML(numCannons = 49, gridColumns = 7) {
242
242
  display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--text2);
243
243
  }
244
244
 
245
- /* ─── Symmetry Tools ─── */
246
- .symmetry-tools {
247
- display: flex; gap: 6px;
248
- }
249
- .sym-btn {
250
- width: 48px; height: 48px; border-radius: 12px; font-size: 18px;
251
- display: flex; align-items: center; justify-content: center;
252
- background: var(--surface2); border: 1px solid var(--border);
253
- color: var(--text2); cursor: pointer; transition: all 0.2s;
254
- }
255
- .sym-btn:hover { border-color: var(--text2); }
256
- .sym-btn.active { border-color: var(--accent); color: var(--accent); background: rgba(74,124,255,0.1); }
257
-
258
245
  /* ─── Drops Mode ─── */
259
246
  .drops-controls {
260
247
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
@@ -335,13 +322,12 @@ function getCanvasHTML(numCannons = 49, gridColumns = 7) {
335
322
  <div class="mode-tabs" id="mode-tabs">
336
323
  <div class="mode-tab active" data-mode="paint">Paint</div>
337
324
  <div class="mode-tab" data-mode="gradient">Gradient</div>
338
- <div class="mode-tab" data-mode="brush">Brush</div>
339
325
  <div class="mode-tab" data-mode="energy">Energy</div>
340
326
  <div class="mode-tab" data-mode="scenes">Scenes</div>
341
327
  <div class="mode-tab" data-mode="animations">Animations</div>
342
328
  <div class="mode-tab" data-mode="motion">Motion</div>
343
329
  <div class="mode-tab" data-mode="drops">Drops</div>
344
- <div class="mode-tab" data-mode="symmetry">Symmetry</div>
330
+
345
331
  </div>
346
332
  <div class="tool-area">
347
333
 
@@ -356,6 +342,16 @@ function getCanvasHTML(numCannons = 49, gridColumns = 7) {
356
342
  <div class="brightness-thumb" id="bright-thumb" style="bottom:80%"></div>
357
343
  </div>
358
344
  <div class="color-preview" id="color-preview" style="background:#4a7cff"></div>
345
+ <div class="brush-controls" style="margin-left:8px">
346
+ <div class="brush-size-wrap">
347
+ <div class="brush-preview">
348
+ <div class="brush-dot" id="brush-dot" style="width:20px;height:20px"></div>
349
+ </div>
350
+ <span class="brush-label">Size</span>
351
+ <input type="range" class="brush-slider" id="brush-size" min="1" max="5" value="1">
352
+ </div>
353
+ <div class="toggle-pill" id="brush-falloff">Soft edge</div>
354
+ </div>
359
355
  </div>
360
356
  </div>
361
357
 
@@ -369,26 +365,6 @@ function getCanvasHTML(numCannons = 49, gridColumns = 7) {
369
365
  </div>
370
366
  </div>
371
367
 
372
- <!-- Brush Mode -->
373
- <div class="tool-panel" id="panel-brush">
374
- <div class="color-section">
375
- <div id="brush-color-wheel-wrap" style="position:relative;width:80px;height:80px;flex-shrink:0">
376
- <canvas id="brush-color-wheel" width="80" height="80" style="width:80px;height:80px;border-radius:50%;cursor:crosshair;touch-action:none"></canvas>
377
- <div id="brush-wheel-cursor" style="position:absolute;width:12px;height:12px;border:2px solid #fff;border-radius:50%;pointer-events:none;transform:translate(-50%,-50%);box-shadow:0 0 6px rgba(0,0,0,0.5);left:40px;top:40px"></div>
378
- </div>
379
- <div class="brush-controls">
380
- <div class="brush-size-wrap">
381
- <div class="brush-preview">
382
- <div class="brush-dot" id="brush-dot" style="width:20px;height:20px"></div>
383
- </div>
384
- <span class="brush-label">Size</span>
385
- <input type="range" class="brush-slider" id="brush-size" min="1" max="5" value="1">
386
- </div>
387
- <div class="toggle-pill" id="brush-falloff">Soft edge</div>
388
- </div>
389
- </div>
390
- </div>
391
-
392
368
  <!-- Energy Mode -->
393
369
  <div class="tool-panel" id="panel-energy">
394
370
  <div style="flex:1;display:flex;flex-direction:column;align-items:center;gap:8px">
@@ -448,15 +424,7 @@ function getCanvasHTML(numCannons = 49, gridColumns = 7) {
448
424
  </div>
449
425
  </div>
450
426
 
451
- <!-- Symmetry -->
452
- <div class="tool-panel" id="panel-symmetry">
453
- <div class="symmetry-tools">
454
- <div class="sym-btn" data-sym="h" title="Mirror left/right">↔</div>
455
- <div class="sym-btn" data-sym="v" title="Mirror top/bottom">↕</div>
456
- <div class="sym-btn" data-sym="radial" title="Radial symmetry">✦</div>
457
- <div class="sym-btn" data-sym="kaleidoscope" title="Kaleidoscope">❋</div>
458
- </div>
459
- </div>
427
+
460
428
  </div>
461
429
  </div>
462
430
  </div>
@@ -477,7 +445,7 @@ let brushSize = 1;
477
445
  let brushFalloff = false;
478
446
  let activeScene = 'civic';
479
447
  let activeAnimation = null;
480
- let symmetry = { h: false, v: false, radial: false, kaleidoscope: false };
448
+
481
449
 
482
450
  // Motion painter state
483
451
  let motionPath = [];
@@ -652,7 +620,7 @@ function getAffectedCannons(centerIdx) {
652
620
  const result = [{ idx: centerIdx, falloff: 1 }];
653
621
 
654
622
  // Brush size > 1: include neighbors
655
- if (brushSize > 1 && (currentMode === 'brush' || currentMode === 'paint')) {
623
+ if (brushSize > 1 && currentMode === 'paint') {
656
624
  const reach = brushSize - 1;
657
625
  for (let dr = -reach; dr <= reach; dr++) {
658
626
  for (let dc = -reach; dc <= reach; dc++) {
@@ -667,34 +635,9 @@ function getAffectedCannons(centerIdx) {
667
635
  }
668
636
  }
669
637
 
670
- // Symmetry: mirror all affected cannons
671
- const mirrored = [];
672
- for (const { idx, falloff } of result) {
673
- mirrored.push({ idx, falloff });
674
- const r = Math.floor(idx / GRID), c = idx % GRID;
675
- if (symmetry.h) mirrored.push({ idx: r * GRID + (GRID - 1 - c), falloff });
676
- if (symmetry.v) mirrored.push({ idx: (GRID - 1 - r) * GRID + c, falloff });
677
- if (symmetry.h && symmetry.v) mirrored.push({ idx: (GRID - 1 - r) * GRID + (GRID - 1 - c), falloff });
678
- if (symmetry.radial) {
679
- // 4-fold rotational
680
- mirrored.push({ idx: c * GRID + (GRID - 1 - r), falloff });
681
- mirrored.push({ idx: (GRID - 1 - c) * GRID + r, falloff });
682
- }
683
- if (symmetry.kaleidoscope) {
684
- // 8-fold
685
- mirrored.push({ idx: r * GRID + (GRID - 1 - c), falloff });
686
- mirrored.push({ idx: (GRID - 1 - r) * GRID + c, falloff });
687
- mirrored.push({ idx: (GRID - 1 - r) * GRID + (GRID - 1 - c), falloff });
688
- mirrored.push({ idx: c * GRID + r, falloff });
689
- mirrored.push({ idx: c * GRID + (GRID - 1 - r), falloff });
690
- mirrored.push({ idx: (GRID - 1 - c) * GRID + r, falloff });
691
- mirrored.push({ idx: (GRID - 1 - c) * GRID + (GRID - 1 - r), falloff });
692
- }
693
- }
694
-
695
638
  // Deduplicate
696
639
  const seen = new Set();
697
- return mirrored.filter(m => {
640
+ return result.filter(m => {
698
641
  if (m.idx < 0 || m.idx >= NUM || seen.has(m.idx)) return false;
699
642
  seen.add(m.idx);
700
643
  return true;
@@ -762,7 +705,7 @@ function handleSculptureStart(e) {
762
705
  return;
763
706
  }
764
707
 
765
- if (idx >= 0 && (currentMode === 'paint' || currentMode === 'brush')) {
708
+ if (idx >= 0 && currentMode === 'paint') {
766
709
  const affected = getAffectedCannons(idx);
767
710
  affected.forEach(a => paintCannon(a.idx, a.falloff));
768
711
  lastPaintedIdx = idx;
@@ -812,7 +755,7 @@ function handleSculptureMove(e) {
812
755
  return;
813
756
  }
814
757
 
815
- if (currentMode === 'paint' || currentMode === 'brush') {
758
+ if (currentMode === 'paint') {
816
759
  const affected = getAffectedCannons(idx);
817
760
  affected.forEach(a => paintCannon(a.idx, a.falloff));
818
761
  lastPaintedIdx = idx;
@@ -899,14 +842,6 @@ setupColorWheel('color-wheel', 'wheel-cursor', (h, s) => {
899
842
  updateColorPreview();
900
843
  });
901
844
 
902
- setupColorWheel('brush-color-wheel', 'brush-wheel-cursor', (h, s) => {
903
- currentHue = h;
904
- currentSat = s;
905
- updateColorPreview();
906
- const dot = document.getElementById('brush-dot');
907
- dot.style.background = hsl(h, s, 50);
908
- });
909
-
910
845
  // Brightness bar
911
846
  function setupBrightnessBar() {
912
847
  const bar = document.getElementById('bright-bar');
@@ -1122,17 +1057,6 @@ function playMotionStep() {
1122
1057
  motionTimer = setTimeout(playMotionStep, 300 - speed * 25);
1123
1058
  }
1124
1059
 
1125
- // ═══════════════════════════════════════════════════
1126
- // Symmetry
1127
- // ═══════════════════════════════════════════════════
1128
- document.querySelectorAll('.sym-btn').forEach(btn => {
1129
- btn.addEventListener('click', function() {
1130
- const key = this.dataset.sym;
1131
- symmetry[key] = !symmetry[key];
1132
- this.classList.toggle('active', symmetry[key]);
1133
- });
1134
- });
1135
-
1136
1060
  // ═══════════════════════════════════════════════════
1137
1061
  // Gradient bar rendering
1138
1062
  // ═══════════════════════════════════════════════════