120 lines
3.6 KiB
Java
120 lines
3.6 KiB
Java
import java.awt.geom.Line2D;
|
|
import java.awt.geom.Point2D;
|
|
import java.util.Arrays;
|
|
import java.util.Random;
|
|
|
|
public class AngleAreaOrientationSortRotationPerpendicular {
|
|
|
|
// pay attention to case ax==0 && ay==0 or bx==0 && by == 0
|
|
public static double angleBetween(long ax, long ay, long bx, long by) {
|
|
double a = Math.atan2(ax * by - ay * bx, ax * bx + ay * by);
|
|
return a < 0 ? a + 2 * Math.PI : a;
|
|
}
|
|
|
|
// pay attention to case ax==0 && ay==0 or bx==0 && by == 0
|
|
public static double angleBetween2(long ax, long ay, long bx, long by) {
|
|
double a = Math.atan2(by, bx) - Math.atan2(ay, ax);
|
|
return a < 0 ? a + 2 * Math.PI : a;
|
|
}
|
|
|
|
public static long doubleSignedArea(int[] x, int[] y) {
|
|
int n = x.length;
|
|
long area = 0;
|
|
for (int i = 0, j = n - 1; i < n; j = i++) {
|
|
area += (long) (x[i] - x[j]) * (y[i] + y[j]);
|
|
// area += (long) x[i] * y[j] - (long) x[j] * y[i];
|
|
}
|
|
return area;
|
|
}
|
|
|
|
// Returns -1 for clockwise, 0 for straight line, 1 for counterclockwise orientation
|
|
public static int orientation(long ax, long ay, long bx, long by, long cx, long cy) {
|
|
bx -= ax;
|
|
by -= ay;
|
|
cx -= ax;
|
|
cy -= ay;
|
|
return Long.signum(bx * cy - by * cx);
|
|
}
|
|
|
|
public static boolean isMiddle(long a, long m, long b) {
|
|
return Math.min(a, b) <= m && m <= Math.max(a, b);
|
|
}
|
|
|
|
public static boolean isMiddle(long ax, long ay, long mx, long my, long bx, long by) {
|
|
return orientation(ax, ay, mx, my, bx, by) == 0 && isMiddle(ax, mx, bx) && isMiddle(ay, my, by);
|
|
}
|
|
|
|
public static class Point implements Comparable<Point> {
|
|
int x;
|
|
int y;
|
|
|
|
public Point(int x, int y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
|
|
@Override
|
|
public int compareTo(Point o) {
|
|
boolean up1 = y > 0 || (y == 0 && x >= 0);
|
|
boolean up2 = o.y > 0 || (o.y == 0 && o.x >= 0);
|
|
if (up1 != up2) return up1 ? -1 : 1;
|
|
int cmp = Long.signum((long) o.x * y - (long) o.y * x);
|
|
if (cmp != 0) return cmp;
|
|
return Long.compare((long) x * x + (long) y * y, (long) o.x * o.x + (long) o.y * o.y);
|
|
//return Double.compare(Math.atan2(y, x), Math.atan2(o.y, o.x));
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "(" + x + ',' + y + ')';
|
|
}
|
|
}
|
|
|
|
public Point2D.Double rotateCCW(Point2D.Double p, double angle) {
|
|
return new Point2D.Double(p.x * Math.cos(angle) - p.y * Math.sin(angle), p.x * Math.sin(angle) + p.y * Math.cos(angle));
|
|
}
|
|
|
|
public Line perpendicular(Line line, long x, long y) {
|
|
return new Line(-line.b, line.a, line.b * x - line.a * y);
|
|
}
|
|
|
|
public static class Line {
|
|
long a, b, c;
|
|
|
|
public Line(long a, long b, long c) {
|
|
this.a = a;
|
|
this.b = b;
|
|
this.c = c;
|
|
}
|
|
}
|
|
|
|
// random test
|
|
public static void main(String[] args) {
|
|
Random rnd = new Random(1);
|
|
int range = 100;
|
|
for (int step = 0; step < 100_000; step++) {
|
|
long ax = rnd.nextInt(range) - range / 2;
|
|
long ay = rnd.nextInt(range) - range / 2;
|
|
long bx = rnd.nextInt(range) - range / 2;
|
|
long by = rnd.nextInt(range) - range / 2;
|
|
long cx = rnd.nextInt(range) - range / 2;
|
|
long cy = rnd.nextInt(range) - range / 2;
|
|
int orientation1 = orientation(ax, ay, bx, by, cx, cy);
|
|
int orientation2 = -Line2D.relativeCCW(ax, ay, bx, by, cx, cy);
|
|
if (orientation1 == 0)
|
|
continue;
|
|
if (orientation1 != orientation2)
|
|
throw new RuntimeException();
|
|
if (ax == 0 && ay == 0 || bx == 0 && by == 0)
|
|
continue;
|
|
double res1 = angleBetween(ax, ay, bx, by);
|
|
double res2 = angleBetween2(ax, ay, bx, by);
|
|
if (!(Math.abs(res1 - res2) < 1e-9))
|
|
throw new RuntimeException();
|
|
}
|
|
Point[] points = new Point[]{new Point(1, 1), new Point(1, -1), new Point(0, 0)};
|
|
Arrays.sort(points);
|
|
System.out.println(Arrays.toString(points));
|
|
}
|
|
}
|