// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.turnlanes.gui;
import static java.lang.Math.PI;
import static java.lang.Math.abs;
import static java.lang.Math.min;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.coor.EastNorth;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.plugins.turnlanes.model.Junction;
final class GuiUtil {
private GuiUtil() {
// Hide default constructor for utilities classes
}
static double normalize(double a) {
while (a < 0) {
a += 2 * Math.PI;
}
while (a > 2 * Math.PI) {
a -= 2 * Math.PI;
}
return a;
}
// control point factor for curves (circle segment of angle a)
static double cpf(double a, double scale) {
return 4.0 / 3 * Math.tan(min(abs(a), PI - 0.001) / 4) * scale;
}
static Point2D intersection(Line2D a, Line2D b) {
final double aa = GuiUtil.angle(a);
final double ab = GuiUtil.angle(b);
// less than 1/2 degree => no intersection
if (Math.abs(Math.PI - abs(minAngleDiff(aa, ab))) < PI / 360) {
return null;
}
final double d = (a.getX1() - a.getX2()) * (b.getY1() - b.getY2()) - (a.getY1() - a.getY2())
* (b.getX1() - b.getX2());
final double x = ((b.getX1() - b.getX2()) * (a.getX1() * a.getY2() - a.getY1() * a.getX2()) - (a.getX1() - a
.getX2()) * (b.getX1() * b.getY2() - b.getY1() * b.getX2()))
/ d;
final double y = ((b.getY1() - b.getY2()) * (a.getX1() * a.getY2() - a.getY1() * a.getX2()) - (a.getY1() - a
.getY2()) * (b.getX1() * b.getY2() - b.getY1() * b.getX2()))
/ d;
return new Point2D.Double(x, y);
}
static Point2D closest(Line2D l, Point2D p) {
final Point2D lv = vector(l.getP1(), l.getP2());
final double numerator = dot(vector(l.getP1(), p), lv);
if (numerator < 0) {
return l.getP1();
}
final double denominator = dot(lv, lv);
if (numerator >= denominator) {
return l.getP2();
}
final double r = numerator / denominator;
return new Point2D.Double(l.getX1() + r * lv.getX(), l.getY1() + r * lv.getY());
}
private static double dot(Point2D a, Point2D b) {
return a.getX() * b.getX() + a.getY() * b.getY();
}
private static Point2D vector(Point2D from, Point2D to) {
return new Point2D.Double(to.getX() - from.getX(), to.getY() - from.getY());
}
public static double angle(Point2D from, Point2D to) {
final double dx = to.getX() - from.getX();
final double dy = -(to.getY() - from.getY());
return normalize(Math.atan2(dy, dx));
}
public static Point2D relativePoint(Point2D p, double r, double a) {
return new Point2D.Double(
p.getX() + r * Math.cos(a),
p.getY() - r * Math.sin(a)
);
}
public static Line2D relativeLine(Line2D l, double r, double a) {
final double dx = r * Math.cos(a);
final double dy = -r * Math.sin(a);
return new Line2D.Double(
l.getX1() + dx,
l.getY1() + dy,
l.getX2() + dx,
l.getY2() + dy
);
}
public static double angle(Line2D l) {
return angle(l.getP1(), l.getP2());
}
public static double minAngleDiff(double a1, double a2) {
final double d = normalize(a2 - a1);
return d > Math.PI ? -(2 * Math.PI - d) : d;
}
public static Point2D middle(Point2D a, Point2D b) {
return relativePoint(a, a.distance(b) / 2, angle(a, b));
}
public static Point2D middle(Line2D l) {
return middle(l.getP1(), l.getP2());
}
public static Line2D line(Point2D p, double a) {
return new Line2D.Double(p, relativePoint(p, 1, a));
}
public static Point2D loc(Node node) {
final EastNorth loc = Main.getProjection().latlon2eastNorth(node.getCoor());
return new Point2D.Double(loc.getX(), -loc.getY());
}
public static List<Point2D> locs(Iterable<Junction> junctions) {
final List<Point2D> locs = new ArrayList<>();
for (Junction j : junctions) {
locs.add(loc(j.getNode()));
}
return locs;
}
static void area(Path2D area, Path inner, Path outer) {
area.append(inner.getIterator(), false);
area.append(ReversePathIterator.reverse(outer.getIterator()), true);
area.closePath();
}
}