/* * (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.Random; 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 FGEBand implements FGEArea { private static final Logger logger = Logger.getLogger(FGEBand.class.getPackage().getName()); public FGELine line1; public FGELine line2; private FGEPoint testPoint; public FGEBand() { super(); } public FGEBand(FGELine aLine1, FGELine aLine2) { super(); this.line1 = new FGELine(aLine1); this.line2 = new FGELine(aLine2); if (!line1.isParallelTo(line2)) { throw new IllegalArgumentException("lines are not parallel"); } computeTestPoint(); } protected void computeTestPoint() { FGEPoint randPoint = null; boolean found = false; while (!found) { Random rand = new Random(); randPoint = new FGEPoint(rand.nextInt(), rand.nextInt()); if (!line1.contains(randPoint) && !line2.contains(randPoint)) { found = true; } } FGESegment segment = new FGESegment(line1.getProjection(randPoint), line2.getProjection(randPoint)); testPoint = segment.getMiddle(); } @Override public boolean containsPoint(FGEPoint p) { return bandContainsPoint(p); } private boolean bandContainsPoint(FGEPoint p) { if (line1.contains(p)) { return true; } if (line2.contains(p)) { return true; } return line1.getPlaneLocation(testPoint) == line1.getPlaneLocation(p) && line2.getPlaneLocation(testPoint) == line2.getPlaneLocation(p); } @Override public boolean containsLine(FGEAbstractLine l) { if (!containsLineIgnoreBounds(l)) { return false; } if (l instanceof FGEHalfLine) { return l.isParallelTo(line1); } if (l instanceof FGESegment) { return true; } if (l instanceof FGELine) { return l.isParallelTo(line1); } logger.warning("Unexpected: " + l); return false; } public boolean containsLineIgnoreBounds(FGEAbstractLine l) { return bandContainsPoint(l.getP1()) && bandContainsPoint(l.getP2()); } @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); } if (a instanceof FGEBand) { return containsLine(((FGEBand) a).line1) && containsLine(((FGEBand) a).line2); } return false; } @Override public FGEArea exclusiveOr(FGEArea area) { return new FGEExclusiveOrArea(this, area); } @Override public FGEArea intersect(FGEArea area) { // There is a big problem if we uncomment this here // We really need to build many JUnit tests in order to cover all area computation // Many cases are wrong !!! // 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 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); } /** * 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 FGEBand clone() { try { return (FGEBand) super.clone(); } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(); } } @Override public FGEArea transform(AffineTransform t) { FGELine l1 = line1.transform(t); FGELine l2 = line1.transform(t); if (l1.overlap(l2)) { return l1; } else { return new FGEBand(l1, l2); } } @Override public void paint(FGEGraphics g) { FGERectangle bounds = g.getGraphicalRepresentation().getNormalizedBounds(); g.useDefaultBackgroundStyle(); bounds.intersect(this).paint(g); g.useDefaultForegroundStyle(); line1.paint(g); line2.paint(g); /* System.out.println("paint() for "+this); FGERectangle bounds = g.getGraphicalRepresentation().getLogicalBounds(); Vector<FGEPoint> pts = new Vector<FGEPoint>(); FGEArea a1 = bounds.intersect(new FGELine(line1)); if (a1 instanceof FGESegment) { FGESegment s1 = (FGESegment)a1; pts.add(s1.getP1()); pts.add(s1.getP2()); } else if (a1 instanceof FGEPoint) { pts.add((FGEPoint)a1); } else if (a1 instanceof FGEEmptyArea) { } else { logger.warning("Unexpected intersection: "+a1); } FGEArea a2 = bounds.intersect(new FGELine(line2)); if (a2 instanceof FGESegment) { FGESegment s2 = (FGESegment)a2; pts.add(s2.getP1()); pts.add(s2.getP2()); } else if (a2 instanceof FGEPoint) { pts.add((FGEPoint)a2); } else if (a2 instanceof FGEEmptyArea) { } else { logger.warning("Unexpected intersection: "+a2); } if (containsPoint(bounds.getNorthEastPt())) pts.add(bounds.getNorthEastPt()); if (containsPoint(bounds.getNorthWestPt())) pts.add(bounds.getNorthWestPt()); if (containsPoint(bounds.getSouthEastPt())) pts.add(bounds.getSouthEastPt()); if (containsPoint(bounds.getSouthWestPt())) pts.add(bounds.getSouthWestPt()); g.useDefaultBackgroundStyle(); FGEPolygon.makeArea(Filling.FILLED,pts).paint(g); g.useDefaultForegroundStyle(); line1.paint(g); line2.paint(g);*/ } @Override public String toString() { return "FGEBand: " + line1.toString() + " " + line2.toString(); } public static void main(String[] args) { FGELine line0 = new FGELine(0, 0, 10, 0); FGELine line1 = new FGELine(0, 1, 10, 1); FGELine line2 = new FGELine(0, 2, 10, 2); FGELine line3 = new FGELine(0, 3, 10, 3); FGEBand b1 = new FGEBand(line3, line0); FGEBand b2 = new FGEBand(line2, line1); System.out.println("intersection: " + b2.intersect(b1)); FGEHalfLine hl3 = new FGEHalfLine(new FGEPoint(1, 3), new FGEPoint(-1, 3)); FGEHalfLine hl2 = new FGEHalfLine(new FGEPoint(0, 2), new FGEPoint(2, 2)); FGEHalfLine hl1 = new FGEHalfLine(new FGEPoint(0, 1), new FGEPoint(2, 1)); FGEHalfLine hl0 = new FGEHalfLine(new FGEPoint(1, 0), new FGEPoint(-1, 0)); FGEHalfBand hb1 = new FGEHalfBand(hl3, hl0); FGEHalfBand hb2 = new FGEHalfBand(hl2, hl1); System.out.println("intersection: " + hb2.intersect(hb1)); System.out.println("hb1=" + hb1); System.out.println("hb1.intersect(hb1.halfPlane)=" + hb1.intersect(hb1.halfPlane)); } protected FGEArea computeHalfPlaneIntersection(FGEHalfPlane hp) { if (hp.containsArea(this)) { return clone(); } if (hp.line.isParallelTo(line1)) { if (hp.containsLine(line1)) { if (hp.containsLine(line2)) { return clone(); } else { return new FGEBand(line1, hp.line); } } else { if (hp.containsLine(line2)) { return new FGEBand(line2, hp.line); } else { return new FGEEmptyArea(); } } } else { try { FGEPoint i1 = line1.getLineIntersection(hp.line); FGEPoint i2 = line2.getLineIntersection(hp.line); FGEPoint pp1, pp2; if (!hp.line.containsPoint(line1.getP1())) { if (hp.containsPoint(line1.getP1())) { pp1 = line1.getP1(); } else { pp1 = FGEAbstractLine.getOppositePoint(line1.getP1(), i1); } } else { if (hp.containsPoint(line1.getP2())) { pp1 = line1.getP2(); } else { pp1 = FGEAbstractLine.getOppositePoint(line1.getP2(), i1); } } if (!hp.line.containsPoint(line2.getP1())) { if (hp.containsPoint(line2.getP1())) { pp2 = line2.getP1(); } else { pp2 = FGEAbstractLine.getOppositePoint(line2.getP1(), i2); } } else { if (hp.containsPoint(line2.getP2())) { pp2 = line2.getP2(); } else { pp2 = FGEAbstractLine.getOppositePoint(line2.getP2(), i2); } } FGEHalfLine hl1 = new FGEHalfLine(i1, pp1); FGEHalfLine hl2 = new FGEHalfLine(i2, pp2); if (hl1.overlap(hl2)) { return new FGEEmptyArea(); } else { return new FGEHalfBand(hl1, hl2); } } catch (ParallelLinesException e) { // Cannot happen e.printStackTrace(); return null; } } } protected FGEArea computeHalfBandIntersection(FGEHalfBand hb) { FGEArea returned = hb.embeddingBand.computeBandIntersection(this); FGEArea reply = returned.intersect(hb.halfPlane); return reply; } protected FGEArea computeBandIntersection(FGEBand band) { FGEArea returned = null; if (!line1.isParallelTo(band.line1)) { try { if (line1.isOrthogonalTo(band.line1)) { FGEPoint p1 = line1.getLineIntersection(band.line1); FGEPoint p2 = line2.getLineIntersection(band.line2); returned = new FGERectangle(p1, p2, Filling.FILLED); } else { returned = FGEPolygon.makeArea(Filling.FILLED, line1.getLineIntersection(band.line1), line1.getLineIntersection(band.line2), line2.getLineIntersection(band.line2), line2.getLineIntersection(band.line1)); } } catch (ParallelLinesException e) { // Cannot happen e.printStackTrace(); return null; } } else { if (containsArea(band)) { returned = band.clone(); } else if (band.containsArea(this)) { returned = this.clone(); } else if (containsLineIgnoreBounds(band.line1)) { if (containsLine(band.line2)) { returned = band.clone(); } else { if (band.containsLineIgnoreBounds(line2)) { returned = new FGEBand(band.line1, line2); // System.out.println("Built from Parallel lines "+returned); } else { returned = new FGEBand(band.line1, line1); // System.out.println("Built from Parallel lines "+returned); } } } else { if (containsLineIgnoreBounds(band.line2)) { if (band.containsLineIgnoreBounds(line2)) { returned = new FGEBand(band.line2, line2); // System.out.println("Built from Parallel lines "+returned); } else { returned = new FGEBand(band.line2, line1); // System.out.println("Built from Parallel lines "+returned); } } else { returned = new FGEEmptyArea(); } } } if (returned == null) { return new FGEEmptyArea(); } /*if (band instanceof FGEHalfBand) { return returned.intersect(((FGEHalfBand)band).halfPlane); }*/ // if (containsArea(returned) && band.containsArea(returned)) return returned; return returned; // return new FGEEmptyArea(); } private FGEArea computeLineIntersection(FGEAbstractLine aLine) { FGEHalfPlane hp1 = new FGEHalfPlane(line1, line2.getP1()); FGEArea a1 = hp1.intersect(aLine); if (a1 instanceof FGEEmptyArea) { return a1; } FGEHalfPlane hp2 = new FGEHalfPlane(line2, line1.getP1()); return hp2.intersect(a1); } @Override public FGEPoint getNearestPoint(FGEPoint aPoint) { if (containsPoint(aPoint)) { return aPoint.clone(); } else { return FGEPoint.getNearestPoint(aPoint, line1.getProjection(aPoint), line2.getProjection(aPoint)); } } @Override public boolean equals(Object obj) { if (obj instanceof FGEBand) { FGEBand b = (FGEBand) obj; return (line1.overlap(b.line1) || line1.overlap(b.line2)) && (line2.overlap(b.line1) || line2.overlap(b.line2)); } return false; } @Override public FGEArea getOrthogonalPerspectiveArea(SimplifiedCardinalDirection orientation) { if (orientation == SimplifiedCardinalDirection.NORTH) { if (line1.isVertical()) { return clone(); } else { return FGEAbstractLine.getNorthernLine(line1, line2); } } else if (orientation == SimplifiedCardinalDirection.SOUTH) { if (line1.isVertical()) { return clone(); } else { return FGEAbstractLine.getSouthernLine(line1, line2); } } else if (orientation == SimplifiedCardinalDirection.EAST) { if (line1.isHorizontal()) { return clone(); } else { return FGEAbstractLine.getEasternLine(line1, line2); } } else if (orientation == SimplifiedCardinalDirection.WEST) { if (line1.isHorizontal()) { return clone(); } else { return FGEAbstractLine.getWesternLine(line1, line2); } } return null; } @Override public FGEBand 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); } }