From 88b74603ee9efe498fbef8c2255325d62fd29246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20L=C3=B6vqvist?= Date: Wed, 3 Sep 2025 16:16:01 +0200 Subject: [PATCH] Added unfinished interactive triangular lattice and a vibe coded gradient editor --- canvas/interactive-triangular-lattice.js | 112 ++++++++ index.html | 8 + standalone/gradient_editor.html | 352 +++++++++++++++++++++++ 3 files changed, 472 insertions(+) create mode 100644 canvas/interactive-triangular-lattice.js create mode 100644 standalone/gradient_editor.html diff --git a/canvas/interactive-triangular-lattice.js b/canvas/interactive-triangular-lattice.js new file mode 100644 index 0000000..139f9c5 --- /dev/null +++ b/canvas/interactive-triangular-lattice.js @@ -0,0 +1,112 @@ +{ // Wrapping in new local scope so we can override constants +const oldCanvas = document.getElementById('canvas'); +const canvas = oldCanvas.cloneNode(true); +oldCanvas.parentNode.replaceChild(canvas, oldCanvas); +const C = canvas.getContext('2d'); + + +//Create useful variables +function setup() { + const [W, H] = [400, 400]; + const aspect = W / H; + const scale = aspect >= 1 ? H / 2 : W / 2; + canvas.width = W; + canvas.height = H; + + // Reset transform + C.setTransform(1, 0, 0, 1, 0, 0); + + // Clear canvas + C.clearRect(0, 0, W, H); + + // Reset common properties + C.globalCompositeOperation = 'source-over'; + C.fillStyle = 'rgba(255,200,100,1)'; + C.strokeStyle = 'rgba(0,0,0,1)'; + + + // Set up transform to map -1,-1..1,1 to canvas with aspect fit + const offsetX = W / 2; + const offsetY = H / 2; + + //Apply transform + C.translate(offsetX, offsetY); + C.scale(scale, scale); + + // Set 1 pixel wide lines + C.lineWidth = 1 / scale; +} + + +setup(); + + + +const { Bitfield_Image_Sampler } = await import('../lib/image-functions.js'); +//Load +const sampler = Bitfield_Image_Sampler.from_string('16,16,AAAAAAAAAAAAAA4AEQDx/xGgDqAAAAAAAAAAAAAAAAA='); + +const s = 0.08; +for (let y = -10; y < 10; y++) { + const xo = y & 1 ? -0.5*s : 0; + const sxo = (y >> 1); + //const sxo = y & 1 ? 1 : 0; + for (let x = -10; x < 10; x++) { + const p0 = [xo + (x + .5) * s, y * s]; + const p1 = [xo + (x + 1.5) * s, y * s]; + const p2 = [xo + (x + 1) * s, (y + 1) * s]; + const p3 = [xo + (x + 0) * s, (y + 1) * s]; + + // Filled parallelogram + C.beginPath(); + C.moveTo(...p0); + C.lineTo(...p1); + C.lineTo(...p2); + C.lineTo(...p3); + C.closePath(); + + if (sampler.sample(sxo + x + 8, 8 + y)) { + C.fillStyle = `#cf8`; + } else { + C.fillStyle = `#f88`; + } + + C.fill(); + + // Triangle lines + C.strokeStyle = '#000'; + + // Outline parallelogram + C.beginPath(); + C.moveTo(...p0); + C.lineTo(...p1); + C.lineTo(...p2); + C.lineTo(...p3); + C.closePath(); + C.stroke(); + + // Diagonal from p0 to p2 + C.beginPath(); + C.moveTo(...p0); + C.lineTo(...p2); + C.stroke(); + } +} + +canvas.addEventListener('mousemove', (e) => { + const rect = canvas.getBoundingClientRect(); + const viewX = e.clientX - rect.left; + const viewY = e.clientY - rect.top; + + // Get the current transform matrix + const m = C.getTransform(); // DOMMatrix + const inv = m.invertSelf(); // In-place inverse + + const worldX = inv.a * viewX + inv.c * viewY + inv.e; + const worldY = inv.b * viewX + inv.d * viewY + inv.f; + + // Store or use the world coords + console.log('World:', worldX, worldY); +}); + +} \ No newline at end of file diff --git a/index.html b/index.html index 32f25ed..3e4671b 100644 --- a/index.html +++ b/index.html @@ -13,6 +13,7 @@
  • Triangular lattice - corrected offset
  • Triangular lattice - sampling raster
  • Triangular lattice - sampling raster in a skewed pattern
  • +
  • Triangular lattice - unfinished interactive example
  • Image loading
  • @@ -24,6 +25,13 @@ +
  • +

    Standalone vibe coded utilities/experiments

    + +
  • +
  • CSS-Examples