EnovinxSchool commited on
Commit
a6a3528
·
verified ·
1 Parent(s): 2ba7ab9

undefined - Initial Deployment

Browse files
Files changed (3) hide show
  1. README.md +5 -3
  2. index.html +679 -19
  3. prompts.txt +0 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Wave Collapse Function Not Designed By Me
3
- emoji: 🦀
4
  colorFrom: blue
5
  colorTo: yellow
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: wave-collapse-function-not-designed-by-me
3
+ emoji: 🐳
4
  colorFrom: blue
5
  colorTo: yellow
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,679 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Wave Function Collapse (WFC) Demo</title>
7
+ <style>
8
+ body {
9
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
10
+ display: flex;
11
+ flex-direction: column;
12
+ align-items: center;
13
+ background-color: #f0f2f5;
14
+ color: #333;
15
+ margin: 0;
16
+ padding: 1rem;
17
+ }
18
+ h1 {
19
+ color: #111;
20
+ }
21
+ .main-container {
22
+ display: flex;
23
+ flex-wrap: wrap;
24
+ gap: 2rem;
25
+ justify-content: center;
26
+ width: 100%;
27
+ max-width: 1200px;
28
+ }
29
+ .canvas-container {
30
+ display: flex;
31
+ flex-direction: column;
32
+ align-items: center;
33
+ padding: 1rem;
34
+ background: #fff;
35
+ border-radius: 8px;
36
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
37
+ }
38
+ canvas {
39
+ border: 1px solid #ccc;
40
+ image-rendering: pixelated;
41
+ image-rendering: -moz-crisp-edges;
42
+ image-rendering: crisp-edges;
43
+ background-color: #e9e9e9;
44
+ }
45
+ h2 {
46
+ margin-top: 0;
47
+ margin-bottom: 1rem;
48
+ color: #444;
49
+ }
50
+ .controls {
51
+ padding: 1rem;
52
+ background: #fff;
53
+ border-radius: 8px;
54
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
55
+ display: flex;
56
+ flex-direction: column;
57
+ gap: 1rem;
58
+ min-width: 250px;
59
+ }
60
+ .control-group {
61
+ display: flex;
62
+ flex-direction: column;
63
+ gap: 0.5rem;
64
+ }
65
+ .control-group label {
66
+ font-weight: bold;
67
+ }
68
+ .control-group input[type="number"], .control-group input[type="checkbox"] {
69
+ padding: 0.5rem;
70
+ border: 1px solid #ccc;
71
+ border-radius: 4px;
72
+ }
73
+ .control-group input[type="checkbox"] {
74
+ width: 20px;
75
+ height: 20px;
76
+ }
77
+ .checkbox-label {
78
+ display: flex;
79
+ align-items: center;
80
+ gap: 0.5rem;
81
+ }
82
+ .palette {
83
+ display: flex;
84
+ gap: 0.5rem;
85
+ flex-wrap: wrap;
86
+ align-items: center;
87
+ }
88
+ .color-swatch {
89
+ width: 30px;
90
+ height: 30px;
91
+ border: 2px solid #ccc;
92
+ border-radius: 4px;
93
+ cursor: pointer;
94
+ transition: transform 0.1s;
95
+ }
96
+ .color-swatch.selected {
97
+ border-color: #007bff;
98
+ transform: scale(1.1);
99
+ box-shadow: 0 0 5px #007bff;
100
+ }
101
+ input[type="color"] {
102
+ width: 40px;
103
+ height: 40px;
104
+ border: none;
105
+ padding: 0;
106
+ background: none;
107
+ cursor: pointer;
108
+ }
109
+ button {
110
+ padding: 0.75rem 1rem;
111
+ border: none;
112
+ border-radius: 4px;
113
+ cursor: pointer;
114
+ font-size: 1rem;
115
+ font-weight: bold;
116
+ transition: background-color 0.2s;
117
+ }
118
+ .btn-generate {
119
+ background-color: #28a745;
120
+ color: white;
121
+ }
122
+ .btn-generate:hover {
123
+ background-color: #218838;
124
+ }
125
+ .btn-clear {
126
+ background-color: #dc3545;
127
+ color: white;
128
+ }
129
+ .btn-clear:hover {
130
+ background-color: #c82333;
131
+ }
132
+ #status {
133
+ margin-top: 1rem;
134
+ font-weight: bold;
135
+ font-size: 1.1rem;
136
+ min-height: 1.5em;
137
+ }
138
+ </style>
139
+ </head>
140
+ <body>
141
+
142
+ <h1>Wave Function Collapse</h1>
143
+ <p>Draw on the left canvas, set your parameters, and click "Generate"!</p>
144
+
145
+ <div class="main-container">
146
+ <div class="controls">
147
+ <h2>Controls</h2>
148
+ <div class="control-group">
149
+ <label for="n-size">Pattern Size (N)</label>
150
+ <input type="number" id="n-size" value="3" min="2" max="5">
151
+ </div>
152
+ <div class="control-group">
153
+ <label for="output-width">Output Width</label>
154
+ <input type="number" id="output-width" value="48" min="10" max="256">
155
+ </div>
156
+ <div class="control-group">
157
+ <label for="output-height">Output Height</label>
158
+ <input type="number" id="output-height" value="48" min="10" max="256">
159
+ </div>
160
+ <div class="control-group">
161
+ <label class="checkbox-label">
162
+ <input type="checkbox" id="augment" checked>
163
+ Use Augmentations (Rotations/Reflections)
164
+ </label>
165
+ </div>
166
+ <div class="control-group">
167
+ <label class="checkbox-label">
168
+ <input type="checkbox" id="visualize">
169
+ Visualize Generation
170
+ </label>
171
+ </div>
172
+ <div class="control-group">
173
+ <label>Color Palette</label>
174
+ <div class="palette">
175
+ <div id="palette-container"></div>
176
+ <input type="color" id="color-picker" value="#ff0000">
177
+ </div>
178
+ </div>
179
+ <button class="btn-generate" id="generate-btn">Generate</button>
180
+ <button class="btn-clear" id="clear-btn">Clear Input</button>
181
+ <div id="status">Ready.</div>
182
+ </div>
183
+
184
+ <div class="canvas-container">
185
+ <h2>Input Canvas</h2>
186
+ <canvas id="input-canvas" width="200" height="200"></canvas>
187
+ </div>
188
+
189
+ <div class="canvas-container">
190
+ <h2>Output Canvas</h2>
191
+ <canvas id="output-canvas" width="480" height="480"></canvas>
192
+ </div>
193
+ </div>
194
+
195
+ <script>
196
+ document.addEventListener('DOMContentLoaded', () => {
197
+ // --- DOM ELEMENTS ---
198
+ const inputCanvas = document.getElementById('input-canvas');
199
+ const inputCtx = inputCanvas.getContext('2d');
200
+ const outputCanvas = document.getElementById('output-canvas');
201
+ const outputCtx = outputCanvas.getContext('2d');
202
+ const nSizeInput = document.getElementById('n-size');
203
+ const outputWidthInput = document.getElementById('output-width');
204
+ const outputHeightInput = document.getElementById('output-height');
205
+ const augmentCheckbox = document.getElementById('augment');
206
+ const visualizeCheckbox = document.getElementById('visualize');
207
+ const generateBtn = document.getElementById('generate-btn');
208
+ const clearBtn = document.getElementById('clear-btn');
209
+ const statusDiv = document.getElementById('status');
210
+ const paletteContainer = document.getElementById('palette-container');
211
+ const colorPicker = document.getElementById('color-picker');
212
+
213
+ // --- DRAWING STATE ---
214
+ const INPUT_DIM = 20; // 20x20 grid for input
215
+ const PIXEL_SIZE = inputCanvas.width / INPUT_DIM;
216
+ let isDrawing = false;
217
+ let colors = ['#ffffff', '#000000', '#007bff', '#28a745'];
218
+ let currentColorIndex = 1;
219
+ let inputGrid = Array.from({ length: INPUT_DIM }, () => Array(INPUT_DIM).fill(0));
220
+
221
+ // --- HELPER FUNCTIONS ---
222
+ const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
223
+
224
+ // --- PALETTE & DRAWING LOGIC ---
225
+ function updatePalette() {
226
+ paletteContainer.innerHTML = '';
227
+ colors.forEach((color, index) => {
228
+ const swatch = document.createElement('div');
229
+ swatch.className = 'color-swatch';
230
+ swatch.style.backgroundColor = color;
231
+ if (index === currentColorIndex) {
232
+ swatch.classList.add('selected');
233
+ }
234
+ swatch.addEventListener('click', () => {
235
+ currentColorIndex = index;
236
+ updatePalette();
237
+ });
238
+ paletteContainer.appendChild(swatch);
239
+ });
240
+ }
241
+
242
+ colorPicker.addEventListener('change', (e) => {
243
+ const newColor = e.target.value;
244
+ if (!colors.includes(newColor)) {
245
+ colors.push(newColor);
246
+ currentColorIndex = colors.length - 1;
247
+ updatePalette();
248
+ }
249
+ });
250
+
251
+ function drawInputGrid() {
252
+ inputCtx.clearRect(0, 0, inputCanvas.width, inputCanvas.height);
253
+ for (let y = 0; y < INPUT_DIM; y++) {
254
+ for (let x = 0; x < INPUT_DIM; x++) {
255
+ inputCtx.fillStyle = colors[inputGrid[y][x]];
256
+ inputCtx.fillRect(x * PIXEL_SIZE, y * PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE);
257
+ }
258
+ }
259
+ // Draw grid lines for clarity
260
+ inputCtx.strokeStyle = '#ddd';
261
+ inputCtx.lineWidth = 0.5;
262
+ for (let i = 0; i <= INPUT_DIM; i++) {
263
+ inputCtx.beginPath();
264
+ inputCtx.moveTo(i * PIXEL_SIZE, 0);
265
+ inputCtx.lineTo(i * PIXEL_SIZE, inputCanvas.height);
266
+ inputCtx.stroke();
267
+ inputCtx.beginPath();
268
+ inputCtx.moveTo(0, i * PIXEL_SIZE);
269
+ inputCtx.lineTo(inputCanvas.width, i * PIXEL_SIZE);
270
+ inputCtx.stroke();
271
+ }
272
+ }
273
+
274
+ function handleDraw(event) {
275
+ if (!isDrawing) return;
276
+ const rect = inputCanvas.getBoundingClientRect();
277
+ const x = Math.floor((event.clientX - rect.left) / PIXEL_SIZE);
278
+ const y = Math.floor((event.clientY - rect.top) / PIXEL_SIZE);
279
+ if (x >= 0 && x < INPUT_DIM && y >= 0 && y < INPUT_DIM) {
280
+ if (inputGrid[y][x] !== currentColorIndex) {
281
+ inputGrid[y][x] = currentColorIndex;
282
+ drawInputGrid();
283
+ }
284
+ }
285
+ }
286
+
287
+ inputCanvas.addEventListener('mousedown', (e) => { isDrawing = true; handleDraw(e); });
288
+ inputCanvas.addEventListener('mouseup', () => { isDrawing = false; });
289
+ inputCanvas.addEventListener('mouseleave', () => { isDrawing = false; });
290
+ inputCanvas.addEventListener('mousemove', handleDraw);
291
+
292
+ clearBtn.addEventListener('click', () => {
293
+ inputGrid = Array.from({ length: INPUT_DIM }, () => Array(INPUT_DIM).fill(0));
294
+ drawInputGrid();
295
+ });
296
+
297
+ // Default pattern
298
+ function createDefaultPattern() {
299
+ inputGrid = Array.from({ length: INPUT_DIM }, () => Array(INPUT_DIM).fill(0));
300
+ // A simple cross pattern
301
+ for(let i = 5; i < 15; i++) {
302
+ inputGrid[10][i] = 1; // black line
303
+ inputGrid[i][10] = 2; // blue line
304
+ }
305
+ inputGrid[10][10] = 3; // green center
306
+ }
307
+
308
+
309
+ // --- WFC ALGORITHM ---
310
+
311
+ class WFC {
312
+ constructor(inputGrid, N, outputWidth, outputHeight, useAugmentations, visualizeCallback) {
313
+ this.N = N;
314
+ this.outputWidth = outputWidth;
315
+ this.outputHeight = outputHeight;
316
+ this.useAugmentations = useAugmentations;
317
+ this.visualizeCallback = visualizeCallback;
318
+
319
+ this.patterns = [];
320
+ this.patternWeights = {};
321
+
322
+ this.parseInput(inputGrid);
323
+ this.buildAdjacency();
324
+
325
+ // The "wave" is the main grid for the output
326
+ this.wave = [];
327
+ // Track entropy for quick lookup
328
+ this.entropy = [];
329
+ this.allPatternIndices = Array.from({ length: this.patterns.length }, (_, i) => i);
330
+
331
+ for (let y = 0; y < outputHeight; y++) {
332
+ this.wave[y] = [];
333
+ this.entropy[y] = [];
334
+ for (let x = 0; x < outputWidth; x++) {
335
+ // Each cell in the wave can initially be any pattern
336
+ this.wave[y][x] = [...this.allPatternIndices];
337
+ this.entropy[y][x] = this.patterns.length;
338
+ }
339
+ }
340
+ }
341
+
342
+ // 1. Read the input and count NxN patterns.
343
+ parseInput(grid) {
344
+ const patternMap = new Map();
345
+
346
+ for (let y = 0; y < grid.length - this.N + 1; y++) {
347
+ for (let x = 0; x < grid[0].length - this.N + 1; x++) {
348
+ const pattern = [];
349
+ for (let dy = 0; dy < this.N; dy++) {
350
+ pattern.push(...grid[y + dy].slice(x, x + this.N));
351
+ }
352
+
353
+ if (this.useAugmentations) {
354
+ const augmentations = this.getAugmentations(pattern);
355
+ augmentations.forEach(p => {
356
+ const hash = p.join(',');
357
+ patternMap.set(hash, (patternMap.get(hash) || 0) + 1);
358
+ });
359
+ } else {
360
+ const hash = pattern.join(',');
361
+ patternMap.set(hash, (patternMap.get(hash) || 0) + 1);
362
+ }
363
+ }
364
+ }
365
+
366
+ patternMap.forEach((weight, hash) => {
367
+ const pattern = hash.split(',').map(Number);
368
+ this.patterns.push(pattern);
369
+ this.patternWeights[this.patterns.length - 1] = weight;
370
+ });
371
+
372
+ if (this.patterns.length === 0) {
373
+ throw new Error("No patterns found. Try a larger or more varied input, or a smaller N.");
374
+ }
375
+ }
376
+
377
+ // (optional) Augment pattern data with rotations and reflections.
378
+ getAugmentations(pattern) {
379
+ const augmentations = new Set();
380
+ let current = pattern;
381
+ for (let i = 0; i < 4; i++) {
382
+ augmentations.add(current.join(','));
383
+ augmentations.add(this.reflect(current).join(','));
384
+ current = this.rotate(current);
385
+ }
386
+ return Array.from(augmentations).map(hash => hash.split(',').map(Number));
387
+ }
388
+
389
+ rotate(p) {
390
+ const newPattern = Array(this.N * this.N);
391
+ for (let y = 0; y < this.N; y++) {
392
+ for (let x = 0; x < this.N; x++) {
393
+ newPattern[x * this.N + (this.N - 1 - y)] = p[y * this.N + x];
394
+ }
395
+ }
396
+ return newPattern;
397
+ }
398
+
399
+ reflect(p) {
400
+ const newPattern = Array(this.N * this.N);
401
+ for (let y = 0; y < this.N; y++) {
402
+ for (let x = 0; x < this.N; x++) {
403
+ newPattern[y * this.N + (this.N - 1 - x)] = p[y * this.N + x];
404
+ }
405
+ }
406
+ return newPattern;
407
+ }
408
+
409
+ // Pre-calculate which patterns can be placed next to each other.
410
+ buildAdjacency() {
411
+ this.adjacency = {}; // { patternIndex: { dx, dy: [valid neighbor indices] } }
412
+ for (let i = 0; i < this.patterns.length; i++) {
413
+ this.adjacency[i] = {
414
+ '1,0': [], // Right
415
+ '-1,0': [], // Left
416
+ '0,1': [], // Down
417
+ '0,-1': [] // Up
418
+ };
419
+ }
420
+
421
+ for (let i = 0; i < this.patterns.length; i++) {
422
+ for (let j = 0; j < this.patterns.length; j++) {
423
+ const p1 = this.patterns[i];
424
+ const p2 = this.patterns[j];
425
+
426
+ // Check Right: p2 is to the right of p1
427
+ if (this.checkOverlap(p1, p2, 1, 0)) this.adjacency[i]['1,0'].push(j);
428
+ // Check Left: p2 is to the left of p1
429
+ if (this.checkOverlap(p1, p2, -1, 0)) this.adjacency[i]['-1,0'].push(j);
430
+ // Check Down: p2 is below p1
431
+ if (this.checkOverlap(p1, p2, 0, 1)) this.adjacency[i]['0,1'].push(j);
432
+ // Check Up: p2 is above p1
433
+ if (this.checkOverlap(p1, p2, 0, -1)) this.adjacency[i]['0,-1'].push(j);
434
+ }
435
+ }
436
+ }
437
+
438
+ checkOverlap(p1, p2, dx, dy) {
439
+ // Checks if p2 can be placed at offset (dx, dy) from p1
440
+ for (let y = 0; y < this.N; y++) {
441
+ for (let x = 0; x < this.N; x++) {
442
+ const nx = x - dx;
443
+ const ny = y - dy;
444
+ if (nx >= 0 && nx < this.N && ny >= 0 && ny < this.N) {
445
+ if (p1[y * this.N + x] !== p2[ny * this.N + nx]) {
446
+ return false;
447
+ }
448
+ }
449
+ }
450
+ }
451
+ return true;
452
+ }
453
+
454
+
455
+ // 4. Main Loop
456
+ async run() {
457
+ let iterations = this.outputWidth * this.outputHeight;
458
+ while(iterations > 0) {
459
+ const collapsedCoords = this.observe();
460
+ if (collapsedCoords === null) {
461
+ // Finished successfully
462
+ return this.generateOutput();
463
+ }
464
+
465
+ const contradiction = this.propagate(collapsedCoords);
466
+ if (contradiction) {
467
+ throw new Error("Contradiction reached. Cannot continue.");
468
+ }
469
+
470
+ iterations--;
471
+ if (this.visualizeCallback) {
472
+ await this.visualizeCallback(this.entropy, this.patterns.length);
473
+ await sleep(0); // Yield to the browser to render changes
474
+ }
475
+ }
476
+ // If loop finishes, it's also a success
477
+ return this.generateOutput();
478
+ }
479
+
480
+ // 4a. Observation: Find the cell with the lowest entropy and collapse it.
481
+ observe() {
482
+ let minEntropy = Infinity;
483
+ let minCoords = null;
484
+
485
+ // Find cell with minimum non-zero entropy
486
+ for (let y = 0; y < this.outputHeight; y++) {
487
+ for (let x = 0; x < this.outputWidth; x++) {
488
+ const ent = this.entropy[y][x];
489
+ if (ent > 1 && ent < minEntropy) {
490
+ minEntropy = ent;
491
+ minCoords = [{x, y}];
492
+ } else if (ent > 1 && ent === minEntropy) {
493
+ minCoords.push({x, y});
494
+ }
495
+ }
496
+ }
497
+
498
+ if (minCoords === null) {
499
+ return null; // All cells collapsed
500
+ }
501
+
502
+ // Break ties randomly
503
+ const choice = minCoords[Math.floor(Math.random() * minCoords.length)];
504
+ const {x, y} = choice;
505
+
506
+ // Collapse the chosen cell
507
+ const possiblePatterns = this.wave[y][x];
508
+ const totalWeight = possiblePatterns.reduce((sum, pIndex) => sum + this.patternWeights[pIndex], 0);
509
+ let rnd = Math.random() * totalWeight;
510
+
511
+ let chosenPattern;
512
+ for(const pIndex of possiblePatterns) {
513
+ rnd -= this.patternWeights[pIndex];
514
+ if(rnd <= 0) {
515
+ chosenPattern = pIndex;
516
+ break;
517
+ }
518
+ }
519
+
520
+ this.wave[y][x] = [chosenPattern];
521
+ this.entropy[y][x] = 1;
522
+
523
+ return {x, y};
524
+ }
525
+
526
+ // 4b. Propagation: Update neighbors based on the recent collapse.
527
+ propagate(startCoords) {
528
+ const stack = [startCoords];
529
+
530
+ while(stack.length > 0) {
531
+ const {x, y} = stack.pop();
532
+ const currentPatterns = this.wave[y][x];
533
+
534
+ // For each neighbor
535
+ for (const [dx, dy] of [[0,1], [0,-1], [1,0], [-1,0]]) {
536
+ const nx = x + dx;
537
+ const ny = y + dy;
538
+
539
+ if (nx < 0 || nx >= this.outputWidth || ny < 0 || ny >= this.outputHeight) continue;
540
+
541
+ let neighborPatterns = this.wave[ny][nx];
542
+ if(neighborPatterns.length <= 1) continue;
543
+
544
+ // Get all patterns that are valid neighbors in this direction
545
+ const validNeighboringPatterns = new Set();
546
+ const directionKey = `${-dx},${-dy}`; // from neighbor's perspective
547
+ for (const pIndex of currentPatterns) {
548
+ this.adjacency[pIndex][directionKey].forEach(validIndex => {
549
+ validNeighboringPatterns.add(validIndex);
550
+ });
551
+ }
552
+
553
+ // Filter neighbor's possibilities
554
+ const originalLength = neighborPatterns.length;
555
+ const newNeighborPatterns = neighborPatterns.filter(pIndex => validNeighboringPatterns.has(pIndex));
556
+
557
+ if (newNeighborPatterns.length === 0) {
558
+ return true; // Contradiction!
559
+ }
560
+
561
+ if (newNeighborPatterns.length < originalLength) {
562
+ this.wave[ny][nx] = newNeighborPatterns;
563
+ this.entropy[ny][nx] = newNeighborPatterns.length;
564
+ stack.push({x: nx, y: ny});
565
+ }
566
+ }
567
+ }
568
+ return false; // No contradiction
569
+ }
570
+
571
+ // 5. Create the final image from the collapsed wave.
572
+ generateOutput() {
573
+ const outputGrid = Array.from({ length: this.outputHeight }, () => Array(this.outputWidth));
574
+ for (let y = 0; y < this.outputHeight; y++) {
575
+ for (let x = 0; x < this.outputWidth; x++) {
576
+ if (this.wave[y][x].length > 0) {
577
+ const patternIndex = this.wave[y][x][0];
578
+ const pattern = this.patterns[patternIndex];
579
+ // Render the top-left pixel of the pattern at this position
580
+ outputGrid[y][x] = pattern[0];
581
+ } else {
582
+ outputGrid[y][x] = 0; // Should not happen in success case
583
+ }
584
+ }
585
+ }
586
+ return outputGrid;
587
+ }
588
+ }
589
+
590
+
591
+ // --- MAIN EXECUTION ---
592
+
593
+ async function handleGenerate() {
594
+ generateBtn.disabled = true;
595
+ statusDiv.textContent = 'Starting...';
596
+ statusDiv.style.color = '#333';
597
+
598
+ // Resize output canvas
599
+ const outputWidth = parseInt(outputWidthInput.value);
600
+ const outputHeight = parseInt(outputHeightInput.value);
601
+ outputCanvas.width = outputWidth * 10;
602
+ outputCanvas.height = outputHeight * 10;
603
+ outputCtx.clearRect(0, 0, outputCanvas.width, outputCanvas.height);
604
+
605
+
606
+ await sleep(10); // Allow UI to update
607
+
608
+ try {
609
+ statusDiv.textContent = '1. Parsing input patterns...';
610
+ const N = parseInt(nSizeInput.value);
611
+ const useAugmentations = augmentCheckbox.checked;
612
+ const visualize = visualizeCheckbox.checked;
613
+
614
+ let visualizeCallback = null;
615
+ if (visualize) {
616
+ visualizeCallback = (entropyGrid, maxEntropy) => {
617
+ const pixelW = outputCanvas.width / outputWidth;
618
+ const pixelH = outputCanvas.height / outputHeight;
619
+ outputCtx.clearRect(0, 0, outputCanvas.width, outputCanvas.height);
620
+ for(let y = 0; y < outputHeight; y++) {
621
+ for (let x = 0; x < outputWidth; x++) {
622
+ const entropy = entropyGrid[y][x];
623
+ if(entropy === 1) {
624
+ outputCtx.fillStyle = '#00ff00'; // Green for collapsed
625
+ } else {
626
+ const grayscale = Math.floor(255 * (1 - (entropy / maxEntropy)));
627
+ outputCtx.fillStyle = `rgb(${grayscale}, ${grayscale}, ${grayscale})`;
628
+ }
629
+ outputCtx.fillRect(x * pixelW, y * pixelH, pixelW, pixelH);
630
+ }
631
+ }
632
+ };
633
+ }
634
+
635
+ const wfc = new WFC(inputGrid, N, outputWidth, outputHeight, useAugmentations, visualizeCallback);
636
+
637
+ statusDiv.textContent = '2. Running WFC algorithm... (this may take a moment)';
638
+ await sleep(10);
639
+
640
+ const resultGrid = await wfc.run();
641
+
642
+ statusDiv.textContent = '3. Rendering output...';
643
+ await sleep(10);
644
+
645
+ // Draw final result
646
+ const pixelW = outputCanvas.width / outputWidth;
647
+ const pixelH = outputCanvas.height / outputHeight;
648
+ outputCtx.clearRect(0, 0, outputCanvas.width, outputCanvas.height);
649
+ for(let y = 0; y < outputHeight; y++) {
650
+ for (let x = 0; x < outputWidth; x++) {
651
+ const colorIndex = resultGrid[y][x];
652
+ outputCtx.fillStyle = colors[colorIndex] || '#ff00ff'; // Magenta for errors
653
+ outputCtx.fillRect(x * pixelW, y * pixelH, pixelW, pixelH);
654
+ }
655
+ }
656
+
657
+ statusDiv.textContent = 'Finished successfully!';
658
+ statusDiv.style.color = 'green';
659
+
660
+ } catch (error) {
661
+ console.error(error);
662
+ statusDiv.textContent = `Error: ${error.message}`;
663
+ statusDiv.style.color = 'red';
664
+ } finally {
665
+ generateBtn.disabled = false;
666
+ }
667
+ }
668
+
669
+ generateBtn.addEventListener('click', handleGenerate);
670
+
671
+ // Initial setup
672
+ createDefaultPattern();
673
+ updatePalette();
674
+ drawInputGrid();
675
+ });
676
+
677
+ </script>
678
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=EnovinxSchool/wave-collapse-function-not-designed-by-me" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
679
+ </html>
prompts.txt ADDED
File without changes