Skip to content

Commit bedb8aa

Browse files
authored
Add CNN implementation in cnn2.js
1 parent 68d7d87 commit bedb8aa

1 file changed

Lines changed: 305 additions & 0 deletions

File tree

cnn/cnn2.js

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
let iterations = 9;
2+
let pad = 1;
3+
4+
let core = [];
5+
let fractalRaw = [];
6+
let fractalNorm = [];
7+
8+
let kernelParams = { a: 0, b: 0, c: 0, d: 0, e: 0 };
9+
10+
// ---------- Math ----------
11+
12+
function activation(t) {
13+
return t;
14+
}
15+
16+
function convolution(array, core9) {
17+
const n = array.length;
18+
const temp = new Array(n);
19+
20+
for (let x = 0; x < n; x++) {
21+
const xm = (x - 1 + n) % n;
22+
const xp = (x + 1) % n;
23+
temp[x] = new Array(n);
24+
25+
for (let y = 0; y < n; y++) {
26+
const ym = (y - 1 + n) % n;
27+
const yp = (y + 1) % n;
28+
29+
temp[x][y] =
30+
array[xm][ym] * core9[0] + array[x][ym] * core9[1] + array[xp][ym] * core9[2] +
31+
array[xm][y ] * core9[3] + array[x][y ] * core9[4] + array[xp][y ] * core9[5] +
32+
array[xm][yp] * core9[6] + array[x][yp] * core9[7] + array[xp][yp] * core9[8];
33+
}
34+
}
35+
return temp;
36+
}
37+
38+
function padding(array, k) {
39+
if (array.length === 1) return [[1, 0], [0, 0]];
40+
41+
const n = array.length;
42+
const temp = new Array(n * 2);
43+
44+
for (let x = 0; x < n; x++) {
45+
temp[x * 2] = new Array(n * 2);
46+
temp[x * 2 + 1] = new Array(n * 2);
47+
48+
for (let y = 0; y < n; y++) {
49+
const v = k * array[x][y];
50+
temp[x * 2][y * 2] = array[x][y];
51+
temp[x * 2][y * 2 + 1] = v;
52+
temp[x * 2 + 1][y * 2] = v;
53+
temp[x * 2 + 1][y * 2 + 1] = v;
54+
}
55+
}
56+
return temp;
57+
}
58+
59+
// ---------- Normalization ----------
60+
61+
function normalizeContrast(array) {
62+
const n = array.length;
63+
const flat = [];
64+
65+
for (let x = 0; x < n; x++)
66+
for (let y = 0; y < n; y++)
67+
flat.push(array[x][y]);
68+
69+
flat.sort((a, b) => a - b);
70+
71+
const lo = flat[Math.floor(flat.length * 0.01)];
72+
const hi = flat[Math.floor(flat.length * 0.99)];
73+
74+
const out = new Array(n);
75+
for (let x = 0; x < n; x++) {
76+
out[x] = new Array(n);
77+
for (let y = 0; y < n; y++) {
78+
const t = (array[x][y] - lo) / (hi - lo + 1e-9);
79+
out[x][y] = Math.max(0, Math.min(1, t));
80+
}
81+
}
82+
return out;
83+
}
84+
85+
// ---------- Squeeze ----------
86+
87+
function squeezeValue(v, mod) {
88+
const r = Math.round(v);
89+
return ((r % mod) + mod) % mod;
90+
}
91+
92+
function squeezeField(array, mode) {
93+
if (mode === "none") return array;
94+
95+
let m = 4;
96+
if (mode === "mod2") m = 2;
97+
if (mode === "mod4") m = 4;
98+
if (mode === "mod8") m = 8;
99+
if (mode === "mod16") m = 16;
100+
101+
const n = array.length;
102+
const out = new Array(n);
103+
104+
for (let x = 0; x < n; x++) {
105+
out[x] = new Array(n);
106+
for (let y = 0; y < n; y++)
107+
out[x][y] = squeezeValue(array[x][y], m);
108+
}
109+
return out;
110+
}
111+
112+
// ---------- Kernel ----------
113+
114+
function buildKernelFromParams() {
115+
const { a, b, c, d, e } = kernelParams;
116+
return [
117+
a, b, c,
118+
d, e, d,
119+
c, b, a
120+
];
121+
}
122+
123+
// ---------- UI helpers ----------
124+
125+
function isIntegerCore() {
126+
return document.getElementById("integerCore").checked;
127+
}
128+
129+
function getSqueezeMode() {
130+
return document.getElementById("squeezeMode").value;
131+
}
132+
133+
function getIterations() {
134+
return parseInt(document.getElementById("iterations").value, 10);
135+
}
136+
137+
function getCoreRange() {
138+
return parseInt(document.getElementById("coreRange").value, 10);
139+
}
140+
141+
// ---------- Slider sync (9 sliders) ----------
142+
143+
const sliderMap = [
144+
["k0","a"], ["k8","a"],
145+
["k1","b"], ["k7","b"],
146+
["k2","c"], ["k6","c"],
147+
["k3","d"], ["k5","d"],
148+
["k4","e"]
149+
];
150+
151+
function setSliderConfig() {
152+
const R = getCoreRange();
153+
const intCore = isIntegerCore();
154+
155+
sliderMap.forEach(([id]) => {
156+
const s = document.getElementById(id);
157+
if (intCore) {
158+
s.min = -R;
159+
s.max = R;
160+
s.step = 1;
161+
} else {
162+
s.min = -1;
163+
s.max = 1;
164+
s.step = 0.001;
165+
}
166+
});
167+
}
168+
169+
function syncSlidersFromParams() {
170+
sliderMap.forEach(([id, k]) => {
171+
document.getElementById(id).value = kernelParams[k];
172+
});
173+
}
174+
175+
// ---------- Core regeneration (ORIGINAL FUNCTION) ----------
176+
177+
function regenerateFieldKeepCore() {
178+
iterations = getIterations();
179+
core = buildKernelFromParams();
180+
181+
let array = [[1]];
182+
183+
for (let i = 0; i < iterations; i++) {
184+
array = padding(array, pad);
185+
array = convolution(array, core);
186+
array = squeezeField(array, getSqueezeMode());
187+
}
188+
189+
fractalRaw = array;
190+
fractalNorm = normalizeContrast(array);
191+
192+
drawFractal();
193+
dumpKernelLine();
194+
}
195+
196+
// ---------- Rendering ----------
197+
198+
function drawFractal() {
199+
const canvas = document.getElementById("myCanvas");
200+
const px = parseInt(document.getElementById("pixelSize").value, 10);
201+
202+
const n = fractalRaw.length;
203+
canvas.width = n * px;
204+
canvas.height = n * px;
205+
206+
const ctx = canvas.getContext("2d");
207+
const mode = document.getElementById("colorMode").value;
208+
209+
for (let x = 0; x < n; x++) {
210+
for (let y = 0; y < n; y++) {
211+
const raw = fractalRaw[x][y];
212+
const v = fractalNorm[x][y];
213+
214+
let color = "#000";
215+
216+
if (mode === "grayscale") {
217+
const g = Math.floor(v * 255);
218+
color = `rgb(${g},${g},${g})`;
219+
}
220+
else if (mode === "mod4") {
221+
const s = Math.round(raw);
222+
color = (s % 4 === 0 || s % 4 === 1) ? "#000" : "#fff";
223+
}
224+
else if (mode === "alleycat") {
225+
const s = Math.round(raw);
226+
const p = ["#000", "#5ff", "#f5f", "#fff"];
227+
color = p[((s % 4) + 4) % 4];
228+
}
229+
230+
ctx.fillStyle = color;
231+
ctx.fillRect(x * px, y * px, px, px);
232+
}
233+
}
234+
}
235+
236+
function dumpKernelLine() {
237+
const n = fractalRaw.length;
238+
const kern = "[" + core.join(",") + "]";
239+
document.getElementById("kernelDump").textContent =
240+
`iter=${iterations} size=${n}×${n} kernel=${kern}`;
241+
}
242+
243+
// ---------- UI wiring ----------
244+
245+
function initUI() {
246+
setSliderConfig();
247+
248+
sliderMap.forEach(([id, k]) => {
249+
document.getElementById(id).oninput = e => {
250+
let v = parseFloat(e.target.value);
251+
if (isIntegerCore()) v = Math.round(v);
252+
kernelParams[k] = v;
253+
syncSlidersFromParams();
254+
regenerateFieldKeepCore();
255+
};
256+
});
257+
258+
document.getElementById("integerCore").onchange = () => {
259+
setSliderConfig();
260+
syncSlidersFromParams();
261+
regenerateFieldKeepCore();
262+
};
263+
264+
document.getElementById("coreRange").oninput = () => {
265+
setSliderConfig();
266+
regenerateFieldKeepCore();
267+
};
268+
269+
document.getElementById("iterations").oninput = regenerateFieldKeepCore;
270+
document.getElementById("pixelSize").oninput = drawFractal;
271+
document.getElementById("colorMode").onchange = drawFractal;
272+
document.getElementById("squeezeMode").onchange = regenerateFieldKeepCore;
273+
document.getElementById("refreshBtn").onclick = refreshSeed;
274+
}
275+
276+
// ---------- Seed ----------
277+
278+
function refreshSeed() {
279+
const R = getCoreRange();
280+
const intCore = isIntegerCore();
281+
282+
function rf() { return Math.random() * 2 - 1; }
283+
function ri() { return Math.floor(Math.random() * (2 * R + 1)) - R; }
284+
285+
kernelParams = {
286+
a: intCore ? ri() : rf(),
287+
b: intCore ? ri() : rf(),
288+
c: intCore ? ri() : rf(),
289+
d: intCore ? ri() : rf(),
290+
e: intCore ? ri() : rf()
291+
};
292+
293+
syncSlidersFromParams();
294+
regenerateFieldKeepCore();
295+
}
296+
297+
document.addEventListener('DOMContentLoaded', () => {
298+
document.querySelectorAll('input[name="padmode"]').forEach(r => {
299+
r.addEventListener('change', () => {
300+
pad = Number(r.value);
301+
regenerateFieldKeepCore();
302+
console.log('pad changed to', pad);
303+
});
304+
});
305+
});

0 commit comments

Comments
 (0)