| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * 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 com.intellij.util.ui; |
| |
| import javax.swing.*; |
| import java.awt.*; |
| import java.awt.geom.AffineTransform; |
| import java.awt.geom.Line2D; |
| import java.awt.geom.Point2D; |
| import java.util.TreeMap; |
| |
| public class GeometryUtil implements SwingConstants { |
| private static final int myArrowSize = 9; |
| private static final Shape myArrowPolygon = new Polygon(new int[] {0, myArrowSize, 0, 0}, new int[] {0, myArrowSize /2, myArrowSize, 0}, 4); |
| |
| public static Point getIntersectionPoint(Line2D aSegment, Rectangle aRectangle) { |
| |
| if (segmentOutsideRectangle(aRectangle, aSegment)) { |
| throw new IllegalArgumentException("Segment " + toString(aSegment) + " lies out of rectangle " + aRectangle + " or intersects more than one bound"); |
| } |
| |
| if (segmentInsideRectangle(aRectangle, aSegment)) { |
| return null; |
| } |
| |
| Line2D[] bounds = new Line2D[4]; |
| bounds[0] = getTopOf(aRectangle); |
| bounds[1] = getRightOf(aRectangle); |
| bounds[2] = getBottomOf(aRectangle); |
| bounds[3] = getLeftOf(aRectangle); |
| |
| for (int i = 0; i < bounds.length; i++) { |
| if (bounds[i].intersectsLine(aSegment)) { |
| return getIntersectionPoint(aSegment, bounds[i]); |
| } |
| } |
| |
| return null; |
| } |
| |
| public static Line2D.Double getLeftOf(Rectangle aRectangle) { |
| return new Line2D.Double(aRectangle.getX(), aRectangle.getY(), aRectangle.getX(), aRectangle.getY() + aRectangle.getHeight()); |
| } |
| |
| public static Line2D.Double getBottomOf(Rectangle aRectangle) { |
| return new Line2D.Double(aRectangle.getX(), aRectangle.getY() + aRectangle.getHeight(), aRectangle.getX() + aRectangle.getWidth(), aRectangle.getY() + aRectangle.getHeight()); |
| } |
| |
| public static Line2D.Double getRightOf(Rectangle aRectangle) { |
| return new Line2D.Double(aRectangle.getX() + aRectangle.getWidth(), aRectangle.getY(), aRectangle.getX() + aRectangle.getWidth(), aRectangle.getY() + aRectangle.getHeight()); |
| } |
| |
| public static Line2D.Double getTopOf(Rectangle aRectangle) { |
| return new Line2D.Double(aRectangle.getX(), aRectangle.getY(), aRectangle.getX() + aRectangle.getWidth(), aRectangle.getY()); |
| } |
| |
| |
| private static boolean segmentInsideRectangle(Rectangle aRectangle, Line2D aSegment) { |
| return isWithin(aRectangle, aSegment.getP1()) && isWithin(aRectangle, aSegment.getP2()); |
| } |
| |
| private static boolean segmentOutsideRectangle(Rectangle aRectangle, Line2D aSegment) { |
| return (!isWithin(aRectangle, aSegment.getP1())) && (!isWithin(aRectangle, aSegment.getP2())); |
| } |
| |
| public static boolean isWithin(Rectangle aRectangle, Point2D aPoint) { |
| return |
| (aPoint.getX() > aRectangle.getX()) && (aPoint.getX() < aRectangle.getX() + aRectangle.getBounds().width) |
| && |
| ((aPoint.getY() > aRectangle.getY()) && (aPoint.getY() < aRectangle.getY() + aRectangle.getBounds().height)); |
| } |
| |
| public static Point getIntersectionPoint(Line2D aFirst, Line2D aSecond) { |
| double firstDeltaX = aFirst.getX2() - aFirst.getX1(); |
| double firstDeltaY = aFirst.getY2() - aFirst.getY1(); |
| |
| double kFirst = firstDeltaY / firstDeltaX; |
| double bFirst = aFirst.getY1() - kFirst * aFirst.getX1(); |
| |
| |
| double secondDeltaX = aSecond.getX2() - aSecond.getX1(); |
| double secondDeltaY = aSecond.getY2() - aSecond.getY1(); |
| |
| double kSecond = secondDeltaY / secondDeltaX; |
| double bSecond = aSecond.getY1() - kSecond * aSecond.getX1(); |
| |
| |
| double xIntersection = -100000000; |
| double yIntersection = -100000000; |
| |
| |
| double deltaK = (kFirst - kSecond); |
| |
| |
| if (linesAreAngledAndParallel(kFirst, kSecond)) { |
| return null; |
| } |
| |
| if (Double.isInfinite(deltaK) || (0 == deltaK)) { |
| |
| if (firstDeltaX == secondDeltaX && 0 == firstDeltaX) { |
| return null; |
| } |
| |
| if (firstDeltaY == secondDeltaY && 0 == firstDeltaY) { |
| return null; |
| } |
| |
| if ((0 == firstDeltaX) && (0 == secondDeltaY)) { |
| xIntersection = aFirst.getX1(); |
| yIntersection = aSecond.getY1(); |
| } |
| else if ((0 == secondDeltaX) && (0 == firstDeltaY)) { |
| xIntersection = aSecond.getX1(); |
| yIntersection = aFirst.getY1(); |
| } |
| else { |
| |
| if (0 == firstDeltaX) { |
| xIntersection = aFirst.getX1(); |
| yIntersection = kSecond * xIntersection + bSecond; |
| } |
| else { |
| xIntersection = aSecond.getX1(); |
| yIntersection = kFirst * xIntersection + bFirst; |
| } |
| } |
| |
| |
| } |
| else { |
| xIntersection = (bSecond - bFirst) / deltaK; |
| yIntersection = kFirst * xIntersection + bFirst; |
| } |
| |
| return new Point((int) xIntersection, (int) yIntersection); |
| } |
| |
| private static boolean linesAreAngledAndParallel(double aKFirst, double aKSecond) { |
| return (aKFirst == aKSecond) && (0 != aKFirst); |
| } |
| |
| public static String toString(Line2D aLine) { |
| return aLine.getP1() + ":" + aLine.getP2(); |
| } |
| |
| public static boolean intersects(Rectangle aRectangle, Line2D aLine) { |
| if (aLine == null || aRectangle == null) { |
| return false; |
| } |
| |
| return (!segmentOutsideRectangle(aRectangle, aLine)) && (!segmentInsideRectangle(aRectangle, aLine)); |
| } |
| |
| public static int getPointPositionOnRectangle(Rectangle aRectangle, Point aPoint, int aEpsilon) { |
| final int ERROR_CODE = Integer.MIN_VALUE; |
| |
| if (pointOnBound(getTopOf(aRectangle), aPoint, aEpsilon)) { |
| return TOP; |
| } |
| else if (pointOnBound(getBottomOf(aRectangle), aPoint, aEpsilon)) { |
| return BOTTOM; |
| } |
| else if (pointOnBound(getLeftOf(aRectangle), aPoint, aEpsilon)) { |
| return LEFT; |
| } |
| else if (pointOnBound(getRightOf(aRectangle), aPoint, aEpsilon)) { |
| return RIGHT; |
| } |
| else { |
| return ERROR_CODE; |
| } |
| } |
| |
| private static boolean pointOnBound(Line2D aTop, Point aPoint, int aEpsilon) { |
| return withinRange(aTop.getX1(), aTop.getX2(), aPoint.getX(), aEpsilon) && withinRange(aTop.getY1(), aTop.getY2(), aPoint.getY(), aEpsilon); |
| } |
| |
| private static boolean withinRange(double aLeft, double aRight, double aValue, int aEpsilon) { |
| return ((aLeft - aEpsilon) <= aValue) && ((aRight + aEpsilon) >= aValue); |
| } |
| |
| // public static Point shiftByY(Line2D aLine, Point aPoint, int aPointDeltaY) { |
| // return new Point((int) (aPoint.getX() + getShiftByY(aLine, aPointDeltaY)), (int) (aPoint.getY() + aPointDeltaY)); |
| // } |
| // |
| // public static Point shiftByX(Line2D aLine, Point aPoint, int aPointDeltaX) { |
| // return new Point((int) (aPoint.getX() + aPointDeltaX), (int) (aPoint.getY() + getShiftByX(aLine, aPointDeltaX))); |
| // } |
| |
| public static double getShiftByY(Line2D aLine, double aPointDeltaY) { |
| return aPointDeltaY * ((aLine.getX2() - aLine.getX1()) / (aLine.getY2() - aLine.getY1())); |
| } |
| |
| public static double getShiftByX(Line2D aLine, double aPointDeltaX) { |
| double width = aLine.getX2() - aLine.getX1(); |
| double height = aLine.getY2() - aLine.getY1(); |
| return aPointDeltaX * (height / width); |
| } |
| |
| public static Shape getArrowShape(Line2D line, Point2D intersectionPoint) { |
| final double deltaY = line.getP2().getY() - line.getP1().getY(); |
| final double length = Math.sqrt(Math.pow(deltaY, 2) + Math.pow(line.getP2().getX() - line.getP1().getX(), 2)); |
| |
| double theta = Math.asin(deltaY / length); |
| |
| if (line.getP1().getX() > line.getP2().getX()) { |
| theta = Math.PI - theta; |
| } |
| |
| AffineTransform rotate = AffineTransform.getRotateInstance(theta, myArrowSize, myArrowSize / 2); |
| Shape polygon = rotate.createTransformedShape(myArrowPolygon); |
| |
| AffineTransform move = AffineTransform.getTranslateInstance(intersectionPoint.getX() - myArrowSize, intersectionPoint.getY() - myArrowSize /2); |
| polygon = move.createTransformedShape(polygon); |
| return polygon; |
| } |
| |
| private static class OrientedPoint extends Point { |
| private final int myOrientation; |
| |
| public OrientedPoint(double x, double y, int aOrientation) { |
| super((int) x, (int) y); |
| myOrientation = aOrientation; |
| } |
| |
| public int getOrientation() { |
| return myOrientation; |
| } |
| } |
| |
| public static int getClosestToLineRectangleCorner(Rectangle aRectange, Line2D aSegment) { |
| Point northWest = new OrientedPoint(aRectange.getX(), aRectange.getY(), NORTH_WEST); |
| Point northEast = new OrientedPoint(aRectange.getMaxX(), aRectange.getY(), NORTH_EAST); |
| Point southEast = new OrientedPoint(aRectange.getMaxX(), aRectange.getMaxY(), SOUTH_EAST); |
| Point southWest = new OrientedPoint(aRectange.getX(), aRectange.getMaxY(), SOUTH_WEST); |
| |
| TreeMap sorter = new TreeMap(); |
| |
| sorter.put(getDistance(aSegment, northWest), northWest); |
| sorter.put(getDistance(aSegment, southWest), southWest); |
| sorter.put(getDistance(aSegment, southEast), southEast); |
| sorter.put(getDistance(aSegment, northEast), northEast); |
| |
| return ((OrientedPoint) sorter.get(sorter.firstKey())).getOrientation(); |
| |
| } |
| |
| private static Double getDistance(Line2D aSegment, Point aPoint) { |
| double lenght1 = getLineLength(aSegment.getX1(), aSegment.getY1(), aPoint.getX(), aPoint.getY()); |
| double lenght2 = getLineLength(aSegment.getX2(), aSegment.getY2(), aPoint.getX(), aPoint.getY()); |
| |
| return new Double(lenght1 + lenght2); |
| } |
| |
| public static double getLineLength(double aX1, double aY1, double aX2, double aY2) { |
| double deltaX = aX2 - aX1; |
| double deltaY = aY2 - aY1; |
| |
| return Math.sqrt(deltaX * deltaX + deltaY * deltaY); |
| } |
| |
| public static double cos(Line2D aLine) { |
| final double length = getLineLength(aLine.getX1(), aLine.getY1(), aLine.getX2(), aLine.getY2()); |
| if (length == 0) { |
| throw new IllegalArgumentException(toString(aLine) + " has a zero length"); |
| } |
| |
| double deltaX = aLine.getX2() - aLine.getX1(); |
| return deltaX / length; |
| } |
| |
| public static double sin(Line2D aLine) { |
| final double length = getLineLength(aLine.getX1(), aLine.getY1(), aLine.getX2(), aLine.getY2()); |
| if (length == 0) { |
| throw new IllegalArgumentException(toString(aLine) + " has a zero length"); |
| } |
| |
| double deltaY = aLine.getY2() - aLine.getY1(); |
| return deltaY / length; |
| } |
| |
| } |