/******************************************************************************* * Copyright (c) 2011 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.diagram.connection; /** * An internal utility class for routing simple connections * (a connection with two terminals without any route lines). * * @author Hannu Niemistö */ public class SimpleConnectionUtility { public static boolean allowsDirection(RouteTerminal a, int dir) { return Directions.isAllowed(a.getAllowedDirections(), dir); } public static final int DIRECT_HORIZONTAL_CONNECTION = 0; public static final int DIRECT_VERTICAL_CONNECTION = 1; public static final int ONE_BEND_HORIZONTAL_VERTICAL = 2; public static final int ONE_BEND_VERTICAL_HORIZONTAL = 3; public static final int MORE_BENDS_BBS_DONT_INTERSECT = 4; public static final int MORE_BENDS_BBS_INTERSECT = 5; public static final int COMPLEX_CONNECTION = 6; public static int simpleConnectionCase(RouteTerminal a, RouteTerminal b) { if(a.isRouteToBounds() && b.isRouteToBounds()) return simpleConnectionCaseRouteToBounds(a, b); // Can connect terminals by one straight line? if(a.y == b.y) { if(a.x < b.x) { if(allowsDirection(a, 0) && allowsDirection(b, 2)) return DIRECT_HORIZONTAL_CONNECTION; } else { if(allowsDirection(a, 2) && allowsDirection(b, 0)) return DIRECT_HORIZONTAL_CONNECTION; } } else if(a.x == b.x) { if(a.y < b.y) { if(allowsDirection(a, 1) && allowsDirection(b, 3)) return DIRECT_VERTICAL_CONNECTION; } else { if(allowsDirection(a, 3) && allowsDirection(b, 1)) return DIRECT_VERTICAL_CONNECTION; } } // Can connect terminals by two lines? if(a.x < b.x) { if(a.y < b.y) { if(allowsDirection(a, 0) && allowsDirection(b, 3) /*&& b.x >= a.getMaxX() && a.y <= b.getMinY()*/) return ONE_BEND_HORIZONTAL_VERTICAL; else if(allowsDirection(a, 1) && allowsDirection(b, 2) /*&& b.y >= a.getMaxY() && a.x <= b.getMinX()*/) return ONE_BEND_VERTICAL_HORIZONTAL; } else { if(allowsDirection(a, 0) && allowsDirection(b, 1) /*&& b.x >= a.getMaxX() && a.y >= b.getMaxY()*/) return ONE_BEND_HORIZONTAL_VERTICAL; else if(allowsDirection(a, 3) && allowsDirection(b, 2) /*&& b.y <= a.getMinY() && a.x <= b.getMinX()*/) return ONE_BEND_VERTICAL_HORIZONTAL; } } else { if(a.y < b.y) { if(allowsDirection(a, 2) && allowsDirection(b, 3) /*&& b.x <= a.getMinX() && a.y <= b.getMinY()*/) return ONE_BEND_HORIZONTAL_VERTICAL; else if(allowsDirection(a, 1) && allowsDirection(b, 0) /*&& b.y >= a.getMaxY() && a.x >= b.getMaxX()*/) return ONE_BEND_VERTICAL_HORIZONTAL; } else { if(allowsDirection(a, 2) && allowsDirection(b, 1) /*&& b.x <= a.getMinX() && a.y >= b.getMaxY()*/) return ONE_BEND_HORIZONTAL_VERTICAL; else if(allowsDirection(a, 3) && allowsDirection(b, 0) /*&& b.y <= a.getMinY() && a.x >= b.getMaxX()*/) return ONE_BEND_VERTICAL_HORIZONTAL; } } // Do bounding boxes intersect each other? boolean boundingBoxesIntersect = !( a.getMaxX() < b.getMinX() || a.getMinX() > b.getMaxX() || a.getMaxY() < b.getMinY() || a.getMinY() > b.getMaxY() ); if(boundingBoxesIntersect) { // Can connect terminals by two lines if we ignore bounding boxes? if(a.x < b.x) { if(a.y < b.y) { if(allowsDirection(a, 0) && allowsDirection(b, 3)) return ONE_BEND_HORIZONTAL_VERTICAL; else if(allowsDirection(a, 1) && allowsDirection(b, 2)) return ONE_BEND_VERTICAL_HORIZONTAL; } else { if(allowsDirection(a, 0) && allowsDirection(b, 1)) return ONE_BEND_HORIZONTAL_VERTICAL; else if(allowsDirection(a, 3) && allowsDirection(b, 2)) return ONE_BEND_VERTICAL_HORIZONTAL; } } else { if(a.y < b.y) { if(allowsDirection(a, 2) && allowsDirection(b, 3)) return ONE_BEND_HORIZONTAL_VERTICAL; else if(allowsDirection(a, 1) && allowsDirection(b, 0)) return ONE_BEND_VERTICAL_HORIZONTAL; } else { if(allowsDirection(a, 2) && allowsDirection(b, 1)) return ONE_BEND_HORIZONTAL_VERTICAL; else if(allowsDirection(a, 3) && allowsDirection(b, 0)) return ONE_BEND_VERTICAL_HORIZONTAL; } } // Otherwise return MORE_BENDS_BBS_INTERSECT; } // Otherwise return MORE_BENDS_BBS_DONT_INTERSECT; } public static int simpleConnectionCaseRouteToBounds(RouteTerminal a, RouteTerminal b) { double aX = 0.5*(a.getMinX() + a.getMaxX()); double aY = 0.5*(a.getMinY() + a.getMaxY()); double bX = 0.5*(b.getMinX() + b.getMaxX()); double bY = 0.5*(b.getMinY() + b.getMaxY()); double minY = Math.max(a.getMinY(), b.getMinY()); double maxY = Math.min(a.getMaxY(), b.getMaxY()); if(minY <= maxY) { double cY = 0.5*(minY+maxY); a.setY(cY); b.setY(cY); if(aX < bX) { a.setX(a.getMaxX()); b.setX(b.getMinX()); } else { a.setX(a.getMinX()); b.setX(b.getMaxX()); } return DIRECT_HORIZONTAL_CONNECTION; } double minX = Math.max(a.getMinX(), b.getMinX()); double maxX = Math.min(a.getMaxX(), b.getMaxX()); if(minX <= maxX) { double cX = 0.5*(minX+maxX); a.setX(cX); b.setX(cX); if(aY < bY) { a.setY(a.getMaxY()); b.setY(b.getMinY()); } else { a.setY(a.getMinY()); b.setY(b.getMaxY()); } return DIRECT_VERTICAL_CONNECTION; } { a.setY(aY); b.setX(bX); if(aX < bX) { a.setX(a.getMaxX()); } else { a.setX(a.getMinX()); } if(aY < bY) { b.setY(b.getMinY()); } else { b.setY(b.getMaxY()); } return ONE_BEND_HORIZONTAL_VERTICAL; } } /** * Finds a route line for two route terminals. */ public static RouteLine findRouteLine(RouteTerminal a, RouteTerminal b, boolean terminalsIntersect) { if(terminalsIntersect) { if(a.x < b.x) { if((a.getAllowedDirections() & RouteTerminal.DIR_RIGHT) != 0 && (b.getAllowedDirections() & RouteTerminal.DIR_LEFT) != 0) { return new RouteLine(false, 0.5 * (a.x + b.x)); } } else { if((a.getAllowedDirections() & RouteTerminal.DIR_LEFT) != 0 && (b.getAllowedDirections() & RouteTerminal.DIR_RIGHT) != 0) { return new RouteLine(false, 0.5 * (a.x + b.x)); } } if(a.y < b.y) { if((a.getAllowedDirections() & RouteTerminal.DIR_DOWN) != 0 && (b.getAllowedDirections() & RouteTerminal.DIR_UP) != 0) { return new RouteLine(true, 0.5 * (a.y + b.y)); } } else { if((a.getAllowedDirections() & RouteTerminal.DIR_UP) != 0 && (b.getAllowedDirections() & RouteTerminal.DIR_DOWN) != 0) { return new RouteLine(true, 0.5 * (a.y + b.y)); } } } //int aDir = Directions.firstAllowedDirection(a.getAllowedDirections()); //int bDir = Directions.firstAllowedDirection(b.getAllowedDirections()); boolean isHorizontal = true; double position = 0.0; loop: for(int aDir=0;aDir<4;++aDir) if(Directions.isAllowed(a.getAllowedDirections(), aDir)) for(int bDir=0;bDir<4;++bDir) if(Directions.isAllowed(b.getAllowedDirections(), bDir)) { // Connection starts to the same direction from the both terminals if(aDir == bDir) { isHorizontal = !isHorizontal(aDir); if(!terminalsIntersect) { if(dist(aDir, a, b) > 0 && isIn(aDir+1, a.x, a.y, b)) { position = middle(aDir, a, b); break loop; } else if(dist(aDir, b, a) > 0 && isIn(aDir+1, b.x, b.y, a)) { position = middle(aDir, b, a); break loop; } } position = boundary(aDir, a, b); } // Connection starts horizontally from one terminal and // vertically from another terminal else if(((aDir ^ bDir)&1) == 1) { if(dist(aDir, a, b) >= 0) { isHorizontal = !isHorizontal(aDir); position = middle(aDir, a, b); break loop; } else if(dist(bDir, b, a) >= 0) { isHorizontal = isHorizontal(aDir);; position = middle(bDir, b, a); break loop; } else if(firstIsBoundary(bDir, a, b)) { isHorizontal = isHorizontal(aDir); position = boundary(bDir, b, a); } else { isHorizontal = !isHorizontal(aDir); position = boundary(aDir, a, b); } } // Connection starts to opposite directions from the terminals else { if(dist(aDir, a, b) >= 0.0) { isHorizontal = !isHorizontal(aDir); position = middle(aDir, a, b); break loop; } else if(dist(aDir+1, a, b) >= 0.0) { isHorizontal = isHorizontal(aDir); position = middle(aDir+1, a, b); break loop; } else if(dist(aDir-1, a, b) >= 0.0) { isHorizontal = isHorizontal(aDir); position = middle(aDir+1, a, b); break loop; } else { isHorizontal = isHorizontal(aDir); double b1 = boundary(aDir+1, a, b); double b2 = boundary(aDir-1, a, b); double cost1, cost2; if(isHorizontal) { double da1 = a.y - b1; double db1 = b.y - b1; cost1 = da1*da1 + db1*db1; double da2 = a.y - b2; double db2 = b.y - b2; cost2 = da2*da2 + db1*db2; } else { double da1 = a.x - b1; double db1 = b.x - b1; cost1 = da1*da1 + db1*db1; double da2 = a.x - b2; double db2 = b.x - b2; cost2 = da2*da2 + db1*db2; } position = cost1 <= cost2 ? b1 : b2; } } } return new RouteLine(isHorizontal, position); } /** * Computes the difference between two points to the given direction */ public static double diff(int dir, double x1, double y1, double x2, double y2) { switch(dir&3) { case 0: return x1 - x2; case 1: return y1 - y2; case 2: return x2 - x1; case 3: return y2 - y1; default: throw new Error("Should not happen."); } } /** * Computes the distance of the bounding boxes of the two route terminals * to the given direction. */ public static double dist(int dir, RouteTerminal a, RouteTerminal b) { switch(dir&3) { case 0: return b.getMinX() - a.getMaxX(); case 1: return b.getMinY() - a.getMaxY(); case 2: return a.getMinX() - b.getMaxX(); case 3: return a.getMinY() - b.getMaxY(); default: throw new Error("Should not happen."); } } /** * Computes the middle point between two terminals in the given direction. */ public static double middle(int dir, RouteTerminal a, RouteTerminal b) { switch(dir&3) { case 0: return 0.5*(b.getMinX() + a.getMaxX()); case 1: return 0.5*(b.getMinY() + a.getMaxY()); case 2: return 0.5*(a.getMinX() + b.getMaxX()); case 3: return 0.5*(a.getMinY() + b.getMaxY()); default: throw new Error("Should not happen."); } } /** * Tests whether the point is inside the bounding box of the terminal * in the given direction. */ public static boolean isIn(int dir, double x, double y, RouteTerminal a) { if((dir&1) == 0) return a.getMinX() < x && x < a.getMaxX(); else return a.getMinY() < y && y < a.getMaxY(); } public static boolean isHorizontal(int dir) { return (dir&1) == 0; } /** * Gives the boundary of the route terminal in the given direction. */ public static double boundary(int dir, RouteTerminal a) { switch(dir&3) { case 0: return a.getMaxX(); case 1: return a.getMaxY(); case 2: return a.getMinX(); case 3: return a.getMinY(); default: throw new Error("Should not happen."); } } /** * Gives the boundary of two route terminals in the given direction. */ public static double boundary(int dir, RouteTerminal a, RouteTerminal b) { switch(dir&3) { case 0: return Math.max(a.getMaxX(), b.getMaxX()); case 1: return Math.max(a.getMaxY(), b.getMaxY()); case 2: return Math.min(a.getMinX(), b.getMinX()); case 3: return Math.min(a.getMinY(), b.getMinY()); default: throw new Error("Should not happen."); } } /** * Returns true if the first terminal is farther away in the given direction. */ public static boolean firstIsBoundary(int dir, RouteTerminal a, RouteTerminal b) { switch(dir&3) { case 0: return a.getMaxX() >= b.getMaxX(); case 1: return a.getMaxY() >= b.getMaxY(); case 2: return a.getMinX() <= b.getMinX(); case 3: return a.getMinY() <= b.getMinY(); default: throw new Error("Should not happen."); } } }