Reputation: 13
How would one attempt to get a random coordinate within an existing arc?
I am currently rendering a piechart from user data and would like to render a number of points on each arc at random locations - they may be outside of their arc, however
At the moment, I am using a random deviation (within a certain range) from the centroid of each arc. This approach is problematic as arcs might be too small and points end up outside of their arc.
I can't really provide any example code at the moment as I am really just rendering a pie chart with five slices so far.
Upvotes: 1
Views: 322
Reputation: 13129
I've used this example as a starting point. What I did was generate 10 times 2 numbers for every arc: a distance from the centre and an angle (in radians). Then I plotted the circles using those values.
To show that it works, I made the radius constant, so you see a circle of black dots. If you want, you can use the commented out line to make that random as well.
Note how the circles are the same colour as the arcs.
I also had to subtract Math.PI / 2
, because of the different zero points between degrees and radians:
-Math.PI / 2
radians is a vertical line to the top.const data = [{
"region": "North",
"count": "53245"
},
{
"region": "South",
"count": "28479"
},
{
"region": "East",
"count": "19697"
},
{
"region": "West",
"count": "24037"
},
{
"region": "Central",
"count": "40245"
}
];
const width = 360;
const height = 360;
const radius = Math.min(width, height) / 2;
const svg = d3.select("#chart-area")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", `translate(${width / 2}, ${height / 2})`);
const color = d3.scaleOrdinal(["#66c2a5", "#fc8d62", "#8da0cb",
"#e78ac3", "#a6d854", "#ffd92f"
]);
const pie = d3.pie()
.value(d => d.count)
.sort(null);
const arc = d3.arc()
.innerRadius(0)
.outerRadius(radius);
// Join new data
const path = svg.selectAll("path")
.data(pie(data));
// Enter new arcs
path.enter().append("path")
.attr("fill", (d, i) => color(i))
.attr("d", arc)
.attr("stroke", "white")
.attr("stroke-width", "6px")
.each(drawPoints);
function drawPoints(d, i) {
// Generate random numbers (x, y) where x between startAngle
// and endAngle
// and y between 0 and radius
const points = new Array(10).fill(undefined).map(() => ({
angle: d.startAngle + Math.random() * (d.endAngle - d.startAngle) - Math.PI / 2,
//radius: Math.random() * radius,
radius: radius / 2,
}));
svg.selectAll(`.point-${i}`)
.data(points)
.enter()
.append('circle')
.attr('class', `point point-${i}`)
.attr("fill", (d) => color(i))
.attr('stroke', 'black')
.attr('stroke-width', '2px')
.attr('cx', (d) => d.radius * Math.cos(d.angle))
.attr('cy', (d) => d.radius * Math.sin(d.angle))
.attr('r', 3)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.js"></script>
<div id="chart-area"></div>
Upvotes: 3