From 7cd96f19979f794299bbfab047c0b29c51f96d7b Mon Sep 17 00:00:00 2001 From: Anson Date: Sun, 25 May 2025 20:16:35 +0000 Subject: [PATCH] make a cute little canvas to draw on and hook it up to some ojs --- .../quarto-listing/quarto-listing.js | 3 +- posts/2025-05-25-Brachistochrone/index.qmd | 114 ++++++++++++++++++ 2 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 posts/2025-05-25-Brachistochrone/index.qmd diff --git a/.quarto/_freeze/site_libs/quarto-listing/quarto-listing.js b/.quarto/_freeze/site_libs/quarto-listing/quarto-listing.js index e9a07b2..54d0e1e 100644 --- a/.quarto/_freeze/site_libs/quarto-listing/quarto-listing.js +++ b/.quarto/_freeze/site_libs/quarto-listing/quarto-listing.js @@ -2,8 +2,7 @@ const kProgressiveAttr = "data-src"; let categoriesLoaded = false; window.quartoListingCategory = (category) => { - // category is URI encoded in EJS template for UTF-8 support - category = decodeURIComponent(atob(category)); + category = atob(category); if (categoriesLoaded) { activateCategory(category); setCategoryHash(category); diff --git a/posts/2025-05-25-Brachistochrone/index.qmd b/posts/2025-05-25-Brachistochrone/index.qmd new file mode 100644 index 0000000..2f1d7f6 --- /dev/null +++ b/posts/2025-05-25-Brachistochrone/index.qmd @@ -0,0 +1,114 @@ +--- +title: "Brachistochrone" +description: | + Lets create a double pendulum in Observable JS! +date: 2025-05-09 +categories: + - Observable JS + - Code + - Math +--- + +This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text + +:::{.column-screen} +```{ojs} +//| echo: false + + +viewof canvas = { + const height = 500; + const canvas = html``; + const ctx = canvas.getContext('2d'); + + let isDrawing = false; + let lastX = 0; + let lastY = 0; + let pathLength = 0; + let pathHistory = [{time: Date.now(), length: 0}]; + + // Drawing settings + ctx.strokeStyle = '#000'; + ctx.lineWidth = 2; + ctx.lineCap = 'round'; + ctx.lineJoin = 'round'; + + canvas.addEventListener('mousedown', (e) => { + if (!isDrawing) { + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.arc(300, 100, 5, 0, 2 * Math.PI); + ctx.fillStyle = 'red'; + ctx.fill(); + ctx.arc(600, 300, 5, 0, 2 * Math.PI); + ctx.fillStyle = 'red'; + ctx.fill(); + pathLength = 0; + pathHistory = [{time: Date.now(), length: 0}]; + canvas.value = {length: pathLength, history: pathHistory}; + canvas.dispatchEvent(new CustomEvent("input")); + } + isDrawing = true; + [lastX, lastY] = [e.offsetX, e.offsetY]; + }); + + canvas.addEventListener('mousemove', (e) => { + if (!isDrawing) return; + + // Calculate distance + const distance = Math.sqrt( + Math.pow(e.offsetX - lastX, 2) + + Math.pow(e.offsetY - lastY, 2) + ); + const angle = Math.atan2(e.offsetY - lastY, e.offsetX - lastX) + pathLength += distance; + + // Draw + ctx.beginPath(); + ctx.moveTo(lastX, lastY); + ctx.lineTo(e.offsetX, e.offsetY); + ctx.stroke(); + [lastX, lastY] = [e.offsetX, e.offsetY]; + + // Update value and emit event + pathHistory.push({time: Date.now(), length: pathLength}); + canvas.value = {length: pathLength, history: pathHistory}; + canvas.dispatchEvent(new CustomEvent("input")); + }); + + canvas.addEventListener('mouseup', () => isDrawing = false); + canvas.addEventListener('mouseout', () => isDrawing = false); + + // Initial value + canvas.value = {length: 0, history: pathHistory}; + return canvas; +} + +``` + + +```{ojs} +//| echo: false +md`Current path length: **${canvas.length?.toFixed(0) || 0} pixels**` +``` + +```{ojs} +//| echo: false +import {Plot} from "@observablehq/plot" + +pathChart = Plot.plot({ + width: width, + height: 200, + y: {label: "Path Length (pixels)"}, + x: {label: "Time", type: "time"}, + marks: [ + Plot.line(canvas.history || [], { + x: "time", + y: "length", + stroke: "steelblue", + strokeWidth: 2 + }) + ] +}) +``` + +This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text This is a bunch of text \ No newline at end of file