/*
* (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.List;
import java.util.Vector;
import java.util.logging.Logger;
import org.openflexo.fge.geom.FGEAbstractLine;
import org.openflexo.fge.geom.FGEArc;
import org.openflexo.fge.geom.FGEGeometricObject;
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.FGEPolylin;
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 FGEUnionArea extends FGEOperationArea {
private static final Logger logger = Logger.getLogger(FGEUnionArea.class.getPackage().getName());
private Vector<FGEArea> _objects;
public static FGEArea makeUnion(FGEArea... objects) {
Vector<FGEArea> v = new Vector<FGEArea>();
for (FGEArea o : objects) {
v.add(o.clone());
}
return makeUnion(v);
}
public static FGEArea makeUnion(List<? extends FGEArea> objects) {
return makeUnion(objects, true);
}
private static FGEArea makeUnion(List<? extends FGEArea> objects, boolean tryToReduceUnionByConcatenation) {
List<? extends FGEArea> objectsToTakeUnderAccount = reduceUnionByEmbedding(objects);
if (tryToReduceUnionByConcatenation) {
List<? extends FGEArea> concatenedObjects = reduceUnionByConcatenation(objectsToTakeUnderAccount);
if (concatenedObjects.size() == 0) {
return new FGEEmptyArea();
} else if (concatenedObjects.size() == 1) {
return concatenedObjects.get(0).clone();
}
// System.out.println("Concatened objects: ");
// for (FGEArea o : concatenedObjects) System.out.println(" > "+o);
return new FGEUnionArea(concatenedObjects);
}
else {
if (objectsToTakeUnderAccount.size() == 1) {
return objectsToTakeUnderAccount.get(0);
}
return new FGEUnionArea(objectsToTakeUnderAccount);
}
}
private static List<? extends FGEArea> reduceUnionByEmbedding(List<? extends FGEArea> objects) {
Vector<FGEArea> objectsToTakeUnderAccount = new Vector<FGEArea>();
for (int i = 0; i < objects.size(); i++) {
FGEArea o = objects.get(i);
if (o instanceof FGEEmptyArea) {
// Ignore
} else {
boolean isAlreadyContained = false;
for (FGEArea a : objectsToTakeUnderAccount) {
if (a.containsArea(o)) {
isAlreadyContained = true;
}
}
if (!isAlreadyContained) {
Vector<FGEArea> noMoreNecessaryObjects = new Vector<FGEArea>();
for (FGEArea a2 : objectsToTakeUnderAccount) {
if (o.containsArea(a2)) {
noMoreNecessaryObjects.add(a2);
}
}
for (FGEArea removeThat : noMoreNecessaryObjects) {
objectsToTakeUnderAccount.remove(removeThat);
}
objectsToTakeUnderAccount.add(o);
}
}
}
return objectsToTakeUnderAccount;
}
private static List<? extends FGEArea> reduceUnionByConcatenation(List<? extends FGEArea> objectsToTakeUnderAccount) {
List<? extends FGEArea> listOfObjects = objectsToTakeUnderAccount;
boolean continueReducing = true;
while (continueReducing) {
continueReducing = false;
Vector<FGEArea> concatenedObjects = new Vector<FGEArea>();
concatenedObjects.addAll(listOfObjects);
for (int i = 0; i < listOfObjects.size(); i++) {
FGEArea a1 = listOfObjects.get(i);
for (int j = i + 1; j < listOfObjects.size(); j++) {
FGEArea a2 = listOfObjects.get(j);
FGEArea concatenation = a1.union(a2);
if (!(concatenation instanceof FGEUnionArea)) {
// Those 2 objects are concatenable, do it
// logger.info("Concatenate "+a1+" and "+a2+" to form "+concatenation);
concatenedObjects.add(concatenation);
concatenedObjects.remove(a1);
concatenedObjects.remove(a2);
continueReducing = true;
break;
}
}
if (continueReducing) {
break;
}
}
listOfObjects = reduceUnionByEmbedding(concatenedObjects);
}
return listOfObjects;
}
public static void main(String[] args) {
FGEPoint pt = new FGEPoint(0, 0);
FGELine line1 = new FGELine(new FGEPoint(0, 0), new FGEPoint(1, 0));
FGELine line2 = new FGELine(new FGEPoint(0, 0), new FGEPoint(1, 1));
FGERectangle rectangle = new FGERectangle(new FGEPoint(0, 0), new FGEPoint(1, 1), Filling.FILLED);
FGEHalfPlane hp = new FGEHalfPlane(line1, new FGEPoint(1, 1));
System.out.println("Union1: " + makeUnion(line1, line2));
System.out.println("Union2: " + makeUnion(rectangle, line1, pt));
System.out.println("Union3: " + makeUnion(pt, line1, rectangle, hp));
System.out.println("Union4: " + makeUnion(pt, line1, rectangle, hp, line2));
}
public FGEUnionArea() {
super();
_objects = new Vector<FGEArea>();
}
public FGEUnionArea(FGEArea... objects) {
this();
for (FGEArea o : objects) {
addArea(o.clone());
}
// logger.info(">>> Creating FGEUnionArea with "+objects);
if (objects.length == 1) {
logger.warning("Called constructor for FGEUnionArea with 1 object");
}
}
public FGEUnionArea(List<? extends FGEArea> objects) {
this();
for (FGEArea o : objects) {
addArea(o.clone());
}
// logger.info(">>> Creating FGEUnionArea with "+objects);
if (objects.size() == 1) {
logger.warning("Called constructor for FGEUnionArea with 1 object");
}
// if (objects.size() == 2 && objects.get(0) instanceof FGEArc && objects.get(1) instanceof FGEPoint ) {
// (new Exception("------------ Ca vient de la ce truc bizarre !!!")).printStackTrace();
// }
}
public Vector<FGEArea> getObjects() {
return _objects;
}
public void setObjects(Vector<FGEArea> objects) {
if (_objects != null) {
_objects.clear();
} else {
_objects = new Vector<FGEArea>();
}
for (FGEArea o : objects) {
addArea(o.clone());
}
}
public void addToObjects(FGEArea obj) {
addArea(obj.clone());
}
public void removeFromObjects(FGEArea obj) {
removeArea(obj);
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("FGEUnionArea: nObjects=" + _objects.size() + "\n");
for (int i = 0; i < _objects.size(); i++) {
sb.append(" " + (i + 1) + " > " + _objects.elementAt(i) + "\n");
}
return sb.toString();
}
@Override
public boolean containsPoint(FGEPoint p) {
for (FGEArea a : _objects) {
if (a.containsPoint(p)) {
return true;
}
}
return false;
}
@Override
public boolean containsLine(FGEAbstractLine l) {
// TODO: what if Union of two objects contains the line ? Implement this...
for (FGEArea a : _objects) {
if (a.containsLine(l)) {
return true;
}
}
return false;
}
public boolean containsPolylin(FGEPolylin p) {
for (FGESegment s : p.getSegments()) {
if (!containsLine(s)) {
return false;
}
}
return true;
}
public boolean containsPolygon(FGEPolygon p) {
for (FGESegment s : p.getSegments()) {
if (!containsLine(s)) {
return false;
}
}
return true;
}
@Override
public FGEArea transform(AffineTransform t) {
FGEArea[] all = new FGEArea[_objects.size()];
for (int i = 0; i < _objects.size(); i++) {
all[i] = _objects.get(i).transform(t);
}
return new FGEUnionArea(all);
}
@Override
public void paint(FGEGraphics g) {
for (FGEArea a : _objects) {
a.paint(g);
}
}
public void addArea(FGEArea area) {
if (area instanceof FGEEmptyArea) {
return;
}
if (containsArea(area)) {
return;
}
if (area instanceof FGEUnionArea) {
for (FGEArea a : ((FGEUnionArea) area).getObjects()) {
addArea(a);
}
return;
}
Vector<FGEArea> uselessObjects = new Vector<FGEArea>();
for (FGEArea a : _objects) {
if (area.containsArea(a)) {
uselessObjects.add(a);
}
}
for (FGEArea a : uselessObjects) {
_objects.remove(a);
}
_objects.add(area);
}
public void removeArea(FGEArea area) {
if (!containsArea(area)) {
return;
}
_objects.remove(area);
}
@Override
public boolean containsArea(FGEArea a) {
for (FGEArea o : _objects) {
if (o.containsArea(a)) {
return true;
}
}
if (a instanceof FGEPoint) {
return containsPoint((FGEPoint) a);
}
if (a instanceof FGEAbstractLine) {
return containsLine((FGEAbstractLine) a);
}
if (a instanceof FGEPolylin) {
return containsPolylin((FGEPolylin) a);
}
if (a instanceof FGEPolygon) {
return containsPolygon((FGEPolygon) a);
}
if (a instanceof FGEShape) {
return FGEShape.AreaComputation.isShapeContainedInArea((FGEShape) a, this);
}
return false;
}
public <T> boolean isUnionOf(Class<T> aClass) {
if (_objects.size() == 0) {
return false;
}
for (FGEArea a : _objects) {
if (!aClass.isAssignableFrom(a.getClass())) {
return false;
}
}
return true;
}
public boolean isUnionOfFiniteGeometricObjects() {
return isUnionOf(FGEGeometricObject.class) && isFinite();
}
public boolean isUnionOfPoints() {
return isUnionOf(FGEPoint.class);
}
public boolean isUnionOfSegments() {
return isUnionOf(FGESegment.class);
}
public boolean isUnionOfArcs() {
return isUnionOf(FGEArc.class);
}
@Override
public FGEPoint getNearestPoint(FGEPoint aPoint) {
if (containsPoint(aPoint)) {
return aPoint.clone();
}
Vector<FGEPoint> pts = new Vector<FGEPoint>();
for (FGEArea o : getObjects()) {
pts.add(o.getNearestPoint(aPoint));
}
return FGEPoint.getNearestPoint(aPoint, pts);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof FGEUnionArea) {
FGEUnionArea u = (FGEUnionArea) obj;
if (getObjects().size() != u.getObjects().size()) {
return false;
}
for (int i = 0; i < getObjects().size(); i++) {
FGEArea a = getObjects().get(i);
// Equals even if not same order
if (u.getObjects().indexOf(a) == -1) {
return false;
}
}
return true;
}
return super.equals(obj);
}
@Override
public FGEArea intersect(FGEArea area) {
if (area.containsArea(this)) {
return this.clone();
}
if (containsArea(area)) {
return area.clone();
}
Vector<FGEArea> intersections = new Vector<FGEArea>();
for (FGEArea o : _objects) {
intersections.add(o.intersect(area));
}
return makeUnion(intersections);
}
@Override
public FGEArea union(FGEArea area) {
if (containsArea(area)) {
return clone();
}
if (area.containsArea(this)) {
return area.clone();
}
Vector<FGEArea> objects = new Vector<FGEArea>();
objects.addAll(getObjects());
objects.add(area);
return makeUnion(objects, false);
}
@Override
public FGEArea getOrthogonalPerspectiveArea(SimplifiedCardinalDirection orientation) {
/*Vector<FGEArea> newObjects = new Vector<FGEArea>();
for (FGEArea a : getObjects()) {
newObjects.add(a.getOrthogonalPerspectiveArea(orientation));
}
return new FGEUnionArea(newObjects);*/
FGEArea anchorArea = getAnchorAreaFrom(orientation);
if (anchorArea instanceof FGEUnionArea) {
FGEUnionArea unionAnchorArea = (FGEUnionArea) anchorArea;
Vector<FGEArea> newObjects = new Vector<FGEArea>();
for (FGEArea a : unionAnchorArea.getObjects()) {
newObjects.add(a.getOrthogonalPerspectiveArea(orientation));
}
return FGEUnionArea.makeUnion(newObjects);
} else {
return anchorArea.getOrthogonalPerspectiveArea(orientation);
}
}
@Override
public FGEArea getAnchorAreaFrom(SimplifiedCardinalDirection direction) {
if (isUnionOfSegments()) {
Vector<FGESegment> segments = new Vector<FGESegment>();
for (FGEArea s : getObjects()) {
segments.add((FGESegment) s);
}
return FGEPolylin.computeVisibleSegmentsFrom(direction, segments);
}
Vector<FGEArea> newObjects = new Vector<FGEArea>();
for (FGEArea a : getObjects()) {
newObjects.add(a.getAnchorAreaFrom(direction));
}
return FGEUnionArea.makeUnion(newObjects);
}
/**
* Return a flag indicating if this area is finite or not An union area is finite if and only if all areas are finite
*
* @return
*/
@Override
public final boolean isFinite() {
for (FGEArea a : _objects) {
if (!a.isFinite()) {
return false;
}
}
return true;
}
/**
* If this area is finite, return embedding bounds as a FGERectangle (this is not guaranteed to be optimal in some cases). For
* non-finite areas (if this area contains a least one non-finite area), return null
*
* @return
*/
@Override
public final FGERectangle getEmbeddingBounds() {
FGERectangle returned = null;
for (FGEArea a : _objects) {
if (!a.isFinite()) {
return null;
}
FGERectangle r = a.getEmbeddingBounds();
if (r != null) {
if (returned == null) {
returned = r;
} else {
returned = returned.rectangleUnion(r);
}
}
}
return returned;
}
/**
* 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) {
if (containsPoint(from)) {
return from.clone();
}
Vector<FGEPoint> pts = new Vector<FGEPoint>();
for (FGEArea o : getObjects()) {
pts.add(o.nearestPointFrom(from, orientation));
}
return FGEPoint.getNearestPoint(from, pts);
}
/*public void simplify()
{
Vector<FGEArea> newObjects = new Vector<FGEArea>();
for (FGEArea o : getObjects()) {
if (o instanceof FGEUnionArea) {
FGEUnionArea union = (FGEUnionArea)
}
}
}*/
}