Skip to content

Instantly share code, notes, and snippets.

@sxywu

sxywu/.block Secret

Last active March 5, 2018 04:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sxywu/fc36182539d450557a465f0574d626b9 to your computer and use it in GitHub Desktop.
Save sxywu/fc36182539d450557a465f0574d626b9 to your computer and use it in GitHub Desktop.
Generative flower experiment #2
license: mit
<!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