Sometimes the mistakes in between are cooler than the end product.
forked from curran's block: Fractal Pie Chart
license: mit | |
border: no | |
height: 1400 |
Sometimes the mistakes in between are cooler than the end product.
forked from curran's block: Fractal Pie Chart
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://unpkg.com/d3@4"></script> | |
<script src="https://unpkg.com/d3-component@3"></script> | |
</head> | |
<body style="margin: 0"> | |
<svg width="960" height="1400"></svg> | |
<script> | |
const svg = d3.select("svg"); | |
const width = +svg.attr("width"); | |
const height = +svg.attr("height"); | |
const pie = d3.pie() | |
.value(d => d.value) | |
.startAngle(- Math.PI/2) | |
.endAngle(Math.PI/2); | |
const arc = d3.arc().innerRadius(0); | |
const slice = d3.component("path") | |
.render((selection, d) => { | |
selection | |
.attr("d", arc(d)) | |
.attr("fill", "#f4f4f4") | |
.attr("stroke", "#707070") | |
.attr("stroke-width", 1) | |
.attr("stroke-linejoin", "round"); | |
}); | |
const connector = d3.component("line") | |
.render((selection, {parentRadius, stemDistance, radius}) => { | |
if(parentRadius !== 0){ | |
selection | |
.attr("x1", -radius) | |
.attr("y1", 0) | |
.attr("x2", -(stemDistance - parentRadius)) | |
.attr("y2", 0) | |
.attr("stroke", "#a0a0a0"); | |
} | |
}); | |
const fractal = d3.component("g") | |
.render((selection, d) => { | |
const { | |
radius, | |
parentRadius, | |
angle, | |
children, | |
stemSize, | |
stemWeight, | |
rotateChildren | |
} = d; | |
const slices = pie(children); | |
arc.outerRadius(radius); | |
//const translate = (parentRadius + radius)/2 * stem; | |
//const translate = radius * stem; | |
const base = parentRadius + radius; | |
const stemDistance = base + ( | |
(parentRadius * stemWeight + radius * (1 - stemWeight)) / 2 | |
) * stemSize; | |
const degrees = angle / Math.PI * 180; | |
const fractals = slices | |
.filter(d => d.data.children) | |
.map(d => { | |
const parentRadius = radius; | |
const parentSliceFraction = (d.endAngle - d.startAngle) / (2*Math.PI); | |
const parentTotalArea = Math.PI * radius * radius; | |
const parentSliceArea = parentTotalArea * parentSliceFraction; | |
const childRadius = Math.sqrt(parentSliceArea / Math.PI); | |
return Object.assign({}, d.data, { | |
radius: childRadius, | |
parentRadius: radius, | |
angle: (d.startAngle + d.endAngle) / 2 - Math.PI/2, | |
stemSize, | |
stemWeight, | |
rotateChildren | |
}); | |
}); | |
selection | |
.attr("transform", `rotate(${degrees - 45}) translate(${stemDistance})`) | |
.call(connector, d, {stemDistance, rotateChildren, degrees}) | |
.call(slice, slices) | |
.call(fractal, fractals); | |
}); | |
const generateData = (value) => { | |
const epsilon = 0.0005; | |
const childRatios = [1/2, 1/4, 1/8, 1/8 * 2/3, 1/8 * 1/3]; | |
const d = { value }; | |
if(value > epsilon){ | |
d.children = childRatios | |
.map(ratio => ratio * value) | |
.map(generateData); | |
} | |
return d; | |
} | |
const data = generateData(1); | |
d3.select("svg").append("g") | |
.attr("transform", "translate(630, 780)") | |
.call(fractal, data, { | |
radius: 200, | |
parentRadius: 0, | |
angle: 0, | |
stemSize: 3.2, | |
// 1 = weighted by parent radius, | |
// 0 = weighted by child radius | |
stemWeight: 1.007155150848 | |
}); | |
</script> | |
</body> |
�PNG | |