what I have done so far is:
I have already calculated the centroid of each path, and I want to calculate the signed angle between segments (centroid, path[0]) and (centroid, path[1])). when using anglebetween() I get an absolute value, but I want the sign to know if it is counter clockwise or clockwise.
the idea is to reverse the order of the path[vertices] array when I detect the “wrong” direction.
is there an easiest way or a simple command/library for that
thanx !
If the angle goes over 180, then to find wether the angle is clockwise of counterclockwise is easy. If it‘s over 180, then it‘s the other way around. (Though i don‘t know in which direction it goes with smaller angle).
But that could give a wrong result, if the shape starts out counterclockwise, but then goes clockwise, like the star one would do.
Therefore you‘ll probably want to calculate the total of all paths minus the last one, just to see wether the total is over 0 or under it. (Since the total of all should equal 0).
well I have a working solution but I am not sure it is the most elegant way to do it
void setDir(PVector[] Liste){ // Liste is an arraye of the vertices of the path
PVector bary,v0,v1,v2;
PVector[] listy;
int leng=Liste.length;
listy= new PVector[leng]; // lesty is a temporary Array to store new values
bary = Centroid(Liste); // calculating the centroid of the path (barycentre)
v0=PVector.sub(bary,Liste[0]); // not used here
v1=PVector.sub(bary,Liste[1]);
v2=PVector.sub(Liste[1],Liste[0]);
v2.rotate(PI/2);
float dot=PVector.dot(v1,v2);
for(int kk=0;kk<leng;kk++){
listy[leng-kk-1]=Liste[kk];
}
if(dot<0){
for(int kk=0;kk<leng;kk++){
Liste[kk]=listy[kk];
}
}
}
since I already have the centroid of each path, I calculated the vector between the centroid and one vertex, then a I calculated the perpenducal vector the first segment, and used the dot product to detect direction, and reversed the array of vertices!
by the way is there a command to find a normal to 2d vector and an other to reverse a PVector array ?
Hi @jeykech, I’ve put in some comments to explain. Note, that the script below is a bit different from the originally posted, because I found that it matches better to the stackoverflow discussion that way.
PVector[] points = new PVector[5];
void setup() {
size(100, 100);
// just for testing: some points
points[0] = new PVector(5, 0);
points[1] = new PVector(6, 4);
points[2] = new PVector(4, 5);
points[3] = new PVector(1, 5);
points[4] = new PVector(1, 0);
// the function isClockwise() returns true, if the polygon is clockwise, so reverse if not
if (!isClockwise(points)) {
// reversing an array could be done by swapping the first with the last entry, the second with the second last, ....
// so just go throught the entries until the middle of the array
for (int i = 0; i < points.length / 2; i++) {
// these lines are swapping two entries
PVector temp = points[i];
points[i] = points[points.length - i - 1];
points[points.length - i - 1] = temp;
}
}
println(points);
}
// the function isClockwise calculates the signed area of a polygon
boolean isClockwise(PVector[] vertices) {
// the algorithm uses this formula to sum up the area:
// https://en.wikipedia.org/wiki/Shoelace_formula
// start with 0
float area = 0;
// go through all the points of the polygon
for (int i = 0; i < vertices.length; i++) {
// index i is the actual point. index j is the next point.
int j = (i + 1) % vertices.length;
// this is the main part of the shoelace formula:
area -= vertices[i].x * vertices[j].y;
area += vertices[j].x * vertices[i].y;
// another way to calculate the signed area:
// area += (vertices[j].x - vertices[i].x) * (vertices[j].y + vertices[i].y);
}
// if area is smaller than 0 it returns true, otherwise false.
return area < 0;
}
One general purpose method is to write a Java Comparator, which compares any two point headings and returns a greater or less measure. Then any PVector[] can be sorted by heading with Arrays.sort().
// Sort PVector Array
// 2019-11-12 Processing 3.4
// based on PointsVecArray from Toolboxing
import java.util.Arrays;
import java.util.Comparator;
PVector[] pts;
PVector ctr;
void setup(){
// make some random points on a circle in shuffled order
pts = new PVector[6];
PVector spinner = new PVector(width/3, 0);
for(int i=0; i<pts.length; i++){
pts[i] = spinner.rotate(random(TWO_PI)).copy();
}
// sort the points clockwise
pts = pheadsort(pts);
}
void draw() {
background(128);
translate(width/2,height/2);
ellipse(0,0,5,5);
// draw lines
for(int i=0; i<pts.length; i++){
int i2 = (i+1)%pts.length;
line(pts[i].x, pts[i].y, pts[i2].x, pts[i2].y);
}
// draw labeled points
for(int i=0; i<pts.length; i++){
ellipse(pts[i].x, pts[i].y, 3, 3);
text(i, pts[i].x, pts[i].y);
}
}
// Sort a list of PVectors by heading.
// You may want to first center the points around 0,0.
PVector[] pheadsort(PVector[] pts) {
/**
* h comparator: sort vectors by heading (angle of rotation from origin)
*/
Comparator<PVector> VEC_CMP_HEAD = new Comparator<PVector>() {
@ Override public final int compare(final PVector a, final PVector b) {
return Float.compare(a.heading(), b.heading());
}
};
// do the sort
Arrays.sort(pts, VEC_CMP_HEAD);
return pts;
}