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); diff --git a/src/components/dots.js b/src/components/dots.js index 3b061b3..0030bdd 100644 --- a/src/components/dots.js +++ b/src/components/dots.js @@ -11,8 +11,11 @@ const RADIUS_OPTS = [ "right-down", "left-up", "left-down", - "in", - "out", + "circle-in", + "circle-out", + "hyper-in", + "hyper-out", + "grid", "noise", ]; @@ -26,15 +29,19 @@ 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 gap in the centre", "noise": "of random sizes", + "grid": "forming a grid pattern", } 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); @@ -43,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; @@ -71,40 +82,42 @@ 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; - case "out": - return 2 * maxr * distance((d.x - this.cx), (d.y - this.cy)) / this.wh; - case "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; - + return (d, r) => 0.5 * r * (this.width - d.x + d.y) / this.wh; + case "circle-out": + return (d, r) => 2 * r * distance((d.x - this.cx), (d.y - this.cy)) / this.wh; + 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 * (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 maxr * Math.random(); + return (d, r) => r * Math.random(); + case "grid": + 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 maxr; + return (d, r) => r; } } } diff --git a/src/index.md b/src/index.md index 16add7a..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

@@ -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 @@ -210,8 +217,8 @@ display(svg.node()); ```js // separate code block for when I understand transitions better - 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)); ```