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=4f9b94f77e452df5e0756a4ed1874ccd75f04ecc;hp=0000000000000000000000000000000000000000;hb=969bd23cab98a79ca9101af33334000879fb60c5;hpb=866dba5cd5a3929bbeae85991796acb212338a08 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 new file mode 100644 index 000000000..4f9b94f77 --- /dev/null +++ b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/SimpleConnectionUtility.java @@ -0,0 +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."); + } + } + +}