package edu.stanford.hci.flowmap.utils; import org.codemap.util.geom.Line2D; import org.codemap.util.geom.Point2D; import org.codemap.util.geom.Rectangle2D; /** * This software is distributed under the Berkeley Software Distribution License. * Please see http://graphics.stanford.edu/~dphan/code/bsd.license.html * */ public class GraphicsGems { public static void checkNaN(Point2D pt) { assert (!Double.isNaN(pt.getX())); assert (!Double.isNaN(pt.getY())); } /** * Copied from http://www.webreference.com/programming/java/beginning/chap5/3/3.html * If the lines are parallel, the denominator in the equation for t will be zero, * something you should really check for in the code. For the moment, we will ignore * it and end up with coordinates that are Infinity if it occurs. * @param line1 * @param line2 * @return */ public static Point2D intersectSegments(Line2D line1, Line2D line2) { //System.out.println("GraphicsGem.intersects: line1" + line1.getP1() + "," + line1.getP2() + " line2: " + line2.getP1() + "," + line2.getP2()); if(!line1.intersectsLine(line2)){ //System.out.println("No Intersection!1"); return new Point2D.Double(Double.POSITIVE_INFINITY,Double.POSITIVE_INFINITY); } Point2D localPoint = new Point2D.Double(0, 0); double num = (line2.getP2().getY()-line2.getP1().getY())*(line2.getP1().getX()-line1.getP1().getX()) - (line2.getP2().getX()-line2.getP1().getX())*(line2.getP1().getY()-line1.getP1().getY()); double denom = (line2.getP2().getY()-line2.getP1().getY())*(line1.getP2().getX()-line1.getP1().getX()) - (line2.getP2().getX()-line2.getP1().getX())*(line1.getP2().getY()-line1.getP1().getY()); double t = num/denom; if(t < 0 || t > 1){ //System.out.println("No Intersection!2"); return new Point2D.Double(Double.POSITIVE_INFINITY,Double.POSITIVE_INFINITY); } double x = line1.getP1().getX() + (line1.getP2().getX()-line1.getP1().getX())*t; double y = line1.getP1().getY() + (line1.getP2().getY()-line1.getP1().getY())*t; localPoint.setLocation(x, y); //System.out.println("Intersection at: " + localPoint); return localPoint; } /** * Copied from http://www.webreference.com/programming/java/beginning/chap5/3/3.html * If the lines are parallel, the denominator in the equation for t will be zero, * something you should really check for in the code. For the moment, we will ignore * it and end up with coordinates that are Infinity if it occurs. * @param line1 * @param line2 * @return */ public static Point2D intersectInfiniteLines(Line2D line1, Line2D line2) { Point2D localPoint = new Point2D.Double(0, 0); double num = (line2.getP2().getY()-line2.getP1().getY())*(line2.getP1().getX()-line1.getP1().getX()) - (line2.getP2().getX()-line2.getP1().getX())*(line2.getP1().getY()-line1.getP1().getY()); double denom = (line2.getP2().getY()-line2.getP1().getY())*(line1.getP2().getX()-line1.getP1().getX()) - (line2.getP2().getX()-line2.getP1().getX())*(line1.getP2().getY()-line1.getP1().getY()); double t = num/denom; double x = line1.getP1().getX() + (line1.getP2().getX()-line1.getP1().getX())*t; double y = line1.getP1().getY() + (line1.getP2().getY()-line1.getP1().getY())*t; localPoint.setLocation(x, y); //System.out.println("Intersection at: " + localPoint); return localPoint; } /** * @param rect * @param line * @param point * @return the closest point on the boundary of rect that is on line, measured from point */ public static Point2D closestIntersectBox(Rectangle2D rect, Line2D line, Point2D point) { //System.out.println("Looking at box: " + rect + " line from " + line.getP1() + " to " + line.getP2() + " and point " + point); Point2D closest, candidate; double closestDist, candidateDist; Point2D topLeft = new Point2D.Double(rect.getX(), rect.getY()); Point2D topRight = new Point2D.Double(rect.getX()+rect.getWidth(), rect.getY()); Point2D bottomLeft = new Point2D.Double(rect.getX(), rect.getY()+rect.getHeight()); Point2D bottomRight = new Point2D.Double(rect.getX()+rect.getWidth(),rect.getY()+rect.getHeight() ); // top of rectangle Line2D top = new Line2D.Double(topLeft, topRight); closest = intersectSegments(line, top); closestDist = point.distance(closest); // left of rectangle Line2D left = new Line2D.Double(topLeft, bottomLeft); candidate = intersectSegments(line, left); candidateDist = point.distance(candidate); if (candidateDist < closestDist) { closestDist = candidateDist; closest = candidate; } // right of rectangle Line2D right = new Line2D.Double(topRight, bottomRight); candidate = intersectSegments(line, right); candidateDist = point.distance(candidate); if (candidateDist < closestDist) { closestDist = candidateDist; closest = candidate; } // bottom of rectangle Line2D bottom = new Line2D.Double(bottomLeft, bottomRight); candidate = intersectSegments(line, bottom); candidateDist = point.distance(candidate); if (candidateDist < closestDist) { closestDist = candidateDist; closest = candidate; } return closest; } /** * @param rect * @param line * @param point * @return the closest corner of rect measured from point */ public static Point2D closestCornerBox(Rectangle2D rect, Line2D line, Point2D point) { Point2D closest, candidate, furthest; double closestDist, candidateDist, furthestDist; Line2D closestLine, furthestLine; Point2D topLeft = new Point2D.Double(rect.getX(), rect.getY()); Point2D topRight = new Point2D.Double(rect.getX()+rect.getWidth(), rect.getY()); Point2D bottomLeft = new Point2D.Double(rect.getX(), rect.getY()+rect.getHeight()); Point2D bottomRight = new Point2D.Double(rect.getX()+rect.getWidth(),rect.getY()+rect.getHeight() ); //System.out.println("closestCornerBox: " + rect + " line1: " + line.getP1() + " line2: " + line.getP2()+ " point: " + point); // find the closest and furthest points of intersection // top of rectangle Line2D top = new Line2D.Double(topLeft, topRight); furthest = closest = GraphicsGems.intersectSegments(line, top); furthestDist = closestDist = point.distance(closest); closestLine = furthestLine = top; if (Double.isInfinite(furthestDist)) { furthestDist = -1; } //System.out.println("1Closest :" + closest + " furthest: " + furthest); // left of rectangle Line2D left = new Line2D.Double(topLeft, bottomLeft); candidate = GraphicsGems.intersectSegments(line, left); candidateDist = point.distance(candidate); if (!Double.isInfinite(candidateDist)) { if (candidateDist < closestDist) { closestDist = candidateDist; closest = candidate; closestLine = left; } if (candidateDist > furthestDist) { furthestDist = candidateDist; furthest = candidate; furthestLine = left; } //System.out.println("2Closest :" + closest + " furthest: " + furthest); } // right of rectangle Line2D right = new Line2D.Double(topRight, bottomRight); candidate = GraphicsGems.intersectSegments(line, right); candidateDist = point.distance(candidate); if (!Double.isInfinite(candidateDist)) { if (candidateDist < closestDist) { closestDist = candidateDist; closest = candidate; closestLine = right; } if (candidateDist > furthestDist) { furthestDist = candidateDist; furthest = candidate; furthestLine = right; } //System.out.println("3Closest :" + closest + " furthest: " + furthest); } // bottom of rectangle Line2D bottom = new Line2D.Double(bottomLeft, bottomRight); candidate = GraphicsGems.intersectSegments(line, bottom); candidateDist = point.distance(candidate); if (!Double.isInfinite(candidateDist)) { if (candidateDist < closestDist) { closestDist = candidateDist; closest = candidate; closestLine = bottom; } if (candidateDist > furthestDist) { furthestDist = candidateDist; furthest = candidate; furthestLine = bottom; } //System.out.println("4Closest :" + closest + " furthest: " + furthest); } // now we have the closest and furthest distances, check to see if the things that were // intersected are on opposite sides, or adjacent // if closest == furthest, it is the corner point if (closest == furthest) return null; assert(!Double.isInfinite(closestDist) && !Double.isInfinite(furthestDist)); // if lines are adjacent Point2D adjacentPoint = areLinesAdjacent(closestLine, furthestLine); //System.out.println("Are lines adjacent: closestLine " + closestLine.getP1() + "," + closestLine.getP2() + " furthest: " + furthestLine.getP1() + "," + furthestLine.getP2()); //System.out.println("adjacent? " + (adjacentPoint != null)); // that adjacent point is the corner point! //System.out.println("ClosestPoint: " + closest + " point: " + point); if (adjacentPoint != null) { //System.out.println("GraphicsGems. closestCorner is a triangle."); return adjacentPoint; // we also want to return the corner point if the given point is on the rectangle itself // this is a degenerate case } else if (closest.equals(point)){ //System.out.println("GraphicsGems. closestCorner handling triangle degeneracy."); // find those adjacent lines boolean foundOne = false; Line2D candidate1 = null; Line2D candidate2 = null; if (isPointOnLine(top, point )) { foundOne = true; candidate1 = top; } if (isPointOnLine(bottom, point)) { if (foundOne) { candidate2 = bottom; } else { candidate1 = bottom; foundOne = true; } } if (isPointOnLine(right, point)) { if (foundOne) { candidate2 = right; } else { candidate1 = right; foundOne = true; } } if (isPointOnLine(left, point)) { if (foundOne) { candidate2 = left; } else { candidate1 = left; foundOne = true; } } assert((candidate1 != null) && (candidate2 != null)); // now find the adjacent lines Point2D onePt = areLinesAdjacent(candidate1, furthestLine); Point2D twoPt = areLinesAdjacent(candidate2, furthestLine); if (onePt == null) { assert(twoPt != null); return twoPt; } else { assert(onePt != null); return onePt; } // if lines are opposite one another, find corner by doing area calculation } else { //System.out.println("GraphicsGems. closestCorner is a trapezoid."); // 1. compute area of rectangle double boxArea = rect.getWidth() * rect.getHeight(); Line2D heightLine;//, otherLine; // 2. pick a line that is not closest or further line, call it the heightLine, // (for the height of the trapezoid), call the remaining line the otherLine // note we know that the lines have to be opposite each other, so this make it easier if ((top != closestLine) && (top != furthestLine) && (bottom != closestLine) && (bottom != furthestLine)) { heightLine = top; // otherLine = bottom; } else { heightLine = left; // otherLine = right; } // 3. get adjacent point of closestLine and heightLine, to compute b1 Point2D b1Point = areLinesAdjacent(closestLine, heightLine); assert (b1Point != null); double b1 = b1Point.distance(closest); // 4. get adjacent point of furthestLine and heightLine, to compute b2 Point2D b2Point = areLinesAdjacent(furthestLine, heightLine); assert(b2Point != null); double b2 = b2Point.distance(furthest); // 5. compute height by getting length of heightLine double h = heightLine.getP1().distance(heightLine.getP2()); // 6. compute area of trapezoid //System.out.println("b1: " + b1 + " b2: " + b2 + " h: " + h); double trapezoidArea = .5*(b1+b2)*h; //System.out.println( "boxArea :" + boxArea + " trapezoid Area: " + trapezoidArea); assert(boxArea > trapezoidArea); // double otherArea = boxArea - trapezoidArea; // return the point on the side of trapezoidArea if (trapezoidArea > boxArea) { return b1Point; // return the point on the side of boxArea (the point on closestLine that isn't b1Point } else { if (closestLine.getP1().equals(b1Point)) return closestLine.getP1(); else return closestLine.getP2(); } } } /** * @param oneLine * @param twoLine * @return the adjacent point of oneLine and twoLine or null if no such point exists */ public static Point2D areLinesAdjacent(Line2D oneLine, Line2D twoLine) { Point2D oneP1 = oneLine.getP1(); Point2D oneP2 = oneLine.getP2(); Point2D twoP1 = twoLine.getP1(); Point2D twoP2 = twoLine.getP2(); if (oneP1.equals(twoP1) || oneP1.equals(twoP2)) return oneP1; else if (oneP2.equals(twoP1) || oneP2.equals(twoP2)) return oneP2; else return null; } public static boolean isPointOnLine(Line2D line, Point2D point) { return (line.getP1().equals(point) || line.getP2().equals(point)); } }