I want to make a series of buttons where every button is like the slice of a pie. The Juce logo is a good example of what I’m looking for (where you can click on each section as a diffierent button). I also saw that @eyalamir made something similar in Beat Scholar–this is pretty much exactly what I want to do.
I can sort of imagine how to do it using AffineTransform and hitTest. But I’ve never worked with radial geometry before, and I’m worried that what I would come up with would be very convoluted. Can anyone share tips or some sample code so that I can make sure I’m on the right path?
It is pretty involved and I don’t have any public code to share with you - these are really things that are in the core of our product and not things that are public.
JUCE does have some math helpers that could help you with the math part like point.getPointOnCircumference().
As for HitTest - that could maybe work but we don’t use it for other reasons.
For example, when we have complicated drag interactions with the different circle slices where you have gestures dragging across a few circles. In those cases, we can’t allow a single circle or circle slice to take over the mouseEvent.
The simple way is to convert to polar coordinates and test if the radius is outside the bounds of the containing circle, and calculate which slice of the pie the angle lies in otherwise. Here’s a quick example (untested, but I think this is correct but you might want to run and double check the boundary conditions to be sure). There might be a bug if you hit exactly (0, y).
/// Given a pie with N slices, get the index of the slice that the point (x, y) is
/// contained within assuming slices are oriented with slice 0 between the angles
/// [0, 2pi/N) and the radius of the circle is 1.0.
std::optional<int> getSliceForCoordinate(float x, float y, int N)
{
auto twoPi = 2.0f * 3.141592653589793f;
// Convert to polar.
auto R = sqrtf(x * x + y * y);
auto theta = atan2(y, x);
// If the radius is outside the circle, return none.
if (R >= 1.0f)
{
return {};
}
// Get the slice that the angle resides in.
auto n = floorf(static_cast<float>(N) * (theta / twoPi));
// Return the result.
return std::make_optional(static_cast<int>(n));
}
If you want to make the circle bigger then adjust the radius test. If you want to rotate it so the slices are oriented differently you have a few options, from rotating the input coordinates using a transform, adding an offset to the calculation and using remainder to keep it in the range [0, 2pi], etc.
It helps to draw a picture and do the math by hand, and write some tests for the function.