/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenFlexo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package org.openflexo.fge.geom.area; import java.awt.geom.AffineTransform; import java.util.logging.Level; import java.util.logging.Logger; import org.openflexo.fge.geom.FGEAbstractLine; import org.openflexo.fge.geom.FGELine; import org.openflexo.fge.geom.FGEPoint; import org.openflexo.fge.geom.FGERectangle; import org.openflexo.fge.geom.FGESegment; import org.openflexo.fge.geom.ParallelLinesException; import org.openflexo.fge.graphics.FGEGraphics; public class FGEHalfLine extends FGEAbstractLine<FGEHalfLine> { private static final Logger logger = Logger.getLogger(FGEHalfLine.class.getPackage().getName()); public FGEHalfLine(double limitX, double limitY, double px, double py) { super(limitX, limitY, px, py); } // oppositePoint if the infinite direction public FGEHalfLine(FGEPoint limit, FGEPoint oppositePoint) { super(limit, oppositePoint); } public static FGEHalfLine makeHalfLine(FGEPoint limit, SimplifiedCardinalDirection orientation) { FGEHalfLine hl = null; switch (orientation) { case NORTH: hl = new FGEHalfLine(limit, limit.transform(AffineTransform.getTranslateInstance(0, -1))); break; case SOUTH: hl = new FGEHalfLine(limit, limit.transform(AffineTransform.getTranslateInstance(0, 1))); break; case EAST: hl = new FGEHalfLine(limit, limit.transform(AffineTransform.getTranslateInstance(1, 0))); break; case WEST: hl = new FGEHalfLine(limit, limit.transform(AffineTransform.getTranslateInstance(-1, 0))); break; default: break; } return hl; } public FGEHalfLine() { super(); } public FGEPoint getLimit() { return getP1(); } public void setLimit(FGEPoint aPoint) { setP1(aPoint); } @Override public boolean containsLine(FGEAbstractLine l) { if (!overlap(l)) { return false; } if (!(containsPoint(l.getP1()) && containsPoint(l.getP2()))) { return false; } if (l instanceof FGEHalfLine) { return getHalfPlane().intersect(((FGEHalfLine) l).getHalfPlane()) instanceof FGEHalfPlane; } if (l instanceof FGESegment) { return true; } // If this is a line this is false return false; } @Override public boolean contains(FGEPoint p) { // First see if located on line if (!_containsPointIgnoreBounds(p)) { return false; } // Now check bounds if (getB() != 0) { FGEPoint pp1 = getP1(); FGEPoint pp2 = getP2(); if (pp1.x > pp2.x) { pp1 = getP2(); pp2 = getP1(); } if (p.x >= pp1.x) { if (p.x > pp2.x) { return pp2.equals(getOpposite()); } else { return true; } } else { return pp1.equals(getOpposite()); } } else { FGEPoint pp1 = getP1(); FGEPoint pp2 = getP2(); if (pp1.y > pp2.y) { pp1 = getP2(); pp2 = getP1(); } if (p.y >= pp1.y) { if (p.y > pp2.y) { return pp2.equals(getOpposite()); } else { return true; } } else { return pp1.equals(getOpposite()); } } } public FGEPoint getOpposite() { return getP2(); } public void setOpposite(FGEPoint aPoint) { setP2(aPoint); } public FGEHalfPlane getHalfPlane() { return new FGEHalfPlane(getOrthogonalLine(getLimit()), getOpposite()); } @Override public boolean equals(Object obj) { if (obj instanceof FGEHalfLine) { FGEHalfLine hl = (FGEHalfLine) obj; if (!overlap(hl)) { return false; } return getLimit().equals(hl.getLimit()) && hl.getNearestPointOnHalfLine(getOpposite()).equals(getOpposite()); } return false; } @Override public FGEPoint getNearestPoint(FGEPoint p) { return getNearestPointOnHalfLine(p); } /** * Return nearest point on half line * * If orthogonal projection of supplied point on segment is inside the halfline, return this projection. Otherwise, return half line * limit * * @param p * @return */ public FGEPoint getNearestPointOnHalfLine(FGEPoint p) { FGEPoint projection = getProjection(p); if (contains(projection)) { return projection; } else { return getLimit(); } } @Override public FGEHalfLine transform(AffineTransform t) { return new FGEHalfLine(getLimit().transform(t), getOpposite().transform(t)); } @Override public String toString() { return "FGEHalfLine: [" + getLimit() + "," + getOpposite() + ")"; } protected FGEPoint getOppositePointProjection(FGEGraphics g) { FGERectangle bounds = g.getGraphicalRepresentation().getNormalizedBounds(); FGEArea area = bounds.intersect(new FGELine(getP1(), getP2())); if (area instanceof FGESegment) { // We must now find which point to choose FGEPoint pp1 = ((FGESegment) area).getP1(); FGEPoint pp2 = ((FGESegment) area).getP2(); if (containsPoint(pp1) && !containsPoint(pp2)) { return pp1; } if (containsPoint(pp2) && !containsPoint(pp1)) { return pp2; } logger.warning("Unexpected situation here..."); return getOpposite(); /* FGEPoint pp1 = ((FGESegment)area).getP1(); FGEPoint pp2 = ((FGESegment)area).getP2(); if (getP1().x < getP2().x) { if (pp1.x<pp2.x) return pp2; else return pp1; } else if (getP1().x > getP2().x) { if (pp1.x>pp2.x) return pp2; else return pp1; } else { if (getP1().y < getP2().y) { if (pp1.y<pp2.y) return pp2; else return pp1; } else if (getP1().y > getP2().y) { if (pp1.y>pp2.y) return pp2; else return pp1; } else return pp1; }*/ } logger.warning("Unexpected situation here..."); return null; } @Override public void paint(FGEGraphics g) { g.useDefaultForegroundStyle(); FGEPoint limit = getLimit(); FGEPoint p2 = getOppositePointProjection(g); if (limit != null && p2 != null) { new FGESegment(limit, p2).paint(g); } /* FGERectangle bounds = g.getGraphicalRepresentation().getLogicalBounds(); FGEArea area = bounds.intersect(new FGELine(getP1(),getP2())); FGEPoint pp1 = ((FGESegment)area).getP1(); FGEPoint pp2 = ((FGESegment)area).getP2(); pp1.paint(g); pp2.paint(g); (new FGESegment(getP1(),getP2())).paint(g);*/ } @Override protected FGEArea computeLineIntersection(FGEAbstractLine line) { if (logger.isLoggable(Level.FINE)) { logger.fine("computeIntersection() between " + this + "\n and " + line + " overlap=" + overlap(line)); } if (overlap(line)) { if (line instanceof FGEHalfLine) { return _compute_hl_hl_Intersection(this, (FGEHalfLine) line); } else if (line instanceof FGESegment) { return _compute_hl_segment_Intersection(this, (FGESegment) line); } else { return clone(); } } else if (isParallelTo(line)) { return new FGEEmptyArea(); } else { FGEPoint returned; try { returned = getLineIntersection(line); if (containsPoint(returned) && line.containsPoint(returned)) { return returned; } } catch (ParallelLinesException e) { // cannot happen } return new FGEEmptyArea(); } } private static FGEArea _compute_hl_hl_Intersection(FGEHalfLine hl1, FGEHalfLine hl2) { FGEHalfPlane hp1 = hl1.getHalfPlane(); FGEHalfPlane hp2 = hl2.getHalfPlane(); FGEArea intersect = hp1.intersect(hp2); if (intersect instanceof FGEEmptyArea) { return new FGEEmptyArea(); } else if (intersect instanceof FGEBand) { return new FGESegment(hl1.getLimit(), hl2.getLimit()); } else if (intersect.equals(hp1)) { return hl1.clone(); } else if (intersect.equals(hp2)) { return hl2.clone(); } else if (intersect instanceof FGELine) { // The 2 halfline are adjacent by their limit if (hl1.getLimit().equals(hl2.getLimit())) { return hl1; } else { logger.warning("Unexpected line intersection: " + intersect + " while computing intersection of " + hl1 + " and " + hl2); return new FGEEmptyArea(); } } else { logger.warning("Unexpected intersection: " + intersect + " while computing intersection of " + hl1 + " and " + hl2); return new FGEEmptyArea(); } } /** * Creates a new object of the same class and with the same contents as this object. * * @return a clone of this instance. * @exception OutOfMemoryError * if there is not enough memory. * @see java.lang.Cloneable * @since 1.2 */ @Override public FGEHalfLine clone() { return (FGEHalfLine) super.clone(); } @Override public FGEArea getOrthogonalPerspectiveArea(SimplifiedCardinalDirection orientation) { if (orientation == SimplifiedCardinalDirection.NORTH) { if (isVertical()) { return clone(); } else { return new FGEHalfPlane(FGELine.makeVerticalLine(getLimit()), getOpposite()); } } else if (orientation == SimplifiedCardinalDirection.SOUTH) { if (isVertical()) { return clone(); } else { return new FGEHalfPlane(FGELine.makeVerticalLine(getLimit()), getOpposite()); } } else if (orientation == SimplifiedCardinalDirection.EAST) { if (isHorizontal()) { return clone(); } else { return new FGEHalfPlane(FGELine.makeHorizontalLine(getLimit()), getOpposite()); } } else if (orientation == SimplifiedCardinalDirection.WEST) { if (isHorizontal()) { return clone(); } else { return new FGEHalfPlane(FGELine.makeHorizontalLine(getLimit()), getOpposite()); } } return null; } @Override public FGEHalfLine getAnchorAreaFrom(SimplifiedCardinalDirection direction) { return clone(); } /** * This area is infinite, so always return false */ @Override public final boolean isFinite() { return false; } /** * This area is infinite, so always return null */ @Override public final FGERectangle getEmbeddingBounds() { return null; } }