Created
June 19, 2025 02:33
-
-
Save jordangarcia/eab9eaadb692f0300890fdd3604dab38 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>80x80 Pixel Weight Physics Simulation</title> | |
<style> | |
body { | |
margin: 0; | |
padding: 20px; | |
background: #1a1a1a; | |
color: white; | |
font-family: 'Courier New', monospace; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
min-height: 100vh; | |
} | |
.container { | |
background: #2a2a2a; | |
border-radius: 15px; | |
padding: 30px; | |
box-shadow: 0 10px 30px rgba(0,0,0,0.5); | |
max-width: 900px; | |
width: 100%; | |
} | |
.title { | |
text-align: center; | |
font-size: 24px; | |
margin-bottom: 20px; | |
color: #4CAF50; | |
} | |
.input-section { | |
background: #3a3a3a; | |
padding: 20px; | |
border-radius: 10px; | |
margin-bottom: 20px; | |
} | |
.pixel-grid { | |
display: grid; | |
grid-template-columns: repeat(80, 4px); | |
grid-template-rows: repeat(80, 4px); | |
gap: 0.5px; | |
background: #666; | |
padding: 5px; | |
border-radius: 5px; | |
margin: 10px auto; | |
width: fit-content; | |
max-height: 400px; | |
overflow: auto; | |
} | |
.pixel { | |
width: 4px; | |
height: 4px; | |
background: #000; | |
cursor: pointer; | |
transition: background 0.1s; | |
} | |
.pixel.light { | |
background: #fff; | |
} | |
.simulation-area { | |
position: relative; | |
height: 400px; | |
background: linear-gradient(135deg, #333, #555); | |
border-radius: 10px; | |
overflow: hidden; | |
margin: 20px 0; | |
border: 2px solid #666; | |
} | |
.ground { | |
position: absolute; | |
bottom: 0; | |
left: 0; | |
right: 0; | |
height: 20px; | |
background: #8B4513; | |
border-top: 2px solid #A0522D; | |
} | |
.fulcrum { | |
position: absolute; | |
bottom: 20px; | |
left: 50%; | |
transform: translateX(-50%); | |
width: 0; | |
height: 0; | |
border-left: 20px solid transparent; | |
border-right: 20px solid transparent; | |
border-bottom: 30px solid #666; | |
} | |
.balance-beam { | |
position: absolute; | |
bottom: 50px; | |
left: 50%; | |
width: 300px; | |
height: 8px; | |
background: #888; | |
transform-origin: center; | |
transform: translateX(-50%); | |
border-radius: 4px; | |
transition: transform 0.3s ease; | |
} | |
.image-container { | |
position: absolute; | |
bottom: 58px; | |
left: 50%; | |
transform: translateX(-50%); | |
width: 128px; | |
height: 128px; | |
background: #f0f0f0; | |
border-radius: 15px; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
transition: transform 0.3s ease; | |
border: 2px solid #ccc; | |
padding: 10px; | |
} | |
.preview-grid { | |
display: grid; | |
grid-template-columns: repeat(80, 1.5px); | |
grid-template-rows: repeat(80, 1.5px); | |
gap: 0; | |
width: 120px; | |
height: 120px; | |
} | |
.preview-pixel { | |
width: 1.5px; | |
height: 1.5px; | |
background: #000; | |
} | |
.preview-pixel.light { | |
background: #fff; | |
} | |
.controls { | |
display: flex; | |
flex-wrap: wrap; | |
gap: 10px; | |
justify-content: center; | |
margin: 20px 0; | |
} | |
.btn { | |
background: #4CAF50; | |
color: white; | |
border: none; | |
padding: 10px 20px; | |
border-radius: 5px; | |
cursor: pointer; | |
font-size: 14px; | |
transition: background 0.3s; | |
} | |
.btn:hover { | |
background: #45a049; | |
} | |
.btn.secondary { | |
background: #666; | |
} | |
.btn.secondary:hover { | |
background: #777; | |
} | |
.stats { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); | |
gap: 15px; | |
margin: 20px 0; | |
} | |
.stat-box { | |
background: #3a3a3a; | |
padding: 15px; | |
border-radius: 8px; | |
border-left: 4px solid #4CAF50; | |
} | |
.stat-label { | |
font-size: 12px; | |
color: #aaa; | |
margin-bottom: 5px; | |
} | |
.stat-value { | |
font-size: 18px; | |
font-weight: bold; | |
color: #4CAF50; | |
} | |
.result { | |
font-size: 18px; | |
font-weight: bold; | |
text-align: center; | |
padding: 15px; | |
border-radius: 8px; | |
margin: 10px 0; | |
} | |
.left-heavy { background: #ff4444; } | |
.right-heavy { background: #4444ff; } | |
.balanced { background: #44ff44; color: black; } | |
.json-status { | |
padding: 10px; | |
border-radius: 5px; | |
margin-top: 10px; | |
font-size: 14px; | |
} | |
.json-success { | |
background: #2d5a2d; | |
color: #90ee90; | |
border: 1px solid #4CAF50; | |
} | |
.json-error { | |
background: #5a2d2d; | |
color: #ff9090; | |
border: 1px solid #ff4444; | |
} | |
.json-examples { | |
background: #3a3a3a; | |
padding: 15px; | |
border-radius: 5px; | |
margin-top: 10px; | |
font-family: monospace; | |
font-size: 12px; | |
max-height: 200px; | |
overflow-y: auto; | |
} | |
.weight-visualization { | |
display: grid; | |
grid-template-columns: 1fr 1fr; | |
gap: 20px; | |
margin: 20px 0; | |
} | |
.weight-side { | |
background: #3a3a3a; | |
padding: 15px; | |
border-radius: 8px; | |
text-align: center; | |
} | |
.weight-bar { | |
height: 20px; | |
background: #666; | |
border-radius: 10px; | |
margin: 10px 0; | |
overflow: hidden; | |
} | |
.weight-fill { | |
height: 100%; | |
transition: width 0.3s ease; | |
} | |
.left-fill { background: #ff4444; } | |
.right-fill { background: #4444ff; } | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<div class="title">π― 80Γ80 Pixel Weight Physics Simulation</div> | |
<div class="input-section"> | |
<h3>π¨ Pixel Editor</h3> | |
<p>Click pixels to toggle between black (1g) and white (0g). Create any pattern!</p> | |
<div class="pixel-grid" id="pixelGrid"></div> | |
<div class="controls"> | |
<button class="btn secondary" onclick="clearGrid()">ποΈ Clear All</button> | |
<button class="btn secondary" onclick="shiftLeft()">β¬ οΈ Shift Left</button> | |
<button class="btn secondary" onclick="shiftRight()">β‘οΈ Shift Right</button> | |
</div> | |
<div style="margin-top: 20px;"> | |
<h4>π JSON Array Input</h4> | |
<p>Paste a 2D JSON array: 80 rows Γ 80 columns. Format: [[0,1,0,...],[1,0,1,...],...] (0=black/1g, 1=white/0g)</p> | |
<textarea id="jsonInput" placeholder="[[0,0,0,1,0,0,0,0,0,0,1,1,1,0,0,0,...],[0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,...],...]" | |
style="width: 100%; height: 120px; background: #4a4a4a; color: white; border: 1px solid #666; border-radius: 5px; padding: 10px; font-family: monospace; font-size: 12px; resize: vertical;"></textarea> | |
<div style="display: flex; gap: 10px; margin-top: 10px;"> | |
<button class="btn" onclick="loadFromJSON()">π₯ Load from JSON</button> | |
<button class="btn secondary" onclick="exportToJSON()">π€ Export to JSON</button> | |
</div> | |
<div id="jsonStatus" style="margin-top: 10px; padding: 10px; border-radius: 5px; display: none;"></div> | |
</div> | |
</div> | |
<div class="simulation-area"> | |
<div class="ground"></div> | |
<div class="fulcrum"></div> | |
<div class="balance-beam" id="balanceBeam"></div> | |
<div class="image-container" id="imageContainer"> | |
<div class="preview-grid" id="previewGrid"></div> | |
</div> | |
</div> | |
<div class="controls"> | |
<button class="btn" onclick="startSimulation()">π― Run Physics Simulation</button> | |
<button class="btn secondary" onclick="resetSimulation()">π Reset Physics</button> | |
</div> | |
<div class="weight-visualization"> | |
<div class="weight-side"> | |
<h4>β¬ οΈ LEFT SIDE</h4> | |
<div class="weight-bar"> | |
<div class="weight-fill left-fill" id="leftBar"></div> | |
</div> | |
<div id="leftWeight">0g</div> | |
</div> | |
<div class="weight-side"> | |
<h4>RIGHT SIDE β‘οΈ</h4> | |
<div class="weight-bar"> | |
<div class="weight-fill right-fill" id="rightBar"></div> | |
</div> | |
<div id="rightWeight">0g</div> | |
</div> | |
</div> | |
<div class="stats"> | |
<div class="stat-box"> | |
<div class="stat-label">TOTAL BLACK PIXELS</div> | |
<div class="stat-value" id="blackPixels">6400</div> | |
</div> | |
<div class="stat-box"> | |
<div class="stat-label">TOTAL WHITE PIXELS</div> | |
<div class="stat-value" id="whitePixels">0</div> | |
</div> | |
<div class="stat-box"> | |
<div class="stat-label">TOTAL WEIGHT</div> | |
<div class="stat-value" id="totalWeight">6400g</div> | |
</div> | |
<div class="stat-box"> | |
<div class="stat-label">BALANCE DIFFERENCE</div> | |
<div class="stat-value" id="weightDiff">0g</div> | |
</div> | |
<div class="stat-box"> | |
<div class="stat-label">CENTER OF MASS</div> | |
<div class="stat-value" id="centerOfMass">40.0</div> | |
</div> | |
</div> | |
<div id="result" class="result" style="display: none;"></div> | |
<div class="analysis"> | |
<h3>π¬ Physics Analysis</h3> | |
<p><strong>Simulation Method:</strong> Each pixel in the 32Γ32 grid represents weight. The simulation calculates the center of mass and determines tipping direction.</p> | |
<p><strong>Weight System:</strong></p> | |
<ul> | |
<li>Dark pixels = 1 gram each</li> | |
<li>Light pixels = 0.1 gram each</li> | |
<li>Total possible weight: 5760g (all dark) to 640g (all light)</li> | |
<li>Center line divides at pixel column 40</li> | |
</ul> | |
<p><strong>Physics Calculation:</strong></p> | |
<ul> | |
<li>Weight Γ Distance from center = Torque</li> | |
<li>Left torque vs Right torque determines tipping</li> | |
<li>Center of mass calculated as weighted average position</li> | |
</ul> | |
</div> | |
</div> | |
<script> | |
// 80x80 pixel array (0 = black/1g, 1 = white/0g) | |
let pixelArray = new Array(80).fill(0).map(() => new Array(80).fill(0)); | |
let simulationData = { | |
leftWeight: 0, | |
rightWeight: 0, | |
totalWeight: 0, | |
centerOfMass: 40, | |
isRunning: false | |
}; | |
function initializeGrid() { | |
const grid = document.getElementById('pixelGrid'); | |
const previewGrid = document.getElementById('previewGrid'); | |
grid.innerHTML = ''; | |
previewGrid.innerHTML = ''; | |
for (let row = 0; row < 80; row++) { | |
for (let col = 0; col < 80; col++) { | |
// Main grid pixel | |
const pixel = document.createElement('div'); | |
pixel.className = 'pixel'; | |
pixel.dataset.row = row; | |
pixel.dataset.col = col; | |
pixel.onclick = () => togglePixel(row, col); | |
grid.appendChild(pixel); | |
// Preview grid pixel | |
const previewPixel = document.createElement('div'); | |
previewPixel.className = 'preview-pixel'; | |
previewPixel.dataset.row = row; | |
previewPixel.dataset.col = col; | |
previewGrid.appendChild(previewPixel); | |
} | |
} | |
updateDisplay(); | |
} | |
function togglePixel(row, col) { | |
pixelArray[row][col] = pixelArray[row][col] === 0 ? 1 : 0; | |
updatePixelDisplay(); | |
calculateWeightDistribution(); | |
updateDisplay(); | |
} | |
function updatePixelDisplay() { | |
const pixels = document.querySelectorAll('.pixel'); | |
const previewPixels = document.querySelectorAll('.preview-pixel'); | |
pixels.forEach(pixel => { | |
const row = parseInt(pixel.dataset.row); | |
const col = parseInt(pixel.dataset.col); | |
pixel.className = pixelArray[row][col] === 1 ? 'pixel light' : 'pixel'; | |
}); | |
previewPixels.forEach(pixel => { | |
const row = parseInt(pixel.dataset.row); | |
const col = parseInt(pixel.dataset.col); | |
pixel.className = pixelArray[row][col] === 1 ? 'preview-pixel light' : 'preview-pixel'; | |
}); | |
} | |
function calculateWeightDistribution() { | |
let leftWeight = 0; | |
let rightWeight = 0; | |
let totalMoment = 0; | |
let totalWeight = 0; | |
let blackPixels = 0; | |
let whitePixels = 0; | |
for (let row = 0; row < 80; row++) { | |
for (let col = 0; col < 80; col++) { | |
const pixelWeight = pixelArray[row][col] === 0 ? 1.0 : 0.0; // 0=black=1g, 1=white=0g | |
const distanceFromCenter = col - 39.5; // Center is at 39.5 | |
totalWeight += pixelWeight; | |
totalMoment += pixelWeight * col; | |
if (pixelArray[row][col] === 0) { | |
blackPixels++; | |
} else { | |
whitePixels++; | |
} | |
if (col < 40) { | |
leftWeight += pixelWeight; | |
} else { | |
rightWeight += pixelWeight; | |
} | |
} | |
} | |
simulationData.leftWeight = leftWeight; | |
simulationData.rightWeight = rightWeight; | |
simulationData.totalWeight = totalWeight; | |
simulationData.centerOfMass = totalWeight > 0 ? totalMoment / totalWeight : 40; | |
simulationData.blackPixels = blackPixels; | |
simulationData.whitePixels = whitePixels; | |
return simulationData; | |
} | |
function updateDisplay() { | |
const data = simulationData; | |
document.getElementById('leftWeight').textContent = data.leftWeight.toFixed(1) + 'g'; | |
document.getElementById('rightWeight').textContent = data.rightWeight.toFixed(1) + 'g'; | |
document.getElementById('totalWeight').textContent = data.totalWeight.toFixed(1) + 'g'; | |
document.getElementById('weightDiff').textContent = Math.abs(data.rightWeight - data.leftWeight).toFixed(1) + 'g'; | |
document.getElementById('centerOfMass').textContent = data.centerOfMass.toFixed(1); | |
document.getElementById('blackPixels').textContent = data.blackPixels || 0; | |
document.getElementById('whitePixels').textContent = data.whitePixels || 0; | |
// Update weight bars | |
const maxWeight = Math.max(data.leftWeight, data.rightWeight); | |
const leftPercent = maxWeight > 0 ? (data.leftWeight / maxWeight) * 100 : 0; | |
const rightPercent = maxWeight > 0 ? (data.rightWeight / maxWeight) * 100 : 0; | |
document.getElementById('leftBar').style.width = leftPercent + '%'; | |
document.getElementById('rightBar').style.width = rightPercent + '%'; | |
} | |
function startSimulation() { | |
if (simulationData.isRunning) return; | |
calculateWeightDistribution(); | |
simulationData.isRunning = true; | |
const data = simulationData; | |
const weightDiff = data.rightWeight - data.leftWeight; | |
const centerOffset = data.centerOfMass - 40; | |
// Calculate tilt angle based on center of mass offset | |
const maxTilt = 20; // degrees | |
const tiltAngle = centerOffset * 2; // Amplify for visibility | |
const balanceBeam = document.getElementById('balanceBeam'); | |
const imageContainer = document.getElementById('imageContainer'); | |
balanceBeam.style.transform = `translateX(-50%) rotate(${tiltAngle}deg)`; | |
imageContainer.style.transform = `translateX(-50%) rotate(${tiltAngle}deg)`; | |
// Show result | |
const resultDiv = document.getElementById('result'); | |
resultDiv.style.display = 'block'; | |
if (Math.abs(centerOffset) < 0.5) { | |
resultDiv.textContent = 'βοΈ PERFECTLY BALANCED! Center of mass is at the center.'; | |
resultDiv.className = 'result balanced'; | |
} else if (centerOffset < 0) { | |
resultDiv.textContent = `β¬ οΈ TIPS LEFT! Center of mass at ${data.centerOfMass.toFixed(1)} (${Math.abs(centerOffset).toFixed(1)} pixels left of center)`; | |
resultDiv.className = 'result left-heavy'; | |
} else { | |
resultDiv.textContent = `β‘οΈ TIPS RIGHT! Center of mass at ${data.centerOfMass.toFixed(1)} (${centerOffset.toFixed(1)} pixels right of center)`; | |
resultDiv.className = 'result right-heavy'; | |
} | |
setTimeout(() => { | |
simulationData.isRunning = false; | |
}, 500); | |
} | |
function resetSimulation() { | |
document.getElementById('balanceBeam').style.transform = 'translateX(-50%) rotate(0deg)'; | |
document.getElementById('imageContainer').style.transform = 'translateX(-50%) rotate(0deg)'; | |
document.getElementById('result').style.display = 'none'; | |
} | |
function shiftLeft() { | |
const newPixelArray = new Array(80).fill(0).map(() => new Array(80).fill(0)); | |
for (let row = 0; row < 80; row++) { | |
for (let col = 0; col < 80; col++) { | |
if (col < 79) { | |
// Shift everything left by 1, fill rightmost column with white (1) | |
newPixelArray[row][col] = pixelArray[row][col + 1]; | |
} else { | |
// Fill the rightmost column with white (1) - weightless | |
newPixelArray[row][col] = 1; | |
} | |
} | |
} | |
pixelArray = newPixelArray; | |
updatePixelDisplay(); | |
calculateWeightDistribution(); | |
updateDisplay(); | |
resetSimulation(); | |
} | |
function shiftRight() { | |
const newPixelArray = new Array(80).fill(0).map(() => new Array(80).fill(0)); | |
for (let row = 0; row < 80; row++) { | |
for (let col = 0; col < 80; col++) { | |
if (col > 0) { | |
// Shift everything right by 1, fill leftmost column with white (1) | |
newPixelArray[row][col] = pixelArray[row][col - 1]; | |
} else { | |
// Fill the leftmost column with white (1) - weightless | |
newPixelArray[row][col] = 1; | |
} | |
} | |
} | |
pixelArray = newPixelArray; | |
updatePixelDisplay(); | |
calculateWeightDistribution(); | |
updateDisplay(); | |
resetSimulation(); | |
} | |
function clearGrid() { | |
pixelArray = new Array(80).fill(0).map(() => new Array(80).fill(0)); | |
updatePixelDisplay(); | |
calculateWeightDistribution(); | |
updateDisplay(); | |
resetSimulation(); | |
} | |
function fillRandom() { | |
for (let row = 0; row < 80; row++) { | |
for (let col = 0; col < 80; col++) { | |
pixelArray[row][col] = Math.random() < 0.3 ? 1 : 0; | |
} | |
} | |
updatePixelDisplay(); | |
calculateWeightDistribution(); | |
updateDisplay(); | |
resetSimulation(); | |
} | |
function loadSampleG() { | |
clearGrid(); | |
// Create a larger G pattern for 80x80 grid (black pixels = weight) | |
const gPattern = [ | |
// Horizontal top line | |
[20,20],[20,21],[20,22],[20,23],[20,24],[20,25],[20,26],[20,27],[20,28],[20,29],[20,30],[20,31],[20,32],[20,33],[20,34],[20,35],[20,36],[20,37],[20,38],[20,39], | |
// Left vertical line | |
[21,20],[22,20],[23,20],[24,20],[25,20],[26,20],[27,20],[28,20],[29,20],[30,20],[31,20],[32,20],[33,20],[34,20],[35,20],[36,20],[37,20],[38,20],[39,20],[40,20], | |
[41,20],[42,20],[43,20],[44,20],[45,20],[46,20],[47,20],[48,20],[49,20],[50,20],[51,20],[52,20],[53,20],[54,20],[55,20],[56,20],[57,20],[58,20],[59,20], | |
// Middle horizontal line | |
[40,20],[40,21],[40,22],[40,23],[40,24],[40,25],[40,26],[40,27],[40,28],[40,29],[40,30],[40,31],[40,32],[40,33],[40,34], | |
// Right vertical line (middle to bottom) | |
[41,34],[42,34],[43,34],[44,34],[45,34],[46,34],[47,34],[48,34],[49,34],[50,34],[51,34],[52,34],[53,34],[54,34],[55,34],[56,34],[57,34],[58,34],[59,34], | |
// Bottom horizontal line | |
[59,20],[59,21],[59,22],[59,23],[59,24],[59,25],[59,26],[59,27],[59,28],[59,29],[59,30],[59,31],[59,32],[59,33],[59,34],[59,35],[59,36],[59,37],[59,38],[59,39] | |
]; | |
// Start with all white (weightless) | |
for (let row = 0; row < 80; row++) { | |
for (let col = 0; col < 80; col++) { | |
pixelArray[row][col] = 1; // 1 = white = 0g | |
} | |
} | |
// Add black G pattern (weight) | |
gPattern.forEach(([row, col]) => { | |
if (row < 80 && col < 80) { | |
pixelArray[row][col] = 0; // 0 = black = 1g | |
} | |
}); | |
// Add a larger dot below | |
for (let row = 65; row < 70; row++) { | |
for (let col = 27; col < 32; col++) { | |
pixelArray[row][col] = 0; // 0 = black = 1g | |
} | |
} | |
updatePixelDisplay(); | |
calculateWeightDistribution(); | |
updateDisplay(); | |
resetSimulation(); | |
} | |
function loadSamplePattern() { | |
clearGrid(); | |
// Start with all white (weightless) | |
for (let row = 0; row < 80; row++) { | |
for (let col = 0; col < 80; col++) { | |
pixelArray[row][col] = 1; // 1 = white = 0g | |
} | |
} | |
// Create an interesting asymmetric pattern for 80x80 (add black weight) | |
for (let row = 10; row < 70; row++) { | |
for (let col = 10; col < 70; col++) { | |
if ((row + col) % 5 === 0 && col > 40) { | |
pixelArray[row][col] = 0; // 0 = black = 1g | |
} | |
if (row === 40 && col < 30) { | |
pixelArray[row][col] = 0; // 0 = black = 1g | |
} | |
if (col === 20 && row > 50) { | |
pixelArray[row][col] = 0; // 0 = black = 1g | |
} | |
} | |
} | |
updatePixelDisplay(); | |
calculateWeightDistribution(); | |
updateDisplay(); | |
resetSimulation(); | |
} | |
function loadFromJSON() { | |
const jsonInput = document.getElementById('jsonInput'); | |
const statusDiv = document.getElementById('jsonStatus'); | |
try { | |
const input = jsonInput.value.trim(); | |
if (!input) { | |
showStatus('Please enter a JSON array', 'error'); | |
return; | |
} | |
let pixelData = JSON.parse(input); | |
// Validate the data - should be 2D array | |
if (!Array.isArray(pixelData)) { | |
showStatus('Input must be an array', 'error'); | |
return; | |
} | |
if (pixelData.length !== 80) { | |
showStatus(`Array must have exactly 80 rows. Found ${pixelData.length} rows.`, 'error'); | |
return; | |
} | |
// Validate each row | |
for (let row = 0; row < 80; row++) { | |
if (!Array.isArray(pixelData[row])) { | |
showStatus(`Row ${row} must be an array`, 'error'); | |
return; | |
} | |
if (pixelData[row].length !== 80) { | |
showStatus(`Row ${row} must have exactly 80 elements. Found ${pixelData[row].length} elements.`, 'error'); | |
return; | |
} | |
// Validate each element in this row | |
for (let col = 0; col < 80; col++) { | |
if (pixelData[row][col] !== 0 && pixelData[row][col] !== 1) { | |
showStatus(`Element at row ${row}, column ${col} must be 0 or 1. Found: ${pixelData[row][col]}`, 'error'); | |
return; | |
} | |
} | |
} | |
// Data is valid, update the pixel array | |
pixelArray = pixelData; | |
updatePixelDisplay(); | |
calculateWeightDistribution(); | |
updateDisplay(); | |
resetSimulation(); | |
showStatus('β Successfully loaded 2D pixel pattern! (0=black/1g, 1=white/0g)', 'success'); | |
} catch (error) { | |
showStatus(`JSON Parse Error: ${error.message}`, 'error'); | |
} | |
} | |
function exportToJSON() { | |
// Export the 2D array directly | |
const jsonString = JSON.stringify(pixelArray, null, 2); | |
document.getElementById('jsonInput').value = jsonString; | |
showStatus('β Current pattern exported as 2D JSON array (0=black/1g, 1=white/0g)', 'success'); | |
} | |
function showJSONExamples() { | |
const statusDiv = document.getElementById('jsonStatus'); | |
// Create example patterns as 2D arrays | |
const emptyPattern = new Array(80).fill(0).map(() => new Array(80).fill(0)); | |
const solidPattern = new Array(80).fill(0).map(() => new Array(80).fill(1)); | |
const checkerboard = new Array(80).fill(0).map((_, row) => | |
new Array(80).fill(0).map((_, col) => (row + col) % 2) | |
); | |
const verticalSplit = new Array(80).fill(0).map(() => | |
new Array(80).fill(0).map((_, col) => col < 40 ? 1 : 0) | |
); | |
const horizontalSplit = new Array(80).fill(0).map((_, row) => | |
new Array(80).fill(0).map(() => row < 40 ? 1 : 0) | |
); | |
const examples = ` | |
<strong>π 2D JSON Array Examples:</strong> | |
<strong>1. All Light (empty pattern):</strong> | |
${JSON.stringify(emptyPattern).substring(0, 100)}... | |
<strong>2. All Dark (solid pattern):</strong> | |
${JSON.stringify(solidPattern).substring(0, 100)}... | |
<strong>3. Checkerboard Pattern:</strong> | |
${JSON.stringify(checkerboard).substring(0, 100)}... | |
<strong>4. Vertical Split (left dark, right light):</strong> | |
${JSON.stringify(verticalSplit).substring(0, 100)}... | |
<strong>5. Horizontal Split (top dark, bottom light):</strong> | |
${JSON.stringify(horizontalSplit).substring(0, 100)}... | |
<strong>6. Simple Cross Pattern:</strong> | |
${JSON.stringify(new Array(80).fill(0).map((_, row) => | |
new Array(80).fill(0).map((_, col) => (row === 40 || col === 40) ? 1 : 0) | |
)).substring(0, 100)}... | |
<strong>Format:</strong> Array of 80 arrays, each containing 80 values (0 or 1) | |
<strong>Structure:</strong> [row0, row1, row2, ..., row79] | |
<strong>Each row:</strong> [col0, col1, col2, ..., col79] | |
<strong>Example structure:</strong> | |
[ | |
[0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...], | |
[0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,...], | |
... | |
] | |
`; | |
statusDiv.innerHTML = examples; | |
statusDiv.className = 'json-status json-examples'; | |
statusDiv.style.display = 'block'; | |
} | |
function showStatus(message, type) { | |
const statusDiv = document.getElementById('jsonStatus'); | |
statusDiv.textContent = message; | |
statusDiv.className = `json-status json-${type}`; | |
statusDiv.style.display = 'block'; | |
if (type === 'success') { | |
setTimeout(() => { | |
statusDiv.style.display = 'none'; | |
}, 3000); | |
} | |
} | |
async function loadDefaultData() { | |
try { | |
showStatus('π Loading default pattern...', 'success'); | |
const response = await fetch('https://gist.githubusercontent.com/jordangarcia/1d7827bd1bca863080acc708740acd92/raw/1ca27386382ce51599aad5f65e235a88773c6480/logo.json'); | |
if (!response.ok) { | |
throw new Error(`HTTP error! status: ${response.status}`); | |
} | |
const data = await response.json(); | |
// Validate the data structure | |
if (!Array.isArray(data)) { | |
throw new Error('Data must be an array'); | |
} | |
if (data.length !== 80) { | |
throw new Error(`Array must have exactly 80 rows. Found ${data.length} rows.`); | |
} | |
// Validate each row | |
for (let row = 0; row < 80; row++) { | |
if (!Array.isArray(data[row])) { | |
throw new Error(`Row ${row} must be an array`); | |
} | |
if (data[row].length !== 80) { | |
throw new Error(`Row ${row} must have exactly 80 elements. Found ${data[row].length} elements.`); | |
} | |
// Validate each element in this row | |
for (let col = 0; col < 80; col++) { | |
if (data[row][col] !== 0 && data[row][col] !== 1) { | |
throw new Error(`Element at row ${row}, column ${col} must be 0 or 1. Found: ${data[row][col]}`); | |
} | |
} | |
} | |
// Data is valid, update the pixel array | |
pixelArray = data; | |
updatePixelDisplay(); | |
calculateWeightDistribution(); | |
updateDisplay(); | |
resetSimulation(); | |
showStatus('β Default pattern loaded successfully!', 'success'); | |
} catch (error) { | |
console.error('Error loading default data:', error); | |
showStatus(`β Error loading default pattern: ${error.message}`, 'error'); | |
// Fall back to empty grid | |
calculateWeightDistribution(); | |
updateDisplay(); | |
} | |
} | |
// Initialize the application | |
initializeGrid(); | |
loadDefaultData(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment