Built with blockbuilder.org
Last active
March 26, 2021 22:53
-
-
Save sxywu/56728e6cf17ad249ef232c49cd9a90ad to your computer and use it in GitHub Desktop.
Generative flower experiment #1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: mit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://unpkg.com/[email protected]/lib/p5.js"></script> | |
<script src="https://unpkg.com/[email protected]/lodash.js"></script> | |
<style> | |
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } | |
path { mix-blend-mode: overlay; } | |
#content { isolation: isolate; } | |
</style> | |
</head> | |
<body> | |
<div id='content' /> | |
<script> | |
const petalSize = 100; | |
const numPetals = 5; | |
const svg = d3.select("#content").append("svg") | |
.attr("width", 960).attr("height", 500); | |
const flower = svg.append('g') | |
.attr('transform', `translate(${[petalSize, petalSize]})`) | |
.attr('fill-opacity', 0.4); | |
const canvas = d3.select("#content").append('canvas') | |
.attr("width", 960).attr("height", 500) | |
.style('position', 'absolute').style('top', 0).style('left', 0); | |
const ctx = canvas.node().getContext('2d'); | |
// using processing so let's just do everything in setup | |
const a2lScale = d3.scaleLinear(); // angle to length scale | |
function setup() { | |
const perAngle = 360 / numPetals; | |
const initialAngle = _.random(perAngle); | |
const petalsData = _.times(2 * numPetals, (i) => { | |
return { | |
path: petalPath(), | |
rotate: randomGaussian(i * perAngle + initialAngle, 3), | |
fill: d3.interpolatePlasma(_.random(0.35, 1)), | |
}; | |
}); | |
const petals = flower.selectAll('path') | |
.data(petalsData).enter().append('path') | |
.attr('fill', (d) => d.fill) | |
.attr('d', d => d.path) | |
.attr('transform', (d) => `rotate(${d.rotate})`); | |
let xoff = 0; | |
petals.each(function(d) { | |
const length = this.getTotalLength(); | |
const numPoints = _.ceil(length, -2); | |
ctx.lineWidth = 2; | |
ctx.fillStyle = '#333'; | |
_.times(numPoints, i => { | |
const point = this.getPointAtLength(i / numPoints * length); | |
const angle = (d.rotate / 180) * Math.PI; | |
let {x, y} = rotateVector(point, angle); | |
x += 8 * noise(xoff) + petalSize; | |
y += 8 * noise(xoff) + petalSize; | |
const r = 4 * noise(xoff); | |
ctx.beginPath(); | |
ctx.arc(x, y, r, 0, Math.PI, 0); | |
ctx.fill(); | |
xoff += 0.01; | |
}); | |
}); | |
} | |
function petalPath() { | |
const blR = [petalSize * 0.75, petalSize]; // bottom length range | |
const baR = [0.65 * Math.PI, 0.8 * Math.PI]; // bottom angle range | |
const taR = [1.4 * Math.PI, 1.6 * Math.PI]; | |
a2lScale.domain(baR).range(blR); | |
let p1 = [0, 0]; | |
let p2 = [randomGaussian(0, 5), randomGaussian(0.9 * petalSize, 8)]; | |
let cp1a = _.random(baR[0], baR[1]); // first cp angle | |
let cp1l = a2lScale(cp1a); | |
let cp1 = [ | |
cp1l * Math.cos(cp1a), | |
cp1l * Math.sin(cp1a), | |
]; | |
let cp2a = _.random(taR[0], taR[1]); | |
let cp2l = Math.max(0.25 * petalSize, petalSize - cp1[1]); | |
let cp2 = [ | |
cp2l * Math.cos(cp2a) + p2[0], | |
cp2l * Math.sin(cp2a) + p2[1], | |
]; | |
let pathD = `M${p1} C${cp1} ${cp2} ${p2} `; | |
// and add the other size | |
p1 = p2; | |
p2 = [0, 0]; | |
cp2a = randomGaussian(cp1a, 0.1); // first cp angle | |
cp2l = randomGaussian(cp1l, 5); | |
cp2 = [ | |
-cp2l * Math.cos(cp2a), | |
cp2l * Math.sin(cp2a), | |
]; | |
cp1a = _.random(taR[0], taR[1]); | |
cp1l = Math.max(0.25 * petalSize, petalSize - cp2[1]); | |
cp1 = [ | |
-cp1l * Math.cos(cp1a) + p1[0], | |
cp1l * Math.sin(cp1a) + p1[1], | |
]; | |
return pathD += `M${p1} C${cp1} ${cp2} ${p2} `; | |
} | |
// taken from https://beta.observablehq.com/@tonyhschu/solidifying-my-intuition-around-matrix-transformations | |
rotateVector = (vector, theta) => { | |
const { x, y } = vector | |
// first column of the matrix | |
const xa = x * Math.cos(theta) | |
const xb = x * Math.sin(theta) | |
// second column of the matrix | |
const yc = y * -Math.sin(theta) | |
const yd = y * Math.cos(theta) | |
// sum the components | |
return { | |
x: xa + yc, | |
y: xb + yd | |
} | |
} | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment