view mrjunejune/src/public/dog.js @ 279:b3b547563ec7

Add Google connector service and agent wiki Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code. Co-authored-by: Copilot <[email protected]> Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
author MrJuneJune <me@mrjunejune.com>
date Mon, 17 Aug 2026 22:22:36 -0700
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();