/* * (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.Logger; import org.openflexo.fge.geom.FGEAbstractLine; import org.openflexo.fge.geom.FGEArc; import org.openflexo.fge.geom.FGEGeometricObject.Filling; import org.openflexo.fge.geom.FGEGeometricObject.SimplifiedCardinalDirection; import org.openflexo.fge.geom.FGELine; import org.openflexo.fge.geom.FGEPoint; import org.openflexo.fge.geom.FGEPolygon; import org.openflexo.fge.geom.FGERectangle; import org.openflexo.fge.geom.FGESegment; import org.openflexo.fge.geom.FGEShape; import org.openflexo.fge.geom.ParallelLinesException; import org.openflexo.fge.graphics.FGEGraphics; public class FGEHalfBand implements FGEArea { private static final Logger logger = Logger.getLogger(FGEHalfBand.class.getPackage().getName()); // Storage data public FGEHalfLine halfLine1; public FGEHalfLine halfLine2; // Computed data public FGESegment limit; public FGEHalfPlane halfPlane; public FGEBand embeddingBand; public FGEHalfBand() { super(); } public FGEHalfBand(FGEHalfLine anHalfLine1, FGEHalfLine anHalfLine2) { super(); this.halfLine1 = anHalfLine1; this.halfLine2 = anHalfLine2; updateParametersFromHalflines(); } public FGEHalfBand(FGELine line1, FGELine line2, FGEHalfPlane hp) { this((FGEHalfLine) hp.intersect(line1), (FGEHalfLine) hp.intersect(line2)); } public FGEHalfBand(FGESegment segment, SimplifiedCardinalDirection orientation) { this(segment.getP1().getOrthogonalPerspectiveArea(orientation), segment.getP2().getOrthogonalPerspectiveArea(orientation)); } private void updateParametersFromHalflines() { if (halfLine1.overlap(halfLine2)) { throw new IllegalArgumentException("lines are overlaping halfLine1=" + halfLine1 + " halfLine2=" + halfLine2); } if (!halfLine1.isParallelTo(halfLine2)) { throw new IllegalArgumentException("lines are not parallel: " + halfLine1 + " and " + halfLine2); } limit = new FGESegment(halfLine1.getLimit(), halfLine2.getLimit()); halfPlane = new FGEHalfPlane(new FGELine(limit), halfLine1.getOpposite()); if (!halfPlane.containsPoint(halfLine2.getOpposite())) { throw new IllegalArgumentException("half lines have opposite directions"); } embeddingBand = new FGEBand(new FGELine(halfLine1), new FGELine(halfLine2)); } public static void main(String[] args) { FGEHalfLine hl1 = new FGEHalfLine(new FGEPoint(1, 2), new FGEPoint(4, 2)); FGEHalfLine hl2 = new FGEHalfLine(new FGEPoint(0, 1), new FGEPoint(8, 1)); FGEHalfBand test = new FGEHalfBand(hl1, hl2); System.out.println("test=" + test); FGEPoint p = new FGEPoint(5, 1.5); System.out.println("contains " + p + " = " + test.containsPoint(p)); p = new FGEPoint(-0.5, 1.5); System.out.println("contains " + p + " = " + test.containsPoint(p)); p = new FGEPoint(5, 2.5); System.out.println("contains " + p + " = " + test.containsPoint(p)); AffineTransform at = AffineTransform.getScaleInstance(100, 200); System.out.println("test2=" + test.transform(at)); } @Override public boolean containsPoint(FGEPoint p) { if (!embeddingBand.containsPoint(p)) { return false; } return halfPlane.containsPoint(p); } @Override public boolean containsLine(FGEAbstractLine l) { if (!embeddingBand.containsLine(l)) { return false; } return halfPlane.containsLine(l); } /** * 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 FGEHalfBand clone() { try { return (FGEHalfBand) super.clone(); } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(); } } @Override public FGEArea transform(AffineTransform t) { FGEHalfLine l1 = halfLine1.transform(t); FGEHalfLine l2 = halfLine2.transform(t); if (l1.overlap(l2)) { return l1.intersect(l2); } else { return new FGEHalfBand(l1, l2); } } @Override public String toString() { return "FGEHalfBand: " + halfLine1.toString() + " " + halfLine2.toString(); } /*public void paint(FGEGraphics g) { super.paint(g); halfLine1.paint(g); halfLine2.paint(g); limit.paint(g); }*/ @Override public void paint(FGEGraphics g) { // System.out.println("paint() for "+this); FGERectangle bounds = g.getGraphicalRepresentation().getNormalizedBounds(); FGEArea l1 = bounds.intersect(halfLine1); FGEArea l2 = bounds.intersect(halfLine2); // System.out.println("l1="+l1); // System.out.println("l2="+l2); if (l1 instanceof FGESegment && l2 instanceof FGESegment) { FGESegment s1 = (FGESegment) l1; FGESegment s2 = (FGESegment) l2; g.useDefaultBackgroundStyle(); if (new FGESegment(s1.getP1(), s2.getP1()).intersectsInsideSegment(new FGESegment(s1.getP2(), s2.getP2()))) { FGEPolygon.makeArea(Filling.FILLED, s1.getP1(), s1.getP2(), s2.getP1(), s2.getP2()).paint(g); } else { FGEPolygon.makeArea(Filling.FILLED, s1.getP1(), s1.getP2(), s2.getP2(), s2.getP1()).paint(g); } } g.useDefaultForegroundStyle(); halfLine1.paint(g); halfLine2.paint(g); limit.paint(g); } protected FGEArea computeBandIntersection(FGEBand band) { FGEArea returned = embeddingBand.computeBandIntersection(band); FGEArea reply = returned.intersect(halfPlane); return reply; } protected FGEArea computeHalfBandIntersection(FGEHalfBand band) { FGEArea returned = embeddingBand.computeHalfBandIntersection(band); FGEArea reply = returned.intersect(halfPlane); return reply; } private FGEArea computeLineIntersection(FGEAbstractLine aLine) { FGEArea returned = embeddingBand.intersect(aLine); return returned.intersect(halfPlane); } protected FGEArea computeHalfPlaneIntersection(FGEHalfPlane anHalfPlane) { // FGEArea returned = embeddingBand.intersect(anHalfPlane); // return returned.intersect(halfPlane); if (anHalfPlane.containsArea(this)) { return clone(); } if (anHalfPlane.line.isParallelTo(halfLine1)) { if (anHalfPlane.containsLine(halfLine1)) { if (anHalfPlane.containsLine(halfLine2)) { return clone(); } else { FGEPoint inters; try { inters = anHalfPlane.line.getLineIntersection(limit); // We must project opposite pt on line, parallel to limit FGELine l = halfLine1.getParallelLine(inters); FGELine l2 = limit.getParallelLine(halfLine1.getOpposite()); FGEPoint opp = l.getLineIntersection(l2); return new FGEHalfBand(halfLine1, new FGEHalfLine(inters, opp)); } catch (ParallelLinesException e) { // cannot happen e.printStackTrace(); return null; } catch (IllegalArgumentException e) { logger.warning("IllegalArgumentException raised while computing intersection of " + this + " and " + anHalfPlane); e.printStackTrace(); return new FGEEmptyArea(); } } } else { if (anHalfPlane.containsLine(halfLine2)) { try { FGEPoint inters = anHalfPlane.line.getLineIntersection(limit); // We must project opposite pt on line, parallel to limit FGELine l = halfLine2.getParallelLine(inters); FGELine l2 = limit.getParallelLine(halfLine2.getOpposite()); FGEPoint opp = l.getLineIntersection(l2); return new FGEHalfBand(halfLine2, new FGEHalfLine(inters, opp)); // TODO: this is not exact, we must project opposite pt on line, parallel to limit /*logger.warning("Please implement this better"); FGEPoint opp = halfLine2.getProjection(halfLine2.getOpposite()); return new FGEHalfBand(halfLine2,new FGEHalfLine(inters,opp));*/ } catch (ParallelLinesException e) { // cannot happen e.printStackTrace(); return null; } } else { return new FGEEmptyArea(); } } } FGEArea returned = embeddingBand.computeHalfPlaneIntersection(anHalfPlane); if (returned instanceof FGEHalfBand) { // we must now intersect with halfplane FGEHalfBand hb = (FGEHalfBand) returned; FGEPoint pp1 = hb.halfLine1.getLineIntersection(halfPlane.line); FGEPoint pp2 = hb.halfLine2.getLineIntersection(halfPlane.line); if (hb.halfLine1.containsPoint(pp1) && hb.halfLine2.containsPoint(pp2)) { FGEArea returnThis = FGEPolygon.makeArea(Filling.FILLED, hb.halfLine1.getLimit(), hb.halfLine2.getLimit(), pp2, pp1); return returnThis; } else if (hb.halfLine1.containsPoint(pp1) || hb.halfLine2.containsPoint(pp2)) { return new FGEIntersectionArea(returned, this); } else { // No intersection, we have all or nothing if (halfPlane.containsLine(hb.halfLine1) && halfPlane.containsLine(hb.halfLine2)) { return hb; } return new FGEEmptyArea(); } } return returned; } @Override public boolean equals(Object obj) { if (obj instanceof FGEHalfBand) { FGEHalfBand hb = (FGEHalfBand) obj; return hb.halfLine1.equals(halfLine1) && hb.halfLine2.equals(halfLine2) || hb.halfLine2.equals(halfLine1) && hb.halfLine1.equals(halfLine2); } return false; } @Override public boolean containsArea(FGEArea a) { if (a instanceof FGEPoint) { return containsPoint((FGEPoint) a); } if (a instanceof FGELine) { return containsLine((FGELine) a); } if (a instanceof FGEShape) { return FGEShape.AreaComputation.isShapeContainedInArea((FGEShape<?>) a, this); } if (a instanceof FGEHalfBand) { return containsLine(((FGEHalfBand) a).halfLine1) && containsLine(((FGEHalfBand) a).halfLine2); } return false; } @Override public FGEArea exclusiveOr(FGEArea area) { return new FGEExclusiveOrArea(this, area); } @Override public FGEArea intersect(FGEArea area) { if (area.containsArea(this)) { return this.clone(); } if (containsArea(area)) { return area.clone(); } if (area instanceof FGEAbstractLine) { return computeLineIntersection((FGEAbstractLine) area); } if (area instanceof FGERectangle) { return area.intersect(this); } if (area instanceof FGEPolygon) { return area.intersect(this); } if (area instanceof FGEArc) { return area.intersect(this); } if (area instanceof FGEBand) { return computeBandIntersection((FGEBand) area); } if (area instanceof FGEHalfBand) { return computeHalfBandIntersection((FGEHalfBand) area); } if (area instanceof FGEHalfPlane) { return computeHalfPlaneIntersection((FGEHalfPlane) area); } FGEIntersectionArea returned = new FGEIntersectionArea(this, area); if (returned.isDevelopable()) { return returned.makeDevelopped(); } else { return returned; } } @Override public FGEPoint getNearestPoint(FGEPoint aPoint) { if (containsPoint(aPoint)) { return aPoint.clone(); } else { return FGEPoint.getNearestPoint(aPoint, halfLine1.getNearestPoint(aPoint), halfLine2.getNearestPoint(aPoint)); } } @Override public FGEArea substract(FGEArea area, boolean isStrict) { return new FGESubstractionArea(this, area, isStrict); } @Override public FGEArea union(FGEArea area) { if (containsArea(area)) { return clone(); } if (area.containsArea(this)) { return area.clone(); } return new FGEUnionArea(this, area); } @Override public FGEArea getOrthogonalPerspectiveArea(SimplifiedCardinalDirection orientation) { if (orientation == SimplifiedCardinalDirection.NORTH) { if (halfLine1.isVertical()) { return clone(); } else { FGEPoint p = limit.getP1().y > limit.getP2().y ? limit.getP1() : limit.getP2(); return new FGEHalfPlane(FGELine.makeVerticalLine(p), halfLine1.getOpposite()); } } else if (orientation == SimplifiedCardinalDirection.SOUTH) { if (halfLine1.isVertical()) { return clone(); } else { FGEPoint p = limit.getP1().y > limit.getP2().y ? limit.getP2() : limit.getP1(); return new FGEHalfPlane(FGELine.makeVerticalLine(p), halfLine1.getOpposite()); } } else if (orientation == SimplifiedCardinalDirection.EAST) { if (halfLine1.isHorizontal()) { return clone(); } else { FGEPoint p = limit.getP1().x > limit.getP2().x ? limit.getP2() : limit.getP1(); return new FGEHalfPlane(FGELine.makeHorizontalLine(p), halfLine1.getOpposite()); } } else if (orientation == SimplifiedCardinalDirection.WEST) { if (halfLine1.isHorizontal()) { return clone(); } else { FGEPoint p = limit.getP1().x > limit.getP2().x ? limit.getP1() : limit.getP2(); return new FGEHalfPlane(FGELine.makeHorizontalLine(p), halfLine1.getOpposite()); } } return null; } @Override public FGEHalfBand 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; } /** * Return nearest point from point "from" following supplied orientation * * Returns null if no intersection was found * * @param from * point from which we are coming to area * @param orientation * orientation we are coming from * @return */ @Override public FGEPoint nearestPointFrom(FGEPoint from, SimplifiedCardinalDirection orientation) { FGEHalfLine hl = FGEHalfLine.makeHalfLine(from, orientation); FGEArea intersect = intersect(hl); return intersect.nearestPointFrom(from, orientation); } }