Skip to content

Instantly share code, notes, and snippets.

@emeeks
Last active March 17, 2016 02:09
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 emeeks/55656a891aa027a06f23 to your computer and use it in GitHub Desktop.
Save emeeks/55656a891aa027a06f23 to your computer and use it in GitHub Desktop.
Superformula Fireworks Scatterplot

Here's a scatterplot of some randomly made up fireworks display datapoint compared to the reported enjoyment of said fireworks display by an equally made up respondent.

What's cool about this is that the patterns that make up the fireworks are created using the superformula, a parameterized shape with tremendous flexibility.

<html>
<head>
<title>Superformula Fireworks</title>
<meta charset="utf-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="superformula.js" type="text/JavaScript"></script>
</head>
<style>
svg {
height: 850px;
width: 900px;
border: 1px solid gray;
background: black;
}
g.am-axis text {
font-size: 8px;
}
.domain {
fill: none;
}
.tick > line {
stroke: white;
stroke-width: 1px;
stroke-opacity: .25;
}
.tick > text {
fill: white;
}
</style>
<body>
<div id="viz">
<svg>
</svg>
</body>
<footer>
<script>
var xAxisName = ["time of day", "number of children", "crowd", "distance from fireworks", "number of drinks", "patriotism"];
var xAxisDomain = [[2,12], [0,6], [2, 1000], [0.1,10], [0,12], [1,100]];
var yAxisName = "Enjoyment";
randomDomain = parseInt(Math.random() * 6);
d3.select("svg").append("text").text("Enjoyment").style("fill", "white").attr("transform", "translate(830,350) rotate(90)")
d3.select("svg").append("text").text(xAxisName[randomDomain]).style("fill", "white").attr("transform", "translate(20,510)")
xAxis = d3.svg.axis().scale(d3.scale.linear().domain(xAxisDomain[randomDomain]).range([0,800])).orient("bottom").tickSize(425).ticks(4);
d3.select("svg").append("g").attr("transform", "translate(0,50)").attr("id", "xAxisG").call(xAxis);
yAxis = d3.svg.axis().scale(d3.scale.linear().domain([0,10]).range([450,50])).orient("right").ticks(10).tickSize(800);
d3.select("svg").append("g").attr("id", "yAxisG").call(yAxis);
var randomData = d3.range(100).map(function (d,i) {
var randY = (Math.random() * 400) + 50;
var randX = (Math.random() * 700) + 50;
return {x: randX, y: randY}
});
var superCircle = d3.superformula()
.type("circle")
.size(25);
d3.select("svg").selectAll("path.fireworks")
.data(randomData)
.enter()
.append("path")
.attr("transform", function (d) {return "translate(400,800)"})
.attr("d", superCircle)
.style("fill", "darkgray")
.each(function (d) {
randomShape = d3.superformulaTypes[parseInt(Math.random() * 21)];
d3.select(this)
.transition()
.delay(function () {return Math.random() * 5000})
.transition()
.duration(function () {return (Math.random() * 500) + 100})
.attr("transform", function (d) {return "translate(" + d.x + "," + d.y + ")"})
.transition()
.duration(function () {return (Math.random() * 100) + 50})
.attr("d", function() {
var randomSize = parseInt((Math.random() * 1000) + 100);
var fireworkPattern = d3.superformula().type(randomShape).size(randomSize);
return fireworkPattern();
})
.style("fill", function () {
var randomColor = parseInt(Math.random() * 12)
return ["#8dd3c7","#ffffb3","#bebada","#fb8072","#80b1d3","#fdb462","#b3de69","#fccde5","#d9d9d9","#bc80bd","#ccebc5","#ffed6f"][randomColor];
})
.transition()
.duration(500)
.attr("d", function() {
var randomSize = parseInt((Math.random() * 1000) + 100);
var fireworkPattern = d3.superformula().type(randomShape).size(35);
return fireworkPattern();
})
.style("fill-opacity", .75)
.style("stroke-width", 1)
.style("stroke-opacity", .25)
.style("stroke", "white")
})
</script>
</footer>
</html>
(function() {
var _symbol = d3.svg.symbol(),
_line = d3.svg.line();
d3.superformula = function() {
var type = _symbol.type(),
size = _symbol.size(),
segments = size,
params = {};
function superformula(d, i) {
var n, p = _superformulaTypes[type.call(this, d, i)];
for (n in params) p[n] = params[n].call(this, d, i);
return _superformulaPath(p, segments.call(this, d, i), Math.sqrt(size.call(this, d, i)));
}
superformula.type = function(x) {
if (!arguments.length) return type;
type = d3.functor(x);
return superformula;
};
superformula.param = function(name, value) {
if (arguments.length < 2) return params[name];
params[name] = d3.functor(value);
return superformula;
};
// size of superformula in square pixels
superformula.size = function(x) {
if (!arguments.length) return size;
size = d3.functor(x);
return superformula;
};
// number of discrete line segments
superformula.segments = function(x) {
if (!arguments.length) return segments;
segments = d3.functor(x);
return superformula;
};
return superformula;
};
function _superformulaPath(params, n, diameter) {
var i = -1,
dt = 2 * Math.PI / n,
t,
r = 0,
x,
y,
points = [];
while (++i < n) {
t = params.m * (i * dt - Math.PI) / 4;
t = Math.pow(Math.abs(Math.pow(Math.abs(Math.cos(t) / params.a), params.n2)
+ Math.pow(Math.abs(Math.sin(t) / params.b), params.n3)), -1 / params.n1);
if (t > r) r = t;
points.push(t);
}
r = diameter * Math.SQRT1_2 / r;
i = -1; while (++i < n) {
x = (t = points[i] * r) * Math.cos(i * dt);
y = t * Math.sin(i * dt);
points[i] = [Math.abs(x) < 1e-6 ? 0 : x, Math.abs(y) < 1e-6 ? 0 : y];
}
return _line(points) + "Z";
}
var _superformulaTypes = {
asterisk: {m: 12, n1: .3, n2: 0, n3: 10, a: 1, b: 1},
bean: {m: 2, n1: 1, n2: 4, n3: 8, a: 1, b: 1},
butterfly: {m: 3, n1: 1, n2: 6, n3: 2, a: .6, b: 1},
circle: {m: 4, n1: 2, n2: 2, n3: 2, a: 1, b: 1},
clover: {m: 6, n1: .3, n2: 0, n3: 10, a: 1, b: 1},
cloverFour: {m: 8, n1: 10, n2: -1, n3: -8, a: 1, b: 1},
cross: {m: 8, n1: 1.3, n2: .01, n3: 8, a: 1, b: 1},
diamond: {m: 4, n1: 1, n2: 1, n3: 1, a: 1, b: 1},
drop: {m: 1, n1: .5, n2: .5, n3: .5, a: 1, b: 1},
ellipse: {m: 4, n1: 2, n2: 2, n3: 2, a: 9, b: 6},
gear: {m: 19, n1: 100, n2: 50, n3: 50, a: 1, b: 1},
heart: {m: 1, n1: .8, n2: 1, n3: -8, a: 1, b: .18},
heptagon: {m: 7, n1: 1000, n2: 400, n3: 400, a: 1, b: 1},
hexagon: {m: 6, n1: 1000, n2: 400, n3: 400, a: 1, b: 1},
malteseCross: {m: 8, n1: .9, n2: .1, n3: 100, a: 1, b: 1},
pentagon: {m: 5, n1: 1000, n2: 600, n3: 600, a: 1, b: 1},
rectangle: {m: 4, n1: 100, n2: 100, n3: 100, a: 2, b: 1},
roundedStar: {m: 5, n1: 2, n2: 7, n3: 7, a: 1, b: 1},
square: {m: 4, n1: 100, n2: 100, n3: 100, a: 1, b: 1},
star: {m: 5, n1: 30, n2: 100, n3: 100, a: 1, b: 1},
triangle: {m: 3, n1: 100, n2: 200, n3: 200, a: 1, b: 1}
};
d3.superformulaTypes = d3.keys(_superformulaTypes);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment