I'm working on a new project with d3 creating a graph that shows a score from 0 to 10. The data looks like this:
var data = [
{axis: 'People', value: getRandomScore()},
{axis: 'Leadership', value: getRandomScore()},
{axis: 'Resources', value: getRandomScore()},
{axis: 'Processes', value: getRandomScore()},
{axis: 'Strategy', value: getRandomScore()},
{axis: 'Culture', value: getRandomScore()},
];
getRandomScore is a method that returns an random int from 0 to 10.
Here is the result that I managed to achieve:
And here is the artwork:
The problem is that d3 doesn't have an option to make the path stroke(that goes through dots) to curve inside to make the path look star shaped like in the artwork and I need some help creating a function that will do that inside curve thing.
You don't specify how you are drawing so in my answer I'll assume it's a variant of a polar plot using d3.svg.line.radial
. The easiest way to get the look you are after is to insert "fake" points in between your existing ones to force the interpolation inward.
Say your points lie on the circular polar as (x is in radians):
var data = [
[Math.PI / 3, someValue],
[0 * Math.PI, someValue],
[(5 * Math.PI / 3), someValue],
[(4 * Math.PI / 3), someValue],
[Math.PI, someValue],
[(2 * Math.PI) / 3, someValue]
];
Then using the midpoints of those radians:
var midPoints = [Math.PI / 6, (11 * Math.PI) / 6,
(3 * Math.PI) / 2, (7 * Math.PI) / 6,
(5 * Math.PI) / 6, Math.PI / 2];
pD = [];
midPoints.forEach(function(d,i){
var i2 = (i === 5) ? 0 : i + 1;
var midY = d3.min([data[i][1],data[i2][1]]) / 2; // find the min of the two neighboring points and the "inner" fake to half the min
pD.push(data[i]);
pD.push([d, midY]);
});
I'm setting my inner fake point to be half the min of the two neighboring points. You can adjust this to get the effect you desire.
Here's complete working code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.frame {
fill: none;
stroke: #000;
}
.axis text {
font: 10px sans-serif;
}
.axis line,
.axis circle {
fill: none;
stroke: steelblue;
stroke-dasharray: 4;
}
.axis:last-of-type circle {
stroke: steelblue;
stroke-dasharray: none;
}
.line {
fill: none;
stroke: orange;
stroke-width: 3px;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var width = 500,
height = 500,
radius = Math.min(width, height) / 2 - 30;
var r = d3.scale.linear()
.domain([0, 2])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {
return r(d[1]);
})
.angle(function(d) {
return -d[0] + Math.PI / 2;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var gr = svg.append("g")
.attr("class", "r axis")
.selectAll("g")
.data(r.ticks(3).slice(1))
.enter().append("g");
gr.append("circle")
.attr("r", r);
var ga = svg.append("g")
.attr("class", "a axis")
.selectAll("g")
.data(d3.range(0, 360, 60))
.enter().append("g")
.attr("transform", function(d) {
return "rotate(" + -d + ")";
});
ga.append("line")
.attr("x2", radius);
var line = d3.svg.line.radial()
.radius(function(d) {
return r(d[1]);
})
.angle(function(d) {
return -d[0] + Math.PI / 2;
})
.interpolate("cardinal-closed");
var data = [
[Math.PI / 3, Math.random() + 1],
[0 * Math.PI, Math.random() + 1],
[(5 * Math.PI / 3), Math.random() + 1],
[(4 * Math.PI / 3), Math.random() + 1],
[Math.PI, Math.random() + 1],
[(2 * Math.PI) / 3, Math.random() + 1]
]
var midPoints = [Math.PI / 6, (11 * Math.PI) / 6,
(3 * Math.PI) / 2, (7 * Math.PI) / 6,
(5 * Math.PI) / 6, Math.PI / 2];
pD = [];
midPoints.forEach(function(d,i){
var i2 = (i === 5) ? 0 : i + 1;
var midY = d3.min([data[i][1],data[i2][1]]) / 2;
pD.push(data[i]);
pD.push([d, midY]);
});
svg.selectAll("point")
.data(data)
.enter()
.append("circle")
.attr("class", "point")
.attr("transform", function(d) {
var coors = line([d]).slice(1).slice(0, -1);
return "translate(" + coors + ")"
})
.attr("r", 8)
.attr("fill", "steelblue");
svg.append("path")
.datum(pD)
.attr("class", "line")
.attr("d", line);
</script>