1
votes

I have a circle with a rotation. See images below for example. The circle is divided into segments of varying degrees, for this example I've divided the circle into three equal 120 degree segments.

Given a point of impact (a point on the exterior radius of the circle) I calculate the degree between the center of the circle and the point of impact. I then need to determine which segment was impacted.

enter image description here

My current solution went something like this:

var circleRotation = 270;
var segments = [120, 120, 120];
function segmentAtAngle(angle) {
    var sumTo = circleRotation;
    for (var i = 0, l = segments.length; l > i; i++) {
        if (sumTo <= angle && sumTo + segments[i] >= angle) {
            // return the segment
            return i;
        }
        sumTo += segments[i];
    }
}

My solution does not work in all cases, given a large offset of say 270 and when requesting the segment at impact degree 45 I currently faultily provide nothing.

Note: Provided angle to segmentAtAngle and circleRotation will also never be negative or above 360. I standardize the degrees by { degrees = degrees % 360; if (degrees < 0) degrees += 360; return degrees; }

What would be the proper way to calculate the hit segment of a circle given an offset rotation?

3

3 Answers

0
votes

A simple ad-hoc solution would be duplicating your lists of segments. Then you have the whole range from 0° to 2·360°=720° covered. If angle and circleRotation is between 0° and 360°, as you say they are, then their sum will be between 0° and 720°, and having twice the list of segments will yield a match in all cases. If the resulting index is greater or equal to the length of the original unduplicated list, you can subtract that length to obtain an index from that original list.

0
votes

First, the conditions of your for loop looks kind of weird. l will always be larger than zero, so the loop will never execute at all. Secondly, you should probably standardize sumTo each time you add to it. Third, you return angle within the loop, which never changes. Do you want to return the index of the impacted segment?

var circleRotation = 270;
var segments = [120, 120, 120];
function standardize(degrees){
    degrees = degrees % 360; 
    if (degrees < 0) degrees += 360; 
    return degrees;
}
function segmentAtAngle(angle) {
    var sumTo = circleRotation;
    for (var i = 0; i<segments.length; i++) {
        if (sumTo <= angle && sumTo + segments[i] >= angle) {
            return i;
        }
        sumTo = standardize(sumTo + segments[i]);
    }
}
0
votes

The function atan2(DY, DX) will give you the angle from the center to any point. This angle will be in range -pi to +pi. For the sake of the discussion, let us convert this to the -180..+180° range.

Now consider the delimiting angles of your segments, as if obtained by the same function: they will correspond to the ranges [-120..0], [0..120] and [120, -120]. All is fine, except that the third interval straddles the discontinuity, and it should be split into [120..180] and [-180..-120].

In the end, you should consider this list of bounds, with corresponding sectors:

   -180    -120      0      120     180
      Yellow |  Red  | Green | Yellow

With N colors, you will need to consider N+1 intervals and compare to N bounds (no need to check against the extreme values, they are implicitly fulfilled). You will do this by linear or dichotomic search (or simple rescaling in case of equidistant bounds).