view graphics/index.js @ 190:a2725419f988 hg-web

Updated so that bun builds will with already existing js files.
author MrJuneJune <me@mrjunejune.com>
date Sat, 24 Jan 2026 21:06:42 -0800
parents 8ceb5d3c6bdd
children
line wrap: on
line source

const SCREEN_WIDTH = 600;
const SCREEN_HEIGHT = 600;
const PIXEL_SIZE = 1;
const FRAME = 60

game.width = SCREEN_WIDTH;
game.height = SCREEN_HEIGHT;

const ctx = game.getContext("2d");

function drawBackground() {
  ctx.fillStyle = "black";
  ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
}

function drawPixel({x, y}) {
  ctx.fillStyle = "pink";
  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({x, y}, {x2, y2}) {
  ctx.beginPath();
  ctx.moveTo(x * SCREEN_WIDTH, y * SCREEN_HEIGHT);
  ctx.lineTo(x2 * SCREEN_WIDTH, y2* SCREEN_HEIGHT);
  ctx.lineWidth = 3;
  ctx.strokeStyle = "red";
  ctx.stroke();
}


// function generateHeartPoints(resolution = 0.008) {
//   const points = [];
//   // Nordstrom needs a slightly higher threshold because the 
//   // gradient of the cubic is steeper in some areas.
//   const threshold = 0.0001; 
// 
//   for (let x = -1.5; x <= 1.5; x += resolution) {
//     for (let y = -1.5; y <= 1.5; y += resolution) {
//       for (let z = -1.5; z <= 1.5; z += resolution) {
//         
//         const x2 = x * x;
//         const y2 = y * y;
//         const z2 = z * z;
//         const z3 = z2 * z;
// 
//         // Nordstrom Equation: (2x² + y² + z² - 1)³ - (1/10)x²z³ - y²z³ = 0
//         const inner = (2 * x2) + y2 + z2 - 1;
//         const value = Math.pow(inner, 3) - (0.1 * x2 * z3) - (y2 * z3);
// 
//         if (Math.abs(value) < threshold) {
//           // Keep the jitter for that organic "floating dust" look
//           points.push({
//             x: x + (Math.random() - 0.5) * resolution, 
//             y: y + (Math.random() - 0.5) * resolution,
//             z: z + (Math.random() - 0.5) * resolution
//           });
//         }
//       }
//     }
//   }
//   return points;
// }

function generateHeartPoints(resolution = 0.01) {
  const points = [];
  const threshold = 0.001;

  for (let x = -1.5; x <= 1.5; x += resolution) {
    for (let y = -1.5; y <= 1.5; y += resolution) {
      for (let z = -1.5; z <= 1.5; z += resolution) {
        
        const x2 = x * x;
        const y2 = y * y;
        const z2 = z * z;
        const z3 = z2 * z;

        const inner = x2 + (9/4) * y2 + z2 - 1;
        const value = Math.pow(inner, 3) - x2 * z3 - (9/80) * y2 * z3;

        if (Math.abs(value) < threshold) {
          points.push({
           x: x + (Math.random() - 0.5) * 0.01, 
           y: y + (Math.random() - 0.5) * 0.01,
           z: z + (Math.random() - 0.5) * 0.01
          });
        }
      }
    }
  }
  return points;
}

let points = generateHeartPoints();
const intial_start_z = 2;
points = points.map(point => ({...point, x: point.x + intial_start_z}));
// const points = [
//   {x: -0.25,  y:  0.25,  z: 0.25},
//   {x: -0.25,  y: -0.25,  z: 0.25},
//   {x:  0.25,  y: -0.25,  z: 0.25},
//   {x:  0.25,  y:  0.25,  z: 0.25},
// 
//   {x: -0.25,  y:  0.25,  z:  -0.25},
//   {x: -0.25,  y: -0.25,  z:  -0.25},
//   {x:  0.25,  y: -0.25,  z:  -0.25},
//   {x:  0.25,  y:  0.25,  z:  -0.25},
// ]
// const vortexs = [
//   [0, 1, 2, 3],
//   [4, 5, 6, 7],
//   [0, 4],
//   [1, 5],
//   [2, 6],
//   [3, 7],
// ]

function rotate_xy({x, y, z}, angle) {
  return {
    x: x * Math.cos(angle) - y * Math.sin(angle),
    y: x * Math.sin(angle) + y * Math.cos(angle),
    z: z
  };
}

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 rotate_yz({x, y, z}, angle) {
  return {
    x: x,
    y: y * Math.sin(angle) + z * Math.cos(angle),
    z: y * Math.cos(angle) - z * Math.sin(angle)
  };
}

function move_z(point, dz) {
  return {...point, z: point.z + dz}
}

function move_y(point, dy) {
  return {...point, y: point.y + dy}
}

let dz = 0;
let dt = 1/FRAME;
let angle = 0;
const fontPoint = {x: SCREEN_WIDTH / 2, y: SCREEN_HEIGHT * 0.3};

function drawAnimation() {
  drawBackground();
  dz -= 1 * dt;
  angle += 2*Math.PI*dt*0.2;

  // Texts...
  ctx.font = "60px serif";
  ctx.textAlign = "center"; // Ensures the point is the middle of the text
  ctx.textBaseline = "middle";
  ctx.fillStyle = "pink";
  ctx.fillText("Heart!", fontPoint.x, fontPoint.y);

  // Hearts...
  for (var point of points)
  {
    const newPoint = normalize(
        threeDtotwoD(
          move_z(rotate_yz(rotate_xz(rotate_xy(point, angle), angle), angle), dz)
          // move_z(point, dz)
        )
      );
    drawPixel(newPoint);
  }

  // for (var vortex of vortexs)
  // {
  //   for (let i = 0; i < vortex.length; i++)
  //   {
  //     const newPoint = normalize(
  //         threeDtotwoD(
  //           move_z(rotate_xz(points[vortex[(i+1)%vortex.length]], angle), dz)
  //         )
  //       );

  //     drawLine(
  //       normalize(
  //         threeDtotwoD(
  //           move_z(rotate_xz(points[vortex[i]], angle), dz)
  //         )
  //       ),
  //       {x2: newPoint.x, y2: newPoint.y}
  //     );
  //   }
  // }
  setTimeout(() => drawAnimation(), 1000/60)
}

drawAnimation()