Created
December 6, 2024 19:33
-
-
Save dmitriypereverza/b1ed19a699af9d720efcd7d6f3c8d76c 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>Bitmap Visualization</title> | |
| <style> | |
| body { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| } | |
| .canvas-container { | |
| display: flex; | |
| gap: 20px; | |
| } | |
| canvas { | |
| border: 1px solid black; | |
| cursor: crosshair; | |
| } | |
| #scaledCanvas { | |
| cursor: default; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Bitmap Visualization</h1> | |
| <p>Click on the detailed canvas (right) to set values in the bitmap. The scaled version (left) updates automatically. Use the "Clear" button to reset.</p> | |
| <div class="canvas-container"> | |
| <canvas id="scaledCanvas" width="400" height="400"></canvas> | |
| <canvas id="bitmapCanvas" width="400" height="400"></canvas> | |
| </div> | |
| <br> | |
| <button id="clearButton">Clear</button> | |
| <script type="module"> | |
| export class Bitmap { | |
| constructor(width, height, scale = 1) { | |
| this.width = width | |
| this.height = height | |
| this.scale = scale | |
| this.scaledWidth = Math.round(width * scale) | |
| this.scaledHeight = Math.round(height * scale) | |
| this.data = new Float64Array(this.scaledWidth * this.scaledHeight) | |
| } | |
| clean() { | |
| this.data.fill(0) | |
| } | |
| set(x, y, val) { | |
| const startY = Math.floor(y * this.scale) | |
| const endY = Math.ceil((y + 1) * this.scale) | |
| const startX = Math.floor(x * this.scale) | |
| const endX = Math.ceil((x + 1) * this.scale) | |
| for (let sy = startY; sy < endY; sy++) { | |
| for (let sx = startX; sx < endX; sx++) { | |
| this.data[sy * this.scaledWidth + sx] = Math.min(1, Math.max(0, val)) | |
| } | |
| } | |
| } | |
| setScaled(x, y, val) { | |
| const idx = Math.round(y) * this.scaledWidth + Math.round(x) | |
| if (idx >= 0 && idx < this.data.length) { | |
| this.data[idx] = Math.min(1, Math.max(0, val)) | |
| } | |
| } | |
| getScaled(x, y) { | |
| const idx = Math.round(y) * this.scaledWidth + Math.round(x) | |
| return idx >= 0 && idx < this.data.length ? this.data[idx] : 0 | |
| } | |
| get(x, y) { | |
| const sx = Math.floor(x * this.scale) | |
| const sy = Math.floor(y * this.scale) | |
| const idx = sy * this.scaledWidth + sx | |
| return idx >= 0 && idx < this.data.length ? this.data[idx] : 0 | |
| } | |
| getInPercents(px, py) { | |
| const x = Math.floor(px * this.scaledWidth) | |
| const y = Math.floor(py * this.scaledHeight) | |
| const idx = y * this.scaledWidth + x | |
| return idx >= 0 && idx < this.data.length ? this.data[idx] : 0 | |
| } | |
| } | |
| // Initialize Canvases and Bitmap | |
| /** @type {HTMLCanvasElement} */ | |
| const scaledCanvas = document.getElementById("scaledCanvas"); | |
| /** @type {HTMLCanvasElement} */ | |
| const detailedCanvas = document.getElementById("bitmapCanvas"); | |
| const scaledCtx = scaledCanvas.getContext("2d"); | |
| const detailedCtx = detailedCanvas.getContext("2d"); | |
| const bitmapSize = 20; | |
| const scale = 0.5; | |
| const localScale = 10; | |
| const bitmap = new Bitmap(bitmapSize, bitmapSize, scale); | |
| // Function to draw Bitmap | |
| function drawBitmap() { | |
| drawScaledBitmap(); | |
| drawDetailedBitmap(); | |
| } | |
| // Draw scaled version of the Bitmap with borders | |
| function drawScaledBitmap() { | |
| const width = bitmap.scaledWidth * localScale; | |
| const height = bitmap.scaledHeight * localScale; | |
| const cellWidth = scaledCanvas.width / width; | |
| const cellHeight = scaledCanvas.height / height; | |
| for (let y = 0; y < height; y++) { | |
| for (let x = 0; x < width; x++) { | |
| const px = x / width; | |
| const py = y / height; | |
| const value = bitmap.getInPercents(px, py); | |
| const color = Math.round(value * 255); | |
| scaledCtx.fillStyle = `rgb(${color}, ${color}, ${color})`; | |
| scaledCtx.fillRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight); | |
| // Draw border | |
| scaledCtx.strokeStyle = "white"; | |
| scaledCtx.strokeRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight); | |
| } | |
| } | |
| } | |
| // Draw detailed version of the Bitmap with borders | |
| function drawDetailedBitmap() { | |
| const cellWidth = detailedCanvas.width / bitmap.width; | |
| const cellHeight = detailedCanvas.height / bitmap.height; | |
| for (let y = 0; y < bitmap.height; y++) { | |
| for (let x = 0; x < bitmap.width; x++) { | |
| const value = bitmap.get(x, y); | |
| const color = Math.round(value * 255); | |
| detailedCtx.fillStyle = `rgb(${color}, ${color}, ${color})`; | |
| detailedCtx.fillRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight); | |
| // Draw border | |
| detailedCtx.strokeStyle = "white"; | |
| detailedCtx.strokeRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight); | |
| } | |
| } | |
| } | |
| function getMousePosition(event, canvas) { | |
| const rect = canvas.getBoundingClientRect(); | |
| const x = Math.floor((event.clientX - rect.left) / (canvas.width / bitmap.width)); | |
| const y = Math.floor((event.clientY - rect.top) / (canvas.height / bitmap.height)); | |
| return { x, y }; | |
| } | |
| // Event: Set value on click | |
| detailedCanvas.addEventListener("click", (event) => { | |
| const { x, y } = getMousePosition(event, detailedCanvas); | |
| bitmap.set(x, y, bitmap.get(x, y) ? 0 : 1); // Set maximum brightness | |
| drawBitmap(); | |
| }); | |
| // Clear button | |
| document.getElementById("clearButton").addEventListener("click", () => { | |
| bitmap.clean(); | |
| drawBitmap(); | |
| }); | |
| // Initial render | |
| drawBitmap(); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment