EgeEken commited on
Commit
edf34c9
·
verified ·
1 Parent(s): 996597c

Upload app.js

Browse files
Files changed (1) hide show
  1. static/app.js +34 -7
static/app.js CHANGED
@@ -25,6 +25,7 @@ const CLICK_T = 2; // … over 2 seconds
25
  const STROKES_PER_FRAME = 18;
26
  const CYCLE_MS = 5000;
27
  const N = 5; // pairs in the ring (3 visible)
 
28
 
29
  const SLOTS = [
30
  { y: 0, op: 1, scale: 1 }, // r0 mid · active
@@ -41,6 +42,10 @@ let mouse = { down: false, x: 0, y: 0, target: null, startSize: GLOBAL_START };
41
  const expSize = (s0, s1, t, T) => Math.max(1, Math.round(s0 * Math.pow(s1 / s0, Math.min(t, T) / T)));
42
  const globalSizeNow = () => expSize(GLOBAL_START, GLOBAL_END, (performance.now() - activeStart) / 1000, GLOBAL_T);
43
 
 
 
 
 
44
  function makePair() {
45
  const el = document.createElement("div");
46
  el.className = "pair";
@@ -51,7 +56,9 @@ function makePair() {
51
  const ctx = cv.getContext("2d");
52
  ctx.fillStyle = "#161618";
53
  ctx.fillRect(0, 0, CANVAS, CANVAS);
54
- return { el, img: el.querySelector("img"), canvas: cv, ctx, srcData: null };
 
 
55
  }
56
 
57
  function proceduralFallback(p, seed) {
@@ -80,6 +87,7 @@ function loadSource(p) {
80
  const seed = srcCursor++;
81
  p.ctx.fillStyle = "#161618";
82
  p.ctx.fillRect(0, 0, CANVAS, CANVAS);
 
83
  const im = new Image();
84
  im.crossOrigin = "anonymous";
85
  im.onload = () => {
@@ -119,19 +127,38 @@ function tick() {
119
  activeStart = performance.now();
120
  }
121
 
 
 
122
  function fillStroke(p, x, y, size) {
123
  x = Math.max(0, Math.min(CANVAS - size, Math.round(x)));
124
  y = Math.max(0, Math.min(CANVAS - size, Math.round(y)));
125
- const data = p.srcData;
126
- if (!data) return;
127
- let r = 0, g = 0, b = 0, n = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  for (let yy = y; yy < y + size; yy++) {
129
  for (let xx = x; xx < x + size; xx++) {
130
- const i = (yy * CANVAS + xx) * 4;
131
- r += data[i]; g += data[i + 1]; b += data[i + 2]; n++;
132
  }
133
  }
134
- p.ctx.fillStyle = `rgb(${r / n | 0},${g / n | 0},${b / n | 0})`;
135
  p.ctx.fillRect(x, y, size, size);
136
  }
137
 
 
25
  const STROKES_PER_FRAME = 18;
26
  const CYCLE_MS = 5000;
27
  const N = 5; // pairs in the ring (3 visible)
28
+ const BG = [22, 22, 24]; // canvas seed color (#161618)
29
 
30
  const SLOTS = [
31
  { y: 0, op: 1, scale: 1 }, // r0 mid · active
 
42
  const expSize = (s0, s1, t, T) => Math.max(1, Math.round(s0 * Math.pow(s1 / s0, Math.min(t, T) / T)));
43
  const globalSizeNow = () => expSize(GLOBAL_START, GLOBAL_END, (performance.now() - activeStart) / 1000, GLOBAL_T);
44
 
45
+ function resetBuf(p) {
46
+ for (let i = 0; i < p.buf.length; i += 3) { p.buf[i] = BG[0]; p.buf[i + 1] = BG[1]; p.buf[i + 2] = BG[2]; }
47
+ }
48
+
49
  function makePair() {
50
  const el = document.createElement("div");
51
  el.className = "pair";
 
56
  const ctx = cv.getContext("2d");
57
  ctx.fillStyle = "#161618";
58
  ctx.fillRect(0, 0, CANVAS, CANVAS);
59
+ const p = { el, img: el.querySelector("img"), canvas: cv, ctx, srcData: null, buf: new Uint8ClampedArray(CANVAS * CANVAS * 3) };
60
+ resetBuf(p);
61
+ return p;
62
  }
63
 
64
  function proceduralFallback(p, seed) {
 
87
  const seed = srcCursor++;
88
  p.ctx.fillStyle = "#161618";
89
  p.ctx.fillRect(0, 0, CANVAS, CANVAS);
90
+ resetBuf(p);
91
  const im = new Image();
92
  im.crossOrigin = "anonymous";
93
  im.onload = () => {
 
127
  activeStart = performance.now();
128
  }
129
 
130
+ // Places a stroke only if the source-area average lowers the error over that area
131
+ // (just like PBC discarding a stroke whose best multiplier is 0).
132
  function fillStroke(p, x, y, size) {
133
  x = Math.max(0, Math.min(CANVAS - size, Math.round(x)));
134
  y = Math.max(0, Math.min(CANVAS - size, Math.round(y)));
135
+ const src = p.srcData, buf = p.buf;
136
+ if (!src) return;
137
+
138
+ let sR = 0, sG = 0, sB = 0, sq = 0, before = 0;
139
+ for (let yy = y; yy < y + size; yy++) {
140
+ for (let xx = x; xx < x + size; xx++) {
141
+ const si = (yy * CANVAS + xx) * 4, bi = (yy * CANVAS + xx) * 3;
142
+ const r = src[si], g = src[si + 1], b = src[si + 2];
143
+ const cr = buf[bi], cg = buf[bi + 1], cb = buf[bi + 2];
144
+ sR += r; sG += g; sB += b;
145
+ sq += r * r + g * g + b * b;
146
+ before += (r - cr) * (r - cr) + (g - cg) * (g - cg) + (b - cb) * (b - cb);
147
+ }
148
+ }
149
+ const n = size * size;
150
+ // error after filling with the area average == variance of the source area
151
+ const after = sq - (sR * sR + sG * sG + sB * sB) / n;
152
+ if (after >= before) return; // would not improve → skip
153
+
154
+ const mr = Math.round(sR / n), mg = Math.round(sG / n), mb = Math.round(sB / n);
155
  for (let yy = y; yy < y + size; yy++) {
156
  for (let xx = x; xx < x + size; xx++) {
157
+ const bi = (yy * CANVAS + xx) * 3;
158
+ buf[bi] = mr; buf[bi + 1] = mg; buf[bi + 2] = mb;
159
  }
160
  }
161
+ p.ctx.fillStyle = `rgb(${mr},${mg},${mb})`;
162
  p.ctx.fillRect(x, y, size, size);
163
  }
164