package org.simantics.diagram.connection.splitting; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.util.ArrayList; import org.simantics.diagram.connection.RouteGraph; import org.simantics.diagram.connection.RouteLine; import org.simantics.diagram.connection.RouteNode; import org.simantics.diagram.connection.RoutePoint; import org.simantics.diagram.connection.RouteTerminal; import org.simantics.diagram.connection.segments.Segment; import gnu.trove.set.hash.THashSet; public class SplittedRouteGraph { public final RouteLine splitLine; public final THashSet interfaceNodes1; public final THashSet lines1; // does not contain splitLine public final THashSet terminals1; public final THashSet interfaceNodes2; public final THashSet lines2; // does not contain splitLine public final THashSet terminals2; public SplittedRouteGraph(RouteLine splitLine, THashSet interfaceNodes1, THashSet lines1, THashSet terminals1, THashSet interfaceNodes2, THashSet lines2, THashSet terminals2) { super(); this.splitLine = splitLine; this.interfaceNodes1 = interfaceNodes1; this.lines1 = lines1; this.terminals1 = terminals1; this.interfaceNodes2 = interfaceNodes2; this.lines2 = lines2; this.terminals2 = terminals2; } @Override public String toString() { StringBuilder b = new StringBuilder(); b.append("splitLine = " + splitLine.getData() + "\n"); b.append("interfaceNodes1 ="); for(RouteNode node : interfaceNodes1) b.append(" " + node.getData() + "(" + node.getClass().getSimpleName() + ")"); b.append("\n"); b.append("lines1 ="); for(RouteLine node : lines1) b.append(" " + node.getData()); b.append("\n"); b.append("terminals1 ="); for(RouteTerminal node : terminals1) b.append(" " + node.getData()); b.append("\n"); b.append("interfaceNodes2 ="); for(RouteNode node : interfaceNodes2) b.append(" " + node.getData() + "(" + node.getClass().getSimpleName() + ")"); b.append("\n"); b.append("lines2 ="); for(RouteLine node : lines2) b.append(" " + node.getData()); b.append("\n"); b.append("terminals2 ="); for(RouteTerminal node : terminals2) b.append(" " + node.getData()); b.append("\n"); return b.toString(); } public static RouteLine findNearestLine(RouteGraph rg, Point2D splitCanvasPos) { double hi = 1000, lo = 1; RouteLine nearestLine = null; final double LIMIT = 0.5; while (true) { double tolerance = (hi + lo) * 0.5; RouteLine line = rg.pickLine(splitCanvasPos.getX(), splitCanvasPos.getY(), tolerance); double delta = (hi - lo) * 0.5; if (delta < LIMIT) return nearestLine; if (line == null) { lo = tolerance; } else { nearestLine = line; hi = tolerance; } } } public static final class PickResult { /** * The connection route line nearest to {@link #pickPoint}. */ public final RouteLine nearestLine; /** * Original pick point in canvas coordinates. */ public final Point2D pickPoint; /** * Intersection point in canvas coordinates of {@link #nearestLine} and * perpendicular line from {@link #pickPoint} to {@link #nearestLine}. */ public final Point2D intersectionPoint; public PickResult(RouteLine nearestLine, Point2D pickPoint, Point2D intersectionPoint) { this.nearestLine = nearestLine; this.pickPoint = pickPoint; this.intersectionPoint = intersectionPoint; } } public static PickResult pickNearestLine(RouteGraph rg, double x, double y) { Segment nearestSegment = null; RouteLine nearestLine = null; ArrayList segments = new ArrayList<>(); double minDistanceSq = Double.MAX_VALUE; for (RouteLine line : rg.getAllLines()) { segments.clear(); line.collectSegments(segments); for (Segment segment : segments) { RoutePoint p1 = segment.p1; RoutePoint p2 = segment.p2; double distanceSq = Line2D.ptSegDistSq(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, y); if (distanceSq < minDistanceSq) { minDistanceSq = distanceSq; nearestSegment = segment; nearestLine = line; } } } if (nearestSegment == null) return null; RoutePoint p1 = nearestSegment.p1; RoutePoint p2 = nearestSegment.p2; Point2D p = pointToLineIntersection(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, y); return new PickResult(nearestLine, new Point2D.Double(x, y), p); } private static Point2D pointToLineIntersection(double x1, double y1, double x2, double y2, double px, double py) { double d = Math.pow(x2 - x1, 2.0) + Math.pow(y2 - y1, 2.0); if (d == 0) { return new Point2D.Double(x1, y1); } else { double u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / d; if (u > 1.0) { return new Point2D.Double(x2, y2); } else if (u <= 0.0) { return new Point2D.Double(x1, y1); } else { return new Point2D.Double(x2 * u + x1 * (1.0-u), (y2 * u + y1 * (1.0- u))); } } } /** * @param point * @param line * @return the specified point instance snapped to the specified line */ public static Point2D snapToLine(Point2D point, RouteLine line) { // line.print(System.out); // for (RoutePoint rp : line.getPoints()) // System.out.println("RP: " + rp.getX() + ", " + rp.getY()); // Get exact intersection point on the line if (line.isHorizontal()) { point.setLocation( point.getX(), line.getPosition() ); } else { point.setLocation( line.getPosition(), point.getY() ); } return point; } /** * @param point * @param line * @return new {@link Point2D} instance with specified point snapped to specified line */ public static Point2D snappedToLine(Point2D point, RouteLine line) { Point2D result = (Point2D) point.clone(); return snapToLine(result, line); } }