X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.diagram.connection%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fconnection%2FSimpleConnectionUtility.java;fp=bundles%2Forg.simantics.diagram.connection%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fconnection%2FSimpleConnectionUtility.java;h=109f8fe735ed9cf1bb947530c290e618633ea842;hp=4f9b94f77e452df5e0756a4ed1874ccd75f04ecc;hb=0ae2b770234dfc3cbb18bd38f324125cf0faca07;hpb=24e2b34260f219f0d1644ca7a138894980e25b14 diff --git a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/SimpleConnectionUtility.java b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/SimpleConnectionUtility.java index 4f9b94f77..109f8fe73 100644 --- a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/SimpleConnectionUtility.java +++ b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/SimpleConnectionUtility.java @@ -1,428 +1,428 @@ -/******************************************************************************* - * 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ö - */ -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; - } - - private 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."); - } - } - -} +/******************************************************************************* + * 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ö + */ +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; + } + + private 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."); + } + } + +}