Built with blockbuilder.org
-
-
Save sxywu/fc36182539d450557a465f0574d626b9 to your computer and use it in GitHub Desktop.
Generative flower experiment #2
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/p5@0.6.0/lib/p5.js"></script> | |
<script src="https://unpkg.com/lodash@4.17.5/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 = 200; | |
const numPetals = 7; | |
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)), | |
}; | |
}); | |
flower.selectAll('.petal') | |
.data(petalsData).enter().append('path') | |
.classed('petal', true) | |
.attr('fill', (d) => d.fill) | |
.attr('d', d => d.path) | |
.attr('transform', (d) => `rotate(${d.rotate})`); | |
// flower.append('path') | |
// .attr('d', stemPath()) | |
// .attr('fill', 'none'); | |
let xoff = 0; | |
flower.selectAll('path').each(function(d) { | |
const length = this.getTotalLength(); | |
const numPoints = _.ceil(length, -2); | |
ctx.lineWidth = 2; | |
ctx.fillStyle = '#444'; | |
_.times(numPoints, i => { | |
let point = this.getPointAtLength(i / numPoints * length); | |
let x = point.x; | |
let y = point.y; | |
if (d && d.rotate) { | |
const angle = (d.rotate / 180) * Math.PI; | |
point = rotateVector(point, angle); | |
x = point.x; | |
y = point.y; | |
} | |
x += 8 * (noise(xoff) - 0.5) + petalSize; | |
y += 8 * (noise(xoff) - 0.5) + petalSize; | |
const r = 4 * Math.abs(noise(xoff) - 0.1); | |
ctx.beginPath(); | |
ctx.arc(x, y, r, 0, Math.PI, 0); | |
ctx.fill(); | |
xoff += 0.01; | |
}); | |
}); | |
} | |
function petalPath() { | |
const blR = [0.5 * petalSize, 0.75 * petalSize]; // bottom length range | |
const baR = [0.55 * Math.PI, 0.65 * Math.PI]; // bottom angle range | |
const taR = [1.3 * Math.PI, 1.4 * Math.PI]; | |
a2lScale.domain(baR).range(blR); | |
let p1 = [0, 0]; | |
let p2 = [randomGaussian(0, 5), randomGaussian(0.9 * petalSize, 10)]; | |
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} `; | |
} | |
function stemPath() { | |
const p1 = [ | |
_.random(-0.75 * petalSize, -0.5 * petalSize), | |
randomGaussian(2 * petalSize, 10) | |
]; | |
const p2 = [0, 0]; | |
const cp = [p1[0], _.random(0, 0.25 * p1[1])]; | |
return `M${p1} Q${cp} ${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