// License: GPL. For details, see LICENSE file. package buildings_tools; import static buildings_tools.BuildingsToolsPlugin.latlon2eastNorth; import java.util.TreeSet; import org.openstreetmap.josm.data.coor.EastNorth; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.tools.Pair; public class AngleSnap { private static final double PI_2 = Math.PI / 2; final TreeSet<Double> snapSet = new TreeSet<>(); public final void clear() { snapSet.clear(); } public final void addSnap(double snap) { snapSet.add(snap % PI_2); } public final Double addSnap(Node[] nodes) { if (nodes.length == 2) { EastNorth p1, p2; p1 = latlon2eastNorth(nodes[0].getCoor()); p2 = latlon2eastNorth(nodes[1].getCoor()); double heading = p1.heading(p2); addSnap(heading); addSnap(heading + Math.PI / 4); return heading; } else { return null; } } public final void addSnap(Way way) { for (Pair<Node, Node> pair : way.getNodePairs(false)) { EastNorth a, b; a = latlon2eastNorth(pair.a.getCoor()); b = latlon2eastNorth(pair.b.getCoor()); double heading = a.heading(b); addSnap(heading); } } public final Double getAngle() { if (snapSet.isEmpty()) { return null; } double first = snapSet.first(); double last = snapSet.last(); if (first < Math.PI / 4 && last > Math.PI / 4) { last -= PI_2; } if (Math.abs(first - last) < 0.001) { double center = (first + last) / 2; if (center < 0) center += PI_2; return center; } else { return null; } } public final double snapAngle(double angle) { if (snapSet.isEmpty()) { return angle; } int quadrant = (int) Math.floor(angle / PI_2); double ang = angle % PI_2; Double prev = snapSet.floor(ang); if (prev == null) prev = snapSet.last() - PI_2; Double next = snapSet.ceiling(ang); if (next == null) next = snapSet.first() + PI_2; if (Math.abs(ang - next) > Math.abs(ang - prev)) { if (Math.abs(ang - prev) > Math.PI / 8) { return angle; } else { double ret = prev + PI_2 * quadrant; if (ret < 0) ret += 2 * Math.PI; return ret; } } else { if (Math.abs(ang - next) > Math.PI / 8) { return angle; } else { double ret = next + PI_2 * quadrant; if (ret > 2 * Math.PI) ret -= 2 * Math.PI; return ret; } } } }