/*
* 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.vbl;
import java.awt.geom.Area;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import com.t3.util.GraphicsUtil;
public class AreaTree {
private static final Logger log = Logger.getLogger(AreaTree.class);
private AreaOcean theOcean;
public AreaTree(Area area) {
digest(area);
}
public AreaOcean getOceanAt(Point2D point) {
return theOcean.getDeepestOceanAt(point);
}
// Package level for testing purposes
AreaOcean getOcean() {
return theOcean;
}
private void digest(Area area) {
if (area == null) {
return;
}
List<AreaOcean> oceanList = new ArrayList<AreaOcean>();
List<AreaIsland> islandList = new ArrayList<AreaIsland>();
// 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();
// Holes are oceans, solids are islands
if (areaMeta.isHole()) {
oceanList.add(new AreaOcean(areaMeta));
} else {
islandList.add(new AreaIsland(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;
}
}
// Create the hierarchy
// Start by putting each ocean into the containing island
// Every ocean should have a containing island. There is only one ocean that doesn't
// have an explicit island and that's the global scope ocean container
for (AreaOcean ocean : oceanList) {
AreaIsland island = findSmallestContainer(ocean, islandList);
if (island == null) {
log.warn("Weird, I couldn't find an island for an ocean. Bad/overlapping VBL?");
continue;
}
island.addOcean(ocean);
}
// Now put each island into the containing ocean
List<AreaIsland> globalIslandList = new ArrayList<AreaIsland>();
for (AreaIsland island : islandList) {
AreaOcean ocean = findSmallestContainer(island, oceanList);
if (ocean == null) {
globalIslandList.add(island);
continue;
}
ocean.addIsland(island);
}
// Now we have our hierarchy, just hook up the global space
theOcean = new AreaOcean(null);
for (AreaIsland island : globalIslandList) {
theOcean.addIsland(island);
}
}
private <T extends AreaContainer> T findSmallestContainer(AreaContainer item, List<T> list) {
T smallest = null;
for (T container : list) {
if (!GraphicsUtil.contains(container.getBounds(), item.getBounds())) {
continue;
}
smallest = getSmallest(smallest, container);
}
return smallest;
}
private <T extends AreaContainer> T getSmallest(T left, T right) {
// Something is smaller than nothing, for our purposes
if (left == null) {
return right;
}
if (right == null) {
return left;
}
// Presumably the container with the smaller area will be the contained area
double leftSize = left.getBounds().getBounds().getWidth() * left.getBounds().getBounds().getHeight();
double rightSize = right.getBounds().getBounds().getWidth() * right.getBounds().getBounds().getHeight();
return leftSize < rightSize ? left : right;
}
}