@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.
- package/esm/ui.js +17 -93
- package/package.json +2 -2
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 &&
|
|
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
|
|
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 &&
|
|
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'
|
|
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
|
+
"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": "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 &&
|
|
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
|
|
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 &&
|
|
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'
|
|
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
|
// ═══════════════════════════════════════════════════
|