/*
* Copyright (c) 2014 tabletoptool.com team.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* rptools.com team - initial implementation
* tabletoptool.com team - further development
*/
package com.t3.client.ui.zone;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Area;
import java.awt.geom.PathIterator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import com.t3.util.GraphicsUtil;
public class AreaData {
private static final int POINT_COUNT_THRESHOLD = 100;
private Area area;
private List<AreaMeta> metaList;
private final List<Area> holeList = new ArrayList<Area>();
public AreaData(Area area) {
// Keep our own copy
this.area = new Area(area);
}
public boolean contains(int x, int y) {
for (AreaMeta meta : metaList) {
if (meta.area.contains(x, y)) {
return true;
}
}
return false;
}
public List<AreaMeta> getAreaList() {
return new ArrayList<AreaMeta>(metaList);
}
public List<AreaMeta> getAreaList(final Point centerPoint) {
List<AreaMeta> areaMetaList = new ArrayList<AreaMeta>(metaList);
Collections.sort(areaMetaList, new Comparator<AreaMeta>() {
@Override
public int compare(AreaMeta o1, AreaMeta o2) {
Double d1 = centerPoint.distance(o1.getCenterPoint());
Double d2 = centerPoint.distance(o2.getCenterPoint());
return d1.compareTo(d2);
}
});
return areaMetaList;
}
public Area getHoleAt(int x, int y) {
for (Area area : holeList) {
if (area.contains(x, y)) {
return area;
}
}
return null;
}
public void digest() {
if (metaList != null) {
// Already digested
return;
}
metaList = new ArrayList<AreaMeta>();
List<Area> areaQueue = new LinkedList<Area>();
List<Point> splitPoints = new LinkedList<Point>();
areaQueue.add(area);
while (areaQueue.size() > 0) {
Area area = areaQueue.remove(0);
// Break the big area into independent areas
float[] coords = new float[6];
AreaMeta areaMeta = new AreaMeta();
for (PathIterator iter = area.getPathIterator(null); !iter.isDone(); iter.next()) {
int type = iter.currentSegment(coords);
switch (type) {
case PathIterator.SEG_CLOSE: {
areaMeta.close();
splitPoints.clear();
for (ListIterator<AreaMeta> metaIter = metaList.listIterator(); metaIter.hasNext();) {
AreaMeta meta = metaIter.next();
// Look for holes
if (GraphicsUtil.intersects(areaMeta.area, meta.area) && meta.isHole()) {
// This is a hole. Holes are always created before their parent, so pull out the existing
// area and remove it from the new area
metaIter.remove();
areaMeta.area.subtract(meta.area);
// Split this hole to rid ourselves of holes
// Because of the way areas are bounded, if we split
// through the center point it will cut the hole into at least 2 pieces
Rectangle bounds = meta.area.getBounds();
splitPoints.add(new Point(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2));
// Keep track of the hole for future reference
holeList.add(meta.area);
}
}
if (splitPoints.size() > 0) {
// Split on one hole (pick it arbitrarily), and resolve the new resulting areas.
// If there are still holes remaining they will be caught here
Point point = splitPoints.iterator().next();
Rectangle bounds = areaMeta.area.getBounds();
Area part1 = new Area(areaMeta.area);
part1.intersect(new Area(new Rectangle(bounds.x, bounds.y, point.x - bounds.x, bounds.height)));
areaQueue.add(part1);
Area part2 = new Area(areaMeta.area);
part2.intersect(new Area(new Rectangle(point.x, bounds.y, (bounds.x + bounds.width) - point.x, bounds.height)));
areaQueue.add(part2);
} else {
metaList.add(areaMeta);
}
break;
}
case PathIterator.SEG_LINETO: {
areaMeta.addPoint(coords[0], coords[1]);
break;
}
case PathIterator.SEG_MOVETO: {
areaMeta = new AreaMeta();
areaMeta.addPoint(coords[0], coords[1]);
break;
}
// NOT SUPPORTED
// case PathIterator.SEG_CUBICTO: coordCount = 3; break;
// case PathIterator.SEG_QUADTO: coordCount = 2;break;
}
}
}
// Optimization, if any area is larger than the threshold, split it and go through the resolution
// cycle again
// if (GeometryUtil.countAreaPoints(areaMeta.area) > POINT_COUNT_THRESHOLD) {
//
// Rectangle bounds = areaMeta.area.getBounds();
//
// int w = bounds.width > bounds.height ? bounds.width/2 : bounds.width;
// int h = bounds.width > bounds.height ? bounds.height : bounds.height/2;
//
// Area part1 = new Area(areaMeta.area);
// part1.intersect(new Area(new Rectangle(bounds.x, bounds.y, w, h)));
// areaQueue.add(part1);
//
// Area part2 = new Area(areaMeta.area);
// part2.intersect(new Area(new Rectangle((bounds.x+bounds.width)-w, (bounds.y+bounds.height)-h, w, h)));
// areaQueue.add(part2);
// }
// No longer needed
// System.out.println("Size: " + metaList.size());
area = null;
}
}