From 6a0d2470a1d2815681c2a58b5253489fdd578e8d Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Sat, 19 Apr 2025 18:27:58 +0800 Subject: [PATCH 1/7] Added hyperbolic vignettes in and out --- src/components/dots.js | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/components/dots.js b/src/components/dots.js index b34cd15..d68efc8 100644 --- a/src/components/dots.js +++ b/src/components/dots.js @@ -11,8 +11,10 @@ const RADIUS_OPTS = [ "right-down", "left-up", "left-down", - "in", - "out", + "circle-in", + "circle-out", + "hyper-in", + "hyper-out", "noise", ]; @@ -26,8 +28,10 @@ const RADIUS_DESC = { "right-down": "getting larger towards the lower right", "left-up": "getting larger towards the upper left", "left-down": "getting larger towards the lower left", - "in": "getting larger in the centre", - "out": "getting larger at the edges", + "circle-in": "forming a circle at the centre", + "circle-out": "leaving a circular gap at the centre", + "hyper-in": "forming a starlike pattern at the centre", + "hyper-out": "leaving a starlike pattern gap in the centre", "noise": "of random sizes", } @@ -35,6 +39,7 @@ function distance(dx, dy) { return Math.sqrt(dx ** 2 + dy ** 2); } + function int_range(v1, v2) { const vs = [v1, v2]; vs.sort((a, b) => a - b); @@ -92,16 +97,14 @@ class DotMaker { return 0.5 * maxr * (d.x + d.y) / this.wh; case "left-down": return 0.5 * maxr * (this.width - d.x + d.y) / this.wh; - case "out": + case "circle-out": return 2 * maxr * distance((d.x - this.cx), (d.y - this.cy)) / this.wh; - case "in": + case "circle-in": return 2 * maxr * (0.5 * this.wh - distance((d.x - this.cx), (d.y - this.cy))) / this.wh; - - // case "hyper-out": - // return 2 * maxr * Math.abs(d.x - this.cx) (d.y - this.cy)) / this.width; - // case "hyper-in": - // return 2 * maxr * (0.5 * this.width - distance((d.x - this.cx), (d.y - this.cy))) / this.width; - + case "hyper-in": + return maxr * Math.abs((d.x - this.cx) * (d.y - this.cy)) / this.wh; + case "hyper-out": + return maxr * (1 - Math.abs((d.x - this.cx) * (d.y - this.cy)) / this.wh); case "noise": return maxr * Math.random(); default: -- 2.49.0 From 81f0702e27ca8b4b63c109e9a929369e38f942cb Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Sat, 19 Apr 2025 18:44:01 +0800 Subject: [PATCH 2/7] Added first version of sin (diamond grid) and horizontal and vertical bands --- src/components/dots.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/components/dots.js b/src/components/dots.js index d68efc8..ded89a1 100644 --- a/src/components/dots.js +++ b/src/components/dots.js @@ -1,5 +1,6 @@ // calculate tiles +const SIN_PERIOD = 100 / Math.PI; const RADIUS_OPTS = [ "const", @@ -16,6 +17,9 @@ const RADIUS_OPTS = [ "hyper-in", "hyper-out", "noise", + "diamonds", + "horizontal-bands", + "vertical-bands", ]; const RADIUS_DESC = { @@ -33,6 +37,9 @@ const RADIUS_DESC = { "hyper-in": "forming a starlike pattern at the centre", "hyper-out": "leaving a starlike pattern gap in the centre", "noise": "of random sizes", + "diamonds": "in a grid tilted at 45 degrees", + "horizontal-bands": "in horizontal bands", + "vertical-bands": "in a vertical bands", } function distance(dx, dy) { @@ -107,6 +114,12 @@ class DotMaker { return maxr * (1 - Math.abs((d.x - this.cx) * (d.y - this.cy)) / this.wh); case "noise": return maxr * Math.random(); + case "horizontal-bands": + return maxr * (1 + Math.cos(SIN_PERIOD *(d.y - this.cy)/ this.wh)); + case "vertical-bands": + return maxr * (1 + Math.cos(SIN_PERIOD *(d.x - this.cx)/ this.wh)); + case "grid": + return maxr * (0.5 + 0.5 * (Math.cos(SIN_PERIOD *(d.x - this.cx)/ this.wh) + Math.cos(SIN_PERIOD * (d.y - this.cy)/ this.wh))); default: return maxr; } -- 2.49.0 From f397af24cdf38aa0809d3c28ee20606989b69bed Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Sun, 20 Apr 2025 15:25:08 +1000 Subject: [PATCH 3/7] the radius method now returns a closure, so we can pick random parameters for things like the sinusoid grids or centre points --- src/components/dots.js | 38 +++++++++++++++++++------------------- src/index.md | 7 +++++-- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/components/dots.js b/src/components/dots.js index ebe5811..0af511d 100644 --- a/src/components/dots.js +++ b/src/components/dots.js @@ -83,44 +83,44 @@ class DotMaker { return ps; } - radius(d, func, maxr) { + radius(func) { switch (func) { case "const": - return maxr; + return (d, r) => r; case "right": - return maxr * d.x / this.width; + return (d, r) => r * d.x / this.width; case "left": - return maxr * (this.width - d.x) / this.width; + return (d, r) => r * (this.width - d.x) / this.width; case "down": - return maxr * d.y / this.height; + return (d, r) => r * d.y / this.height; case "up": - return maxr * (this.height - d.y) / this.height; + return (d, r) => r * (this.height - d.y) / this.height; case "right-up": - return 0.5 * maxr * (d.x + this.height - d.y) / this.wh; + return (d, r) => 0.5 * r * (d.x + this.height - d.y) / this.wh; case "left-up": - return 0.5 * maxr * (this.width - d.x + this.height - d.y) / this.wh; + return (d, r) => 0.5 * r * (this.width - d.x + this.height - d.y) / this.wh; case "right-down": - return 0.5 * maxr * (d.x + d.y) / this.wh; + return (d, r) => 0.5 * r * (d.x + d.y) / this.wh; case "left-down": - return 0.5 * maxr * (this.width - d.x + d.y) / this.wh; + return (d, r) => 0.5 * r * (this.width - d.x + d.y) / this.wh; case "circle-out": - return 2 * maxr * distance((d.x - this.cx), (d.y - this.cy)) / this.wh; + return (d, r) => 2 * r * distance((d.x - this.cx), (d.y - this.cy)) / this.wh; case "circle-in": - return 2 * maxr * (0.5 * this.wh - distance((d.x - this.cx), (d.y - this.cy))) / this.wh; + return (d, r) => 2 * r * (0.5 * this.wh - distance((d.x - this.cx), (d.y - this.cy))) / this.wh; case "hyper-in": - return maxr * Math.abs((d.x - this.cx) * (d.y - this.cy)) / this.wh; + return (d, r) => r * Math.abs((d.x - this.cx) * (d.y - this.cy)) / this.wh; case "hyper-out": - return maxr * (1 - Math.abs((d.x - this.cx) * (d.y - this.cy)) / this.wh); + return (d, r) => r * (1 - Math.abs((d.x - this.cx) * (d.y - this.cy)) / this.wh); case "noise": - return maxr * Math.random(); + return (d, r) => r * Math.random(); case "horizontal-bands": - return maxr * (1 + Math.cos(SIN_PERIOD *(d.y - this.cy)/ this.wh)); + return (d, r) => r * (1 + Math.cos(SIN_PERIOD *(d.y - this.cy)/ this.wh)); case "vertical-bands": - return maxr * (1 + Math.cos(SIN_PERIOD *(d.x - this.cx)/ this.wh)); + return (d, r) => r * (1 + Math.cos(SIN_PERIOD *(d.x - this.cx)/ this.wh)); case "grid": - return maxr * (0.5 + 0.5 * (Math.cos(SIN_PERIOD *(d.x - this.cx)/ this.wh) + Math.cos(SIN_PERIOD * (d.y - this.cy)/ this.wh))); + return (d, r) => r * (0.5 + 0.5 * (Math.cos(SIN_PERIOD *(d.x - this.cx)/ this.wh) + Math.cos(SIN_PERIOD * (d.y - this.cy)/ this.wh))); default: - return maxr; + return (d, r) => r; } } } diff --git a/src/index.md b/src/index.md index 16add7a..5c07cad 100644 --- a/src/index.md +++ b/src/index.md @@ -209,9 +209,12 @@ display(svg.node()); ```js // separate code block for when I understand transitions better + + const rfunc1 = dm.radius(f1); + const rfunc2 = dm.radius(f2); - dots_g1.selectAll("circle").attr("r", (d) => dm.radius(d, f1, r1)); - dots_g2.selectAll("circle").attr("r", (d) => dm.radius(d, f2, r2)); + dots_g1.selectAll("circle").attr("r", (d) => rfunc1(d, r1)); + dots_g2.selectAll("circle").attr("r", (d) => rfunc2(d, r2)); ``` -- 2.49.0 From 3a9d2190ba47ef4871b2293b3249143c319c6699 Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Sun, 20 Apr 2025 15:29:55 +1000 Subject: [PATCH 4/7] bot script now uses closures - I really shouldn't have to have changed this in both places --- poptimal.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/poptimal.js b/poptimal.js index 44b3dbf..dc0cb69 100644 --- a/poptimal.js +++ b/poptimal.js @@ -156,13 +156,14 @@ function poptimal_svg(params) { params.patterns.map((p) => { const dots = dm.dots(1 / p.m, p.n, false); + const rfunc = dm.radius(p.f); const dots_g = svg.append("g") .attr("id", `dots${p.i}`); dots_g.selectAll("circle") .data(dots) .join("circle") - .attr("r", (d) => dm.radius(d, p.f, p.r)) + .attr("r", (d) => rfunc(d, p.r)) .attr("fill", p.colour) .attr("cx", (d) => d.x) .attr("cy", (d) => d.y); -- 2.49.0 From 077dc5254dd458861107a1b0ae7e304a0eaaf9b1 Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Sun, 20 Apr 2025 15:32:22 +1000 Subject: [PATCH 5/7] Fixed alt-text for hyper-out --- src/components/dots.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dots.js b/src/components/dots.js index 0af511d..15ef418 100644 --- a/src/components/dots.js +++ b/src/components/dots.js @@ -35,7 +35,7 @@ const RADIUS_DESC = { "circle-in": "forming a circle at the centre", "circle-out": "leaving a circular gap at the centre", "hyper-in": "forming a starlike pattern at the centre", - "hyper-out": "leaving a starlike pattern gap in the centre", + "hyper-out": "leaving a starlike gap in the centre", "noise": "of random sizes", "diamonds": "in a grid tilted at 45 degrees", "horizontal-bands": "in horizontal bands", -- 2.49.0 From bf7ae246a3a06cbb9c96644d9d68b85c7b81f362 Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Sun, 20 Apr 2025 16:02:41 +1000 Subject: [PATCH 6/7] Generalised grids and bands into a single function --- src/components/dots.js | 25 +++++++++++-------------- src/index.md | 10 +++++++--- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/components/dots.js b/src/components/dots.js index 15ef418..0030bdd 100644 --- a/src/components/dots.js +++ b/src/components/dots.js @@ -1,6 +1,5 @@ // calculate tiles -const SIN_PERIOD = 100 / Math.PI; const RADIUS_OPTS = [ "const", @@ -16,10 +15,8 @@ const RADIUS_OPTS = [ "circle-out", "hyper-in", "hyper-out", + "grid", "noise", - "diamonds", - "horizontal-bands", - "vertical-bands", ]; const RADIUS_DESC = { @@ -37,9 +34,7 @@ const RADIUS_DESC = { "hyper-in": "forming a starlike pattern at the centre", "hyper-out": "leaving a starlike gap in the centre", "noise": "of random sizes", - "diamonds": "in a grid tilted at 45 degrees", - "horizontal-bands": "in horizontal bands", - "vertical-bands": "in a vertical bands", + "grid": "forming a grid pattern", } function distance(dx, dy) { @@ -55,6 +50,10 @@ function int_range(v1, v2) { return [...Array(high - low + 1).keys()].map((i) => i + low); } +function randint(min, max) { + return min + Math.floor(Math.random() * (max + 1)); +} + class DotMaker { constructor(width, height) { this.width = width; @@ -108,17 +107,15 @@ class DotMaker { case "circle-in": return (d, r) => 2 * r * (0.5 * this.wh - distance((d.x - this.cx), (d.y - this.cy))) / this.wh; case "hyper-in": - return (d, r) => r * Math.abs((d.x - this.cx) * (d.y - this.cy)) / this.wh; - case "hyper-out": return (d, r) => r * (1 - Math.abs((d.x - this.cx) * (d.y - this.cy)) / this.wh); + case "hyper-out": + return (d, r) => r * Math.abs((d.x - this.cx) * (d.y - this.cy)) / this.wh; case "noise": return (d, r) => r * Math.random(); - case "horizontal-bands": - return (d, r) => r * (1 + Math.cos(SIN_PERIOD *(d.y - this.cy)/ this.wh)); - case "vertical-bands": - return (d, r) => r * (1 + Math.cos(SIN_PERIOD *(d.x - this.cx)/ this.wh)); case "grid": - return (d, r) => r * (0.5 + 0.5 * (Math.cos(SIN_PERIOD *(d.x - this.cx)/ this.wh) + Math.cos(SIN_PERIOD * (d.y - this.cy)/ this.wh))); + const xk = Math.PI * (2 * randint(1, 10) - 1) / this.width; + const yk = Math.PI * (2 * randint(1, 10) - 1) / this.height; + return (d, r) => r * (0.5 + 0.5 * (Math.sin(xk * d.x) + Math.sin(yk * d.y))); default: return (d, r) => r; } diff --git a/src/index.md b/src/index.md index 5c07cad..4a87cbe 100644 --- a/src/index.md +++ b/src/index.md @@ -137,6 +137,13 @@ const dots1 = dm.dots(1 / m1, n1, false); const dots2 = dm.dots(1 / m2, n2, false); +``` + +```js + +const rfunc1 = dm.radius(f1); +const rfunc2 = dm.radius(f2); + ``` ```js @@ -209,9 +216,6 @@ display(svg.node()); ```js // separate code block for when I understand transitions better - - const rfunc1 = dm.radius(f1); - const rfunc2 = dm.radius(f2); dots_g1.selectAll("circle").attr("r", (d) => rfunc1(d, r1)); dots_g2.selectAll("circle").attr("r", (d) => rfunc2(d, r2)); -- 2.49.0 From 824605820b7ea14d3dc0c223a5263ed090a83c20 Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Sun, 20 Apr 2025 16:03:44 +1000 Subject: [PATCH 7/7] Bumped version number --- src/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.md b/src/index.md index 4a87cbe..36bb782 100644 --- a/src/index.md +++ b/src/index.md @@ -8,7 +8,7 @@ toc: false colourful generative patterns using [d3](https://d3js.org/) and [Observable Framework](https://observablehq.com/framework/) -

v1.2.0 | by mike lynch | @mikelynch@aus.social | source

+

v1.2.1 | by mike lynch | @mikelynch@aus.social | source

-- 2.49.0