public static void rotatingCaliper(Point[] P) { int n = P.length; Point[] L = new Point[P.length + 1]; Point[] U = new Point[P.length]; // 下凸包、上凸包 /* 尋找凸包,Andrew's Monotone Chain。 */ // 先排X座標、再排Y座標 // 按X从小到大,相等时Y从小大到 排序 Point tmp; for (int i = 0; i < n - 1; i++) { for (int j = i + 1; j < n; j++) { if ((P[i].x > P[j].x) || ((P[i].x == P[j].x) && (P[i].y > P[j].y))) { tmp = P[i]; P[i] = P[j]; P[j] = tmp; } } } int l = 0, u = 0; // 上凸包的點數、下凸包的點數 for (int i = 0; i < 10; ++i) { while (l >= 2 && Vector.direction(L[l - 2], L[l - 1], P[i]) <= 0) l--; while (u >= 2 && Vector.direction(U[u - 2], U[u - 1], P[i]) >= 0) u--; L[l++] = P[i]; U[u++] = P[i]; } /* 旋轉卡尺 */ // 下凸包額外補上一個凸包頂點,以便操作。 if (u - 2 >= 0) L[l] = U[u - 2]; System.out.println("l:" + l + " u:" + u); for (int i = 0, j = u - 1; i < l && j > 0; ) { System.out.println("夾到了頂點L[" + i + "]" + L[i] + "與頂點 U[" + j + "] " + U[j]); // 下方邊與上方邊的張開角度: // 小於180°,則上方前進。想像成下方很平、上方變凸。 // 大於180°,則下方前進。想像成上方很平、下方變凸。 // 等於180°,則同時前進,亦可擇一前進。 if (Vector.crossProduct(new Vector(L[i + 1], L[i]), new Vector(U[j - 1], U[j])) < 0) i++; // 下方前進 else j--; // 上方前進 } }
public static double angleBetweenPoints(Vector point, Vector target) { return target.subtract(point).angle(); }
public static double angleToVector(Vector v1, Vector v2) { return mod(v2.angle() - v1.angle()); }