/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * ConnectionRoute.java * Creation date: (12/6/00 7:46:02 AM) * By: Luke Evans */ package org.openquark.gems.client; import java.awt.Point; import java.util.ArrayList; import java.util.List; /** * A connection route describes a series of points to join to describe * a route from an initial point to a final point. * Creation date: (12/6/00 7:46:02 AM) * @author Luke Evans */ public class ConnectionRoute { private final List<Point> points; // The path itself private int minX = 0, minY = 0, maxX = 0, maxY = 0; /** * Default ConnectionRoute constructor. */ private ConnectionRoute() { super(); points = new ArrayList<Point>(2); } /** * Construct a ConnectionRoute from start and end points. */ public ConnectionRoute(Point start, Point end) { this(); addPoint(start); addPoint(end); // Initialise the max and min (for bounds) minX = Math.min(start.x, end.x); maxX = Math.max(start.x, end.x); minY = Math.min(start.y, end.y); maxY = Math.max(start.y, end.y); } /** * Add a final leg based on a fraction extrapolation. * Creation date: (12/6/00 12:07:56 PM) */ public void addFinalLeg(float extrapolation) { // Can only do this if we have more than 3 points int i = points.size(); if (i < 3) { return; } // Get some points i--; Point lastPoint = points.get(i--); Point penultPoint = points.get(i--); Point antePenultPoint = points.get(i); // Work out an extrapolation of the penultimate line (scaled by the factor provided) Point delta = new Point(penultPoint.x - antePenultPoint.x, penultPoint.y - antePenultPoint.y); delta.x *= extrapolation; delta.y *= extrapolation; // Translate the penultPoint by this delta penultPoint.x += delta.x; penultPoint.y += delta.y; checkPoint(penultPoint); // Insert a new penultPoint points.add(points.size() - 1, checkPoint(new Point(lastPoint.x + delta.x, lastPoint.y + delta.y))); } /** * Add a final leg based on a fixed delta amount. * Creation date: (12/6/00 12:07:56 PM) */ public void addFinalLeg(Point delta) { // Can only do this if we have more than 3 points int i = points.size(); if (i < 3) { return; } // Get some points i--; Point lastPoint = points.get(i--); Point penultPoint = points.get(i--); // Translate the penultPoint by this delta penultPoint.x += delta.x; penultPoint.y += delta.y; checkPoint(penultPoint); // Insert a new penultPoint points.add(points.size() - 1, checkPoint(new Point(lastPoint.x + delta.x, lastPoint.y + delta.y))); } /** * Add the given point the end of the points list. * Creation date: (12/6/00 8:05:24 AM) * @param xy Point the point to add */ public void addPoint(Point xy) { points.add(checkPoint(xy)); } /** * Return the index of the point after the middle of the line. * Creation date: (12/6/00 8:58:00 AM) * @return int the index of the point */ private int afterMiddle() { return (int) (points.size() / 2.0F + 0.5); } /** * Return the index of the point before the middle of the line. * Creation date: (12/6/00 8:58:00 AM) * @return int the index of the point */ private int beforeMiddle() { return points.size()/2; } /** * Insert a new point after the 'middle' of the line. * Creation date: (12/6/00 8:07:18 AM) * @param xy Point the new point */ public void bisectAfterMiddle(Point xy) { // Where is the insert point? int insert = afterMiddle(); // Insert point points.add(insert, checkPoint(xy)); } /** * Insert a new point after the 'middle' of the line. * Creation date: (12/6/00 8:07:18 AM) * @param xy Point the new point */ public void bisectBeforeMiddle(Point xy) { // Where is the insert point? int insert = beforeMiddle(); // Insert point points.add(insert, checkPoint(xy)); } /** * Bisect with a horizontal line offset by delta from * the point before the centre. If delta is zero, we divide equally * Creation date: (12/6/00 8:55:12 AM) * @param delta int the offset to the horizontal line or 0 for equal split */ public void bisectWithHorizontalLine(int delta) { // Get points around the middle int afterMiddle = beforeMiddle(); int beforeMiddle = afterMiddle -1; Point beforePoint = points.get(beforeMiddle); Point afterPoint = points.get(afterMiddle); // Generate the new points Point firstPoint = new Point(beforePoint); Point secondPoint = new Point(afterPoint); // If delta is zero work out the split if (delta == 0) { delta = (secondPoint.y - firstPoint.y) / 2; } // To generate a horizontal line, we offset these points in the Y dimension firstPoint.y += delta; secondPoint.y = firstPoint.y; // Add these points bisectWithLine(firstPoint, secondPoint); } /** * Add the line xy1->xy2 to the middle of the connection. * Creation date: (12/6/00 8:33:30 AM) * @param xy1 Point the first point in the line * @param xy2 Point the second point in the line */ public void bisectWithLine(Point xy1, Point xy2) { // Just insert the two points about the middle bisectBeforeMiddle(xy1); bisectAfterMiddle(xy2); } /** * Bisect with a vertical line offset by delta from * the point before the centre. If delta is zero, we divide equally * Creation date: (12/6/00 8:55:12 AM) * @param delta int the offset to the vertical line or 0 for equal split */ public void bisectWithVerticalLine(int delta) { // Get points around the middle int afterMiddle = beforeMiddle(); int beforeMiddle = afterMiddle -1; Point beforePoint = points.get(beforeMiddle); Point afterPoint = points.get(afterMiddle); // Generate the new points Point firstPoint = new Point(beforePoint); Point secondPoint = new Point(afterPoint); // If delta is zero work out the split if (delta == 0) { delta = (secondPoint.x - firstPoint.x) / 2; } // To generate a vertical line, we offset these points in the X dimension firstPoint.x += delta; secondPoint.x = firstPoint.x; // Add these points bisectWithLine(firstPoint, secondPoint); } /** * Check this point for relevance to the overall bounds of the route. * Creation date: (12/7/00 11:14:28 AM) * @return Point the point (so we can treat this as a filter) * @param point Point the point */ private Point checkPoint(Point point) { maxX = Math.max(point.x, maxX); minX = Math.min(point.x, minX); maxY = Math.max(point.y, maxY); minY = Math.min(point.y, minY); return point; } /** * Draw this ConnectionRoute in the given graphics context. * Creation date: (12/6/00 9:14:10 AM) * @param g2d java.awt.Graphics2D */ public void draw(java.awt.Graphics2D g2d) { // Just draw lines between each point int numPoints = points.size(); for (int i = 1; i < numPoints; i++) { Point fromPoint = points.get(i - 1); Point toPoint = points.get(i); // workaround for a Mac bug in OS X: // negative delta's are not drawn properly in XOR mode, so we make all delta's positive int maxX = Math.max(toPoint.x, fromPoint.x); int minX = Math.min(toPoint.x, fromPoint.x); int maxY = Math.max(toPoint.y, fromPoint.y); int minY = Math.min(toPoint.y, fromPoint.y); // use the fact that connection routes are either horizontal or vertical // (this works only if minX and minY are from the same point) g2d.drawLine(minX, minY, maxX, maxY); // g2d.drawLine(fromPoint.x, fromPoint.y, toPoint.x, toPoint.y); } } /** * Return the first point in the route. * Creation date: (12/7/00 2:10:58 PM) * @return Point the point */ private Point firstPoint() { return points.get(0); } /** * Generate a square path between source and destination suitable * for horizontal connectors * Creation date: (12/7/00 1:30:56 PM) * @return boolean true if suitable for extra leg */ public boolean genSquareRouteForHorizontalConnectors() { // Get the first and last points Point first = firstPoint(); Point last = lastPoint(); // Determine whether to split vertically or horizontally if (last.x >= first.x) { bisectWithVerticalLine(0); return false; } else { bisectWithHorizontalLine(0); return true; } } /** * Generate a square path between source and destination suitable * for horizontal connectors. Add a final leg of the given size. * Creation date: (12/7/00 1:30:56 PM) */ public void genSquareRouteForHorizontalConnectors(int extraFinalLegOffset) { if (genSquareRouteForHorizontalConnectors()) { addFinalLeg(new Point(extraFinalLegOffset, 0)); } } /** * Generate a square path between source and destination suitable * for vertical connectors * Creation date: (12/7/00 1:30:56 PM) * @return boolean true if suitable for extra leg */ public boolean genSquareRouteForVerticalConnectors() { // Get the first and last points Point first = firstPoint(); Point last = lastPoint(); // Determine whether to split vertically or horizontally if (last.y >= first.y) { bisectWithHorizontalLine(0); return false; } else { bisectWithVerticalLine(0); return true; } } /** * Generate a square path between source and destination suitable * for vertical connectors. Add a final leg of the given size. * Creation date: (12/7/00 1:30:56 PM) */ public void genSquareRouteForVerticalConnectors(int extraFinalLegOffset) { if (genSquareRouteForVerticalConnectors()) { addFinalLeg(new Point(0, extraFinalLegOffset)); } } /** * Insert the method's description here. * Creation date: (12/7/00 11:04:28 AM) * @return java.awt.Rectangle */ public java.awt.Rectangle getBoundingRectangle() { return new java.awt.Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1); } /** * Return the last point in the route * Creation date: (12/7/00 2:12:09 PM) * @return Point the point */ private Point lastPoint() { return points.get(points.size() - 1); } }