/*
* (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.FGEGeometricObject.CardinalDirection;
import org.openflexo.fge.geom.FGEGeometricObject.SimplifiedCardinalDirection;
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.FGEShape;
import org.openflexo.fge.graphics.FGEGraphics;
public class FGEHalfPlane implements FGEArea {
private static final Logger logger = Logger.getLogger(FGEHalfPlane.class.getPackage().getName());
public FGELine line;
public FGEPoint testPoint; // located on the "good" side
public FGEHalfPlane() {
super();
}
public FGEHalfPlane(FGELine line, FGEPoint testPoint) {
super();
this.line = line;
this.testPoint = testPoint;
}
public FGEHalfPlane(FGEAbstractLine line, FGEPoint testPoint) {
super();
this.line = new FGELine(line);
this.testPoint = testPoint;
}
public static FGEHalfPlane makeFGEHalfPlane(FGEPoint point, CardinalDirection orientation) {
FGELine line;
FGEPoint testPoint;
if (orientation == CardinalDirection.NORTH) {
line = FGELine.makeHorizontalLine(point);
testPoint = new FGEPoint(point);
testPoint.y -= 1;
} else if (orientation == CardinalDirection.EAST) {
line = FGELine.makeVerticalLine(point);
testPoint = new FGEPoint(point);
testPoint.x += 1;
} else if (orientation == CardinalDirection.SOUTH) {
line = FGELine.makeHorizontalLine(point);
testPoint = new FGEPoint(point);
testPoint.y += 1;
} else /* orientation == CardinalDirection.WEST */{
line = FGELine.makeVerticalLine(point);
testPoint = new FGEPoint(point);
testPoint.x -= 1;
}
return new FGEHalfPlane(line, testPoint);
}
@Override
public boolean containsPoint(FGEPoint p) {
if (line.contains(p)) {
return true;
}
if (line.getPlaneLocation(testPoint) == line.getPlaneLocation(p)) {
return true;
}
return false;
}
@Override
public boolean containsLine(FGEAbstractLine l) {
if (!(containsPoint(l.getP1()) && containsPoint(l.getP2()))) {
return false;
}
if (l instanceof FGEHalfLine) {
if (l.isParallelTo(line)) {
return true;
}
return !l.containsPoint(line.getLineIntersection(l)) || line.containsPoint(((FGEHalfLine) l).getLimit());
} else if (l instanceof FGESegment) {
return true;
} else if (l instanceof FGELine) {
return l.isParallelTo(line);
}
logger.warning("Unexpected: " + l);
return false;
}
public boolean containsHalfBand(FGEHalfBand hb) {
return containsLine(hb.halfLine1) && containsLine(hb.halfLine2);
}
public boolean containsBand(FGEBand band) {
return containsLine(band.line1) && containsLine(band.line2);
}
@Override
public boolean containsArea(FGEArea a) {
if (a instanceof FGEPoint) {
return containsPoint((FGEPoint) a);
}
if (a instanceof FGEAbstractLine) {
return containsLine((FGEAbstractLine) a);
}
if (a instanceof FGEHalfBand) {
return containsHalfBand((FGEHalfBand) a);
}
if (a instanceof FGEBand) {
return containsBand((FGEBand) a);
}
if (a instanceof FGEShape) {
return FGEShape.AreaComputation.isShapeContainedInArea((FGEShape<?>) a, this);
}
if (a instanceof FGEHalfPlane) {
return containsHalfPlane((FGEHalfPlane) a);
}
return false;
}
public FGEHalfLine getHalfLine() {
FGEPoint p = line.getProjection(testPoint);
return new FGEHalfLine(p, testPoint);
}
private boolean containsHalfPlane(FGEHalfPlane hp) {
if (line.overlap(hp.line)) {
if (containsPoint(hp.testPoint)) {
return true;
} else {
return false; // Only line is common
}
} else if (line.isParallelTo(hp.line)) {
if (containsLine(hp.line) && !hp.containsLine(line)) {
return true;
}
}
return false;
}
private FGEArea computeLineIntersection(FGEAbstractLine aLine) {
if (aLine instanceof FGELine) {
if (aLine.equals(line)) {
return aLine.clone();
}
if (aLine.isParallelTo(line)) {
if (containsPoint(aLine.getP1())) {
return aLine.clone();
} else {
return new FGEEmptyArea();
}
} else {
FGEPoint limit = line.getLineIntersection(aLine);
if (limit.equals(aLine.getP1())) {
if (containsPoint(aLine.getP2())) {
return new FGEHalfLine(limit, aLine.getP2());
} else {
return new FGEHalfLine(limit, FGEAbstractLine.getOppositePoint(aLine.getP2(), limit));
}
} else if (containsPoint(aLine.getP1())) {
return new FGEHalfLine(limit, aLine.getP1());
} else {
return new FGEHalfLine(limit, FGEAbstractLine.getOppositePoint(aLine.getP1(), limit));
}
}
} else if (aLine instanceof FGEHalfLine) {
FGEHalfLine hl = (FGEHalfLine) aLine;
if (hl.overlap(line)) {
return hl.clone();
}
if (hl.isParallelTo(line)) {
if (containsPoint(hl.getP1())) {
return hl.clone();
} else {
return new FGEEmptyArea();
}
} else {
FGEPoint intersect = hl.getLineIntersection(line);
FGEPoint limit = hl.getLimit();
if (intersect.equals(limit)) {
return intersect.clone();
}
FGEPoint opposite = FGEAbstractLine.getOppositePoint(limit, intersect);
if (containsPoint(limit)) {
if (hl.contains(opposite)) {
return new FGESegment(limit, intersect);
} else {
return hl.clone();
}
} else {
if (hl.contains(opposite)) {
return new FGEHalfLine(intersect, opposite);
} else {
return new FGEEmptyArea();
}
}
}
} else if (aLine instanceof FGESegment) {
FGESegment segment = (FGESegment) aLine;
if (segment.overlap(line)) {
return segment.clone();
}
if (segment.isParallelTo(line)) {
if (containsPoint(segment.getP1())) {
return segment.clone();
} else {
return new FGEEmptyArea();
}
} else {
if (containsPoint(segment.getP1())) {
if (containsPoint(segment.getP2())) {
return segment.clone();
} else {
FGEPoint p1 = segment.getP1();
FGEPoint p2 = segment.getLineIntersection(line);
if (p1.equals(p2)) {
return p1.clone();
} else {
return new FGESegment(p1, p2);
}
}
} else {
if (containsPoint(segment.getP2())) {
FGEPoint p1 = segment.getP2();
FGEPoint p2 = segment.getLineIntersection(line);
if (p1.equals(p2)) {
return p1.clone();
} else {
return new FGESegment(p1, p2);
}
} else {
return new FGEEmptyArea();
}
}
}
} else {
logger.warning("Unexpected: " + line);
return null;
}
}
private FGEArea computeHalfPlaneIntersection(FGEHalfPlane hp) {
if (line.overlap(hp.line)) {
if (containsPoint(hp.testPoint)) {
return clone(); // Same half planes
} else {
return line.clone(); // Only line is common
}
} else if (line.isParallelTo(hp.line)) {
if (hp.containsLine(line)) {
if (containsLine(hp.line)) {
return new FGEBand(line, hp.line);
} else {
return clone();
}
} else {
if (containsLine(hp.line)) {
return hp.clone();
} else {
return new FGEEmptyArea();
}
}
} else {
// Don't try to formalize it
return FGEIntersectionArea.makeIntersection(this, hp);
}
}
private FGEArea computeShapeIntersection(FGEShape shape) {
FGEArea workingArea = intersect(shape.getBoundingBox());
if (workingArea instanceof FGEEmptyArea) {
return workingArea; // Empty area
}
if (workingArea instanceof FGEShape || workingArea instanceof FGEAbstractLine || workingArea instanceof FGEPoint) {
return workingArea.intersect(shape);
} else {
logger.warning("Unexpected: " + workingArea);
return new FGEEmptyArea();
}
}
@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 FGEBand) {
return area.intersect(this);
}
if (area instanceof FGEHalfBand) {
return area.intersect(this);
}
if (area instanceof FGEHalfPlane) {
return computeHalfPlaneIntersection((FGEHalfPlane) area);
}
if (area instanceof FGEShape) {
return computeShapeIntersection((FGEShape) 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 FGEHalfPlane clone() {
try {
return (FGEHalfPlane) super.clone();
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
@Override
public FGEHalfPlane transform(AffineTransform t) {
return new FGEHalfPlane(line.transform(t), testPoint.transform(t));
}
@Override
public void paint(FGEGraphics g) {
FGERectangle bounds = g.getGraphicalRepresentation().getNormalizedBounds();
g.useDefaultForegroundStyle();
bounds.intersect(line).paint(g);
g.useDefaultBackgroundStyle();
bounds.intersect(this).paint(g);
}
@Override
public String toString() {
return "FGEHalfPlane: " + line.toString() + " testPoint=" + testPoint;
}
@Override
public FGEPoint getNearestPoint(FGEPoint aPoint) {
if (containsPoint(aPoint)) {
return aPoint.clone();
} else {
return line.getProjection(aPoint);
}
}
@Override
public boolean equals(Object obj) {
if (obj instanceof FGEHalfPlane) {
FGEHalfPlane hp = (FGEHalfPlane) obj;
return line.overlap(hp.line) && containsPoint(hp.testPoint);
}
return super.equals(obj);
}
@Override
public FGEArea getOrthogonalPerspectiveArea(SimplifiedCardinalDirection orientation) {
return line.getOrthogonalPerspectiveArea(orientation);
}
@Override
public FGEHalfPlane 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);
}
}