// PathVisio, // a tool for data visualization and analysis using Biological Pathways // Copyright 2006-2011 BiGCaT Bioinformatics // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package org.pathvisio.core.model; import java.awt.Shape; import java.awt.geom.Point2D; /** * ConnectorShape implementation for the elbow connector * @author thomas * */ public class ElbowConnectorShape extends SegmentedConnector { private final static double SEGMENT_OFFSET = 20; public void recalculateShape(ConnectorRestrictions restrictions) { WayPoint[] wps = calculateWayPoints(restrictions); setSegments(calculateSegments(restrictions, wps)); setWayPoints(wayPointsToCenter(wps, getSegments())); setShape(calculateShape()); } public boolean hasValidWaypoints(ConnectorRestrictions restrictions) { //Only check if number of waypoints matches number of segments return restrictions.getWayPointPreferences().length == getNrSegments(restrictions) - 2; } protected WayPoint[] wayPointsToCenter(WayPoint[] waypoints, Segment[] segments) { //Set all waypoints to the center of the segments for(int i = 1; i < segments.length - 1; i++) { waypoints[i - 1].setLocation(segments[i].getMCenter()); } return waypoints; } protected Shape calculateShape() { return calculateShape(getSegments()); } protected WayPoint[] calculateWayPoints(ConnectorRestrictions restrictions) { int nrSegments = getNrSegments(restrictions); WayPoint[] waypoints = restrictions.getWayPointPreferences(); //Use the preferred waypoint if it's possible to draw //a valid path through them if(hasValidWaypoints(restrictions)) { return waypoints; } //Else, calculate the default waypoints waypoints = new WayPoint[nrSegments - 2]; int startSide = restrictions.getStartSide(); int startAxis = getSegmentAxis(startSide); int startDirection = getSegmentDirection(startSide); int endSide = restrictions.getEndSide(); int endAxis = getSegmentAxis(endSide); int endDirection = getSegmentDirection(endSide); Point2D start = restrictions.getStartPoint(); Point2D end = restrictions.getEndPoint(); if(nrSegments - 2 == 1) { /* * [S]--- * | * ---[S] */ waypoints[0] = calculateWayPoint(start, end, startAxis, startDirection); } else if(nrSegments - 2 == 2) { /* * [S]--- * | [S] * | | * |--- */ waypoints[0] = calculateWayPoint(start, new Point2D.Double( end.getX() + SEGMENT_OFFSET * endDirection, end.getY() + SEGMENT_OFFSET * endDirection ), startAxis, startDirection); waypoints[1] = calculateWayPoint(end, waypoints[0], endAxis, endDirection); } else if(nrSegments - 2 == 3) { /* ----- * | | * [S] | [S] * | | * |--- */ //Start with middle waypoint waypoints[1] = new WayPoint(new Point2D.Double( start.getX() + (end.getX() - start.getX()) / 2, start.getY() + (end.getY() - start.getY()) / 2 )); waypoints[0] = calculateWayPoint(start, waypoints[1], startAxis, startDirection); waypoints[2] = calculateWayPoint(end, waypoints[1], endAxis, endDirection); } return waypoints; } protected WayPoint calculateWayPoint(Point2D start, Point2D end, int axis, int direction) { double x,y = 0; if(axis == AXIS_Y) { x = start.getX() + (end.getX() - start.getX()) / 2; y = start.getY() + SEGMENT_OFFSET * direction; } else { x = start.getX() + SEGMENT_OFFSET * direction; y = start.getY() + (end.getY() - start.getY()) / 2; } return new WayPoint(x, y); } protected Segment[] calculateSegments(ConnectorRestrictions restrictions, WayPoint[] waypoints) { int nrSegments = getNrSegments(restrictions); Segment[] segments = new Segment[nrSegments]; Point2D start = restrictions.getStartPoint(); Point2D end = restrictions.getEndPoint(); int startAxis = getSegmentAxis(restrictions.getStartSide()); if(nrSegments == 2) { //No waypoints segments[0] = createStraightSegment(start, end, startAxis); segments[1] = createStraightSegment(segments[0].getMEnd(), end, getOppositeAxis(startAxis)); } else { segments[0] = createStraightSegment( restrictions.getStartPoint(), waypoints[0], startAxis ); int axis = getOppositeAxis(startAxis); for(int i = 0; i < waypoints.length - 1; i++) { segments[i + 1] = createStraightSegment( segments[i].getMEnd(), waypoints[i + 1], axis ); axis = getOppositeAxis(axis); } segments[segments.length - 2] = createStraightSegment( segments[segments.length -3].getMEnd(), end, axis ); segments[segments.length - 1] = createStraightSegment( segments[segments.length - 2].getMEnd(), end, getSegmentAxis(restrictions.getEndSide()) ); } setWayPoints(waypoints); return segments; } protected Segment createStraightSegment(Point2D start, Point2D end, int axis) { double ex = end.getX(); double ey = end.getY(); if(axis == AXIS_X) { ey = start.getY(); } else { ex = start.getX(); } return new Segment(start, new Point2D.Double(ex, ey)); } private int getOppositeAxis(int axis) { return axis == ConnectorShape.AXIS_X ? AXIS_Y : AXIS_X; } private int getSegmentDirection(int side) { switch(side) { case ConnectorRestrictions.SIDE_EAST: case ConnectorRestrictions.SIDE_SOUTH: return 1; case ConnectorRestrictions.SIDE_NORTH: case ConnectorRestrictions.SIDE_WEST: return -1; } return 0; } private int getSegmentAxis(int side) { switch(side) { case ConnectorRestrictions.SIDE_EAST: case ConnectorRestrictions.SIDE_WEST: return AXIS_X; case ConnectorRestrictions.SIDE_NORTH: case ConnectorRestrictions.SIDE_SOUTH: return AXIS_Y; } return 0; } /* The number of connector for each side and relative position RN RE RS RW BLN 1 2 1 0 TLN 1 2 3 2 BLE 3 1 0 1 TLE 0 1 2 1 BLS 3 2 1 2 TLS 1 2 1 0 BLW 2 3 2 1 TLW 2 3 2 1 There should be some logic behind this, but hey, it's Friday... (so we just hard code the array) */ private int[][][] waypointNumbers; private int getNrWaypoints(int x, int y, int z) { // if(waypointNumbers == null) { waypointNumbers = new int[][][] { new int[][] { new int[] { 1, 1 }, new int[] { 2, 2 }, new int[] { 1, 3 }, new int[] { 0, 2 } }, new int[][] { new int[] { 2, 0 }, new int[] { 1, 1 }, new int[] { 0, 2 }, new int[] { 1, 1 }, }, new int[][] { new int[] { 3, 1 }, new int[] { 2, 2 }, new int[] { 1, 1 }, new int[] { 2, 0 }, }, new int[][] { new int[] { 2, 2 }, new int[] { 3, 3 }, new int[] { 2, 2 }, new int[] { 1, 1 }, } }; // } return waypointNumbers[x][y][z]; } /** * Get the direction of the line on the x axis * @param start The start point of the line * @param end The end point of the line * @return 1 if the direction is positive (from left to right), * -1 if the direction is negative (from right to left) */ int getDirectionX(Point2D start, Point2D end) { return (int)Math.signum(end.getX() - start.getX()); } /** * Get the direction of the line on the y axis * @param start The start point of the line * @param end The end point of the line * @return 1 if the direction is positive (from top to bottom), * -1 if the direction is negative (from bottom to top) */ protected int getDirectionY(Point2D start, Point2D end) { return (int)Math.signum(end.getY() - start.getY()); } protected int getNrSegments(ConnectorRestrictions restrictions) { Point2D start = restrictions.getStartPoint(); Point2D end = restrictions.getEndPoint(); boolean leftToRight = getDirectionX(start, end) > 0; Point2D left = leftToRight ? start : end; Point2D right = leftToRight ? end : start; boolean leftBottom = getDirectionY(left, right) < 0; int z = leftBottom ? 0 : 1; int x = leftToRight ? restrictions.getStartSide() : restrictions.getEndSide(); int y = leftToRight ? restrictions.getEndSide() : restrictions.getStartSide(); return getNrWaypoints(x, y, z) + 2; } private boolean isPointOnSegment(Double p1, Double p2, Double point) { double offset = 50.0; if (p1 < p2 && (p1 - offset) < point && (point < (p2 + offset))) { return true; } else if (p2 < p1 && (p2 - offset) < point && (point < (p1 + offset))) { return true; } return false; } }