Condition for angle range spanning 0

Hello. I am trying to write a condition that will test if the mouse heading is within an angle range, and I’m having trouble figuring out how to make it work for cases when the angle range spans 0.

The idea is that an arc is drawn in the segment where the mouse is between two points. I tried to simplify it as much as I could, while still making it easy to see what’s not working.

Segment[] segs;
PVector c, m, mo, mCenter;
PVector[] sPoints;
float[] sAngles;
float r, moHeading, sAngle, sAOffset;
int nPts, nSegments;
void setup() {
  size(400, 600);
  ellipseMode(RADIUS);
  m = new PVector();
  mCenter = new PVector();
  c = new PVector(width/2, height/2);
  r = 150;
  nSegments = 4;
  nPts = nSegments;
  sAngle = TWO_PI / nSegments;
  sAOffset = -PI/2 - sAngle/2;
  sAngles = new float[nSegments];
  sPoints = new PVector[nPts];
  for (int i = 0; i < sAngles.length; i++) {
    float a = sAOffset + i*sAngle;
    a = adjAngRange(a);
    sAngles[i] = a;
  }
  for (int i = 0; i < sPoints.length; i++) {
    float a = sAOffset + i*sAngle;
    a = adjAngRange(a);
    sPoints[i] = new PVector(c.x+cos(a)*r, c.y+sin(a)*r);
  }
  segs = new Segment[nSegments];
  for (int i=0; i<segs.length; i++) {
    segs[i] = new Segment();
  }
}
void draw() {
  background(127);
  m.set(mouseX, mouseY);

  mo = getMoffset(m, c);
  mo.limit(r);
  moHeading = getHeadingFromPt(mo, mCenter);
  moHeading = adjAngRange(moHeading);

  for (int i=0; i<sAngles.length; i++) {
    float startA = sAngles[i];
    float endA = sAngles[i] + sAngle;
    
    //////////////////
    if (moHeading > startA && moHeading < endA) {
      fill(255);
      arcPtR(c, r, startA, endA);
    }
    //////////////////
  }

  for (int i=0; i<sAngles.length; i++) {
    float a = sAngles[i];
    lineAngR(c, a, r);
  }

  for (int i=0; i<sPoints.length; i++) {
    String notationStr = nf(i, 1);
    textDot(notationStr, sPoints[i]);
  }

  linePtPt(c, PVector.add(c, mo));

  arrayToText(sAngles, moHeading);
}
PVector getMoffset(PVector a, PVector b) {
  return PVector.sub(a, b);
}
class Segment {
}
float adjAngRange(float a) {
  return (a < 0) ? TWO_PI + a : a;
}
float getMagFromPt(PVector magOf, PVector fromPt) {
  return dist(magOf.x, magOf.y, fromPt.x, fromPt.y);
}
float getHeadingFromPt(PVector headingOf, PVector fromPt) {
  PVector tempVec = PVector.sub(headingOf, fromPt);
  return tempVec.heading();
}
void arcPtR(PVector xy, float radius, float start, float end) {
  arc(xy.x, xy.y, radius, radius, start, end);
}
void linePtPt(PVector a, PVector b) {
  line(a.x, a.y, b.x, b.y);
}
void lineAngR(PVector a, float angle, float r) {
  line(a.x, a.y, a.x+cos(angle)*r, a.y+sin(angle)*r);
}
void circlePtR(PVector a, float radius) {
  ellipse(a.x, a.y, radius, radius);
}
void textDot(String s, PVector loc) {
  fill(255, 0, 0);
  circlePtR(loc, 15);
  fill(255);
  textSize(15);
  textAlign(CENTER, CENTER);
  text(s, loc.x, loc.y);
}
void arrayToText(float[] array, float angle) {
  textAlign(LEFT, TOP);
  String angleStr = nf(round(degrees(angle)), 3);
  text(angleStr, 5, 10);
  for (int i=0; i<array.length; i++) {
    String s = nf(i, 1) + ": " + nf(round(degrees(array[i])), 3);
    text(s, 5, (i*20)+30);
  }
}
1 Like

wouldn’t this be much easier?

int segments;
float stepSize;
float cx, cy;
void setup() {
  size(640, 480, P2D);
  segments = 4;
  stepSize = TWO_PI / segments;
  
  cx = width / 2;
  cy = height / 2;
}

void draw() {
  background(0);
  float currentSegment = floor(atan2(cy - mouseY, cx - mouseX) / stepSize) * stepSize;
  arc(cx, cy, 100, 100, PI + currentSegment, PI + currentSegment + stepSize);
}

edit: with a bit more flourish (showing the segments etc)

int segments;
float stepSize;
float r, r2;
float cx, cy;

void setup() {
  size(640, 480, P2D);
  segments = 8;
  stepSize = TWO_PI / segments;
  r = 100;
  r2 = r * 2;
  
  cx = width / 2;
  cy = height / 2;
}

void draw() {
  background(32);
  
  fill(192, 32, 32);
  noStroke();
  float currentSegment = floor(atan2(cy - mouseY, cx - mouseX) / stepSize) * stepSize;
  arc(cx, cy, r2, r2, PI + currentSegment, PI + currentSegment + stepSize);
  
  stroke(32, 32, 192);
  fill(255);
  float ex, ey, tw;
  for(int i = 0; i < segments; i++) {
    ex = cx + r * cos(i * stepSize);
    ey = cy + r * sin(i * stepSize);
    line(cx, cy, ex, ey);
    tw = textWidth("" + i);
    text(i, ex - tw / 4, ey);
  }
}
3 Likes

I will see if using floor(atan2 helps…

But at first try, this example doesn’t work for odd numbers of segments, and my segments will always be drawn so segment “0, 1” is centered at the top, and most of the time, there will be a segment spanning the 0 degree point.

you can do odd segment counts like so

int segments;
float stepSize;
float r, r2;
float cx, cy;
float offset;

void setup() {
  size(640, 480, P2D);
  segments = 9;
  stepSize = TWO_PI / segments;
  r = 100;
  r2 = r * 2;
  offset = segments % 2 == 0 ? PI : PI + PI / segments;
  cx = width / 2;
  cy = height / 2;
}

void draw() {
  background(32);
  
  fill(192, 32, 32);
  noStroke();
  float currentSegment = floor(atan2(cy - mouseY, cx - mouseX) / stepSize) * stepSize;
  arc(cx, cy, r2, r2, offset + currentSegment, offset + currentSegment + stepSize);
  
  stroke(32, 32, 192);
  fill(255);
  float ex, ey, tw;
  for(int i = 0; i < segments; i++) {
    ex = cx + r * cos(i * stepSize);
    ey = cy + r * sin(i * stepSize);
    line(cx, cy, ex, ey);
    tw = textWidth("" + i);
    text(i, ex - tw / 4, ey);
  }
}

as for the numbering well you can adjust that however you please easily enough.

1 Like

Thanks for helping me look at the problem in a different way.

I was just hoping I could find a way to check whether the mouse heading falls in between the range of two angles when for example angle 1 = 315, and angle 2 = 18.

Seems like this works, create a new variable for relative heading and subtract the start angle from it, then check if it’s between 0 and the segment width angle.

  for (int i=0; i<sAngles.length; i++) {
    float relHeading = moHeading - sAngles[i];
    relHeading = adjAngRange(relHeading);
    //////////////////
    if (relHeading > 0 && relHeading < sAngle) {
      fill(255);
      arcPtR(c, r, sAngles[i], sAngles[i] + sAngle);
    }
    //////////////////
  }

This sketch creates two functions to test whether an angle falls within a range, one using degrees and one using radians. Computers use radians so I suggest you use them and kick degrees int to long grass.

/*
Is an angle within a range.
 
 In this skecth all angles are in the range 0 - 360 drgrees inclusive or
 0 - 2 Pi radians inclusive
 
 if the start angle is greater than the end angle then the range straddles
 the 0 degree / radian position
 */

void setup() {
  testDegrees(45, 270, 350);
  testDegrees(60, 190, 34);
  testDegrees(60, 350, 90);
  testDegrees(90, 90, 120);
  println();
  testRadians(0.25 * PI, 1.5 * PI, 1.95 * PI);
  testRadians(0.33 * PI, 1.02 * PI, 0.29 * PI);
  testRadians(0.33 * PI, 1.95 * PI, 0.39 * PI);
  testRadians(0.5 * PI, 0.5 * PI, 1.33 * PI);
}

void testDegrees(float ang, float start, float end) {
  boolean withinRange = betweenAnglesDegrees(ang, start, end);
  print(ang + " degrees is ");
  print(withinRange ? "inside" : "outside");
  println(" the range " + start + " to " + end + " inclusive");
}

void testRadians(float ang, float start, float end) {
  boolean withinRange = betweenAnglesRadians(ang, start, end);
  print(ang + " radians is ");
  print(withinRange ? "inside" : "outside");
  println(" the range " + start + " to " + end + " inclusive");
}

boolean betweenAnglesDegrees(float ang, float start, float end) {
  if (end >= start) { // simple range
    return ang >= start && ang <= end;
  }
  // start - end angle go across zero position
  return ( (ang >= start && ang <= 360) || (ang >= 0 && ang <= end));
}

boolean betweenAnglesRadians(float ang, float start, float end) {
  if (end >= start) { // simple range
    return ang >= start && ang <= end;
  }
  // start - end angle go across zero position
  return ( (ang >= start && ang <= TWO_PI) || (ang >= 0 && ang <= end));
}
2 Likes

If you want tight code then change the methods to

boolean betweenAnglesDegrees(float ang, float start, float end) {
  return end >= start
    ? ang >= start && ang <= end
    : (ang >= start && ang <= 360) || (ang >= 0 && ang <= end);
}

boolean betweenAnglesRadians(float ang, float start, float end) {
  return end >= start
    ? ang >= start && ang <= end
    : (ang >= start && ang <= TWO_PI) || (ang >= 0 && ang <= end);
}

Does exactly the same thing as before.

3 Likes