What I am trying for is how to get rid of “magicNumber”, by setting a pair of PVectors at the points where the arrow intersects with the yellow trimLine, no matter how “wide” the arrow head is (in a range that makes sense like 10deg to 60deg), or what the trimAngle is in relation to the startAngle (within a scaleFactor range of about 0.2 and 0.9.)
I used a bit of matrix transforms here to show more closely what I’m trying to do, but I would prefer to understand how to use math to locate A) the tangent line and B) the “trim” points so I can just draw the arrow lines between two PVectors without having to do matrix transforms.
I would like to be able to start at a PVector which here is startPoint, and draw the arrow lines at variable angles in relation to the arc’s tangent at startPoint, i.e. tangent +/-15deg, +/-45deg, +/-60deg, etc, (in this example I used PI/6) up to the points where they would intersect trimLine, without having to do a matrix translate/ rotate.
PVector c;
Arrow a1, a2;
float magicNumber = 50;
void setup() {
size(400, 600);
c = new PVector(width/2, height/2);
a1 = new Arrow(new PVector(c.x, c.y-150), 1, 2.25, 0.95);
a2 = new Arrow(new PVector(c.x, c.y+150), 32, 3, 0.3);
}
void draw() {
background(127);
ellipseMode(RADIUS);
a1.show(); // a very long arc, the arrow needs a larger trim angle
a2.show(); // a very short arc, the arrow needs a smaller trim angle
}
class Arrow {
PVector loc, startPoint, trimLine;
int n;
float d, r, theta, startAngle, trimAngle, arrowArcOffset, arrowScaleFactor;
Arrow(PVector loc, int n, float arrowArcOffset, float arrowScaleFactor) {
this.loc = loc;
d = 250;
r = d/2;
this.n = n;
// the arc length will change to fractions of a circle
// based on the number of divisions
theta = TWO_PI/n;
// startAngle is where the arrow point is,
// the offset keeps it closer to the beginning of the arc
// to make room for another arrow
// at the end of the arc which will
// point in the opposite direction
startAngle = theta/arrowArcOffset;
// trimAngle is the angle where the arrow head
// lines should be trimmed to,
// arrowScaleFactor will vary so that
// the arrow head looks normal
// whether the arc length is long or very short
trimAngle = startAngle*arrowScaleFactor;
startPoint = new PVector(loc.x+cos(startAngle)*r, loc.y+sin(startAngle)*r);
trimLine = new PVector(loc.x+cos(trimAngle)*r, loc.y+sin(trimAngle)*r);
}
void show() {
drawCircle();
drawArc();
drawStartLine();
drawArrowArc();
drawTangent();
drawArrow();
drawTrimLine();
}
void drawCircle() {
noFill();
stroke(200);
strokeWeight(5);
ellipse(loc.x, loc.y, r, r);
}
void drawArc() {
noFill();
stroke(200, 0, 0);
strokeWeight(1);
arc(loc.x, loc.y, r, r, 0, theta);
}
void drawArrowArc() {
strokeWeight(2);
stroke(0, 200, 0);
arc(loc.x, loc.y, r, r, 0, startAngle);
}
void drawStartLine() {
strokeWeight(1);
stroke(50);
line(loc.x, loc.y, startPoint.x, startPoint.y);
}
void drawTrimLine() {
strokeWeight(1);
stroke(225, 225, 0);
line(loc.x, loc.y, trimLine.x, trimLine.y);
line(trimLine.x, trimLine.y, loc.x+cos(trimAngle)*(r+50), loc.y+sin(trimAngle)*(r+50));
}
void drawTangent() {
strokeWeight(1);
stroke(50);
pushMatrix();
translate(startPoint.x, startPoint.y);
rotate(startAngle-HALF_PI);
line(0, 0, 200, 0);
popMatrix();
}
void drawArrow() {
strokeWeight(2);
stroke(0, 200, 0);
pushMatrix();
translate(startPoint.x, startPoint.y);
pushMatrix();
rotate(startAngle-HALF_PI-PI/6); // startAngle-HALF_PI is tangent
line(0, 0, magicNumber, 0);
popMatrix();
pushMatrix();
rotate(startAngle-HALF_PI+PI/6); // startAngle-HALF_PI is tangent
line(0, 0, magicNumber, 0);
popMatrix();
popMatrix();
}
}