Mercurial
view mrjunejune/src/public/dog.js @ 207:58d9b64d8dca
Updated deployment script to include sqlite3
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Sun, 15 Feb 2026 12:25:50 -0800 |
| parents | bcc76a156aea |
| children |
line wrap: on
line source
// -- Dog -- // const SCREEN_WIDTH = 800; const SCREEN_HEIGHT = 600; const PIXEL_SIZE = 3; const FRAME = 60; const dog = document.getElementById("epi3D"); dog.width = SCREEN_WIDTH; dog.height = SCREEN_HEIGHT; const ctx = dog.getContext("2d"); function drawBackground() { ctx.fillStyle = "black"; ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); } function drawPixel({x, y}) { ctx.fillStyle = "blue"; ctx.fillRect(x * SCREEN_WIDTH, y * SCREEN_HEIGHT, PIXEL_SIZE, PIXEL_SIZE); } function normalize({x, y}) { return { x: ((x + 1) / 2), y: (1 - ((y + 1) / 2)), } } function threeDtotwoD({x, y, z}) { return { x: x/z, y: y/z } } function drawLine(point1, point2) { ctx.beginPath(); ctx.moveTo(point1.x * SCREEN_WIDTH, point1.y * SCREEN_HEIGHT); ctx.lineTo(point2.x * SCREEN_WIDTH, point2.y * SCREEN_HEIGHT); ctx.lineWidth = 3; ctx.strokeStyle = "red"; ctx.stroke(); } let points = []; let vertices = []; async function loadXYZModel(filepath) { try { const response = await fetch(filepath); const xyzContent = await response.text(); // return parseXYZFile(xyzContent); return parseSmartSTL(xyzContent); } catch (error) { console.error('Error loading XYZ file:', error); return null; } } function downsamplePoints(points, gridSize = 0.2) { const grid = new Map(); for (const point of points) { // Create grid cell key const cellX = Math.floor(point.x / gridSize); const cellY = Math.floor(point.y / gridSize); const cellZ = Math.floor(point.z / gridSize); const key = `${cellX},${cellY},${cellZ}`; // Keep first point in each cell (or you could average them) if (!grid.has(key)) { grid.set(key, point); } } return Array.from(grid.values()); } async function initModel() { const model = await loadXYZModel('/public/dog.xyz'); if (model) { const normalizedPoints = normalizePoints(model.points, 1.0); points = downsamplePoints(normalizedPoints, 0.1); // grid method vertices = model.vertices; for (let i = 0; i < points.length - 1; i++) { vertices.push([i, i + 1]); } // Start the animation after loading drawAnimation(); } else { console.error('Failed to load model'); } } function parseSmartSTL(stlString) { const lines = stlString.split('\n'); const points = []; const vortexs = []; let currentNormal = ""; let tempVertices = []; lines.forEach((line) => { const trimmed = line.trim(); // 1. Detect the "Angle" (Normal) if (trimmed.startsWith('facet normal')) { const normal = trimmed.replace('facet normal ', ''); // Only care if the normal is different from the last one (it's a new angle) // Or just keep them all for now and we'll filter by position currentNormal = normal; tempVertices = []; } // 2. Grab the vertices if (trimmed.startsWith('vertex')) { const parts = trimmed.split(/\s+/); tempVertices.push({ x: parseFloat(parts[1]), y: parseFloat(parts[2]), z: parseFloat(parts[3]) }); } // 3. When the triangle ends, connect them if (trimmed.startsWith('endloop')) { const startIndex = points.length; points.push(...tempVertices); // Create a triangle connection (vortex) vortexs.push([startIndex, startIndex + 1, startIndex + 2]); } }); return { points, vortexs }; } // Usage: // const { points, vortexs } = parseSmartSTL(yourStlString); initModel(); function parseXYZFile(xyzFileContent) { const lines = xyzFileContent.trim().split('\n').filter(line => line.trim()); const points = lines.map(line => { const coords = line.trim().split(/\s+/).map(Number); return { x: coords[0], y: coords[1], z: coords[2] }; }); return { points }; } function normalizePoints(points, scale = 1.0) { const xs = points.map(p => p.x); const ys = points.map(p => p.y); const zs = points.map(p => p.z); const minX = Math.min(...xs), maxX = Math.max(...xs); const minY = Math.min(...ys), maxY = Math.max(...ys); const minZ = Math.min(...zs), maxZ = Math.max(...zs); const centerX = (minX + maxX) / 2; const centerY = (minY + maxY) / 2; const centerZ = (minZ + maxZ) / 2; const rangeX = maxX - minX; const rangeY = maxY - minY; const rangeZ = maxZ - minZ; const maxRange = Math.max(rangeX, rangeY, rangeZ); return points.map(p => ({ x: ((p.x - centerX) / maxRange) * scale, y: ((p.y - centerY) / maxRange) * scale, z: ((p.z - centerZ) / maxRange) * scale })); } function rotate_xz({x, y, z}, angle) { return { x: x * Math.cos(angle) - z * Math.sin(angle), y: y, z: x * Math.sin(angle) + z * Math.cos(angle), } } function move_point(point, { x, y, z}) { return { ...point, x: point.x + x, y: 1 - (point.y + y), z: point.z + z } } function move_z(point, dz) { return {...point, z: point.z + dz} } let dz = 0; let dt = 1/FRAME; let angle = 0; function drawAnimation() { drawBackground(); // dz += 1 * dt; angle += 1 * Math.PI * dt; for (const point of points) { drawPixel( threeDtotwoD( move_point(rotate_xz(point, angle), { x: 0.7, y: 0.5, z: 1.5}) ) ); } for (const vertex of vertices) { for (let i = 0; i < vertex.length - 1; i++) { const point1 = normalize( threeDtotwoD( move_point(rotate_xz(points[vertex[i]], angle), { x: 0.7, y: 0.5, z: 1.5}) ) ); const point2 = normalize( threeDtotwoD( move_point(rotate_xz(points[vertex[i + 1]], angle), { x: 0.7, y: 0.5, z: 1.5}) ) ); drawLine(point1, point2); } } setTimeout(() => drawAnimation(), 1000/60); } drawAnimation();