/*
* Copyright 2014 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.polyworld.water;
import java.util.Collection;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Map;
import org.terasology.math.geom.Rect2i;
import org.terasology.math.geom.BaseVector2f;
import org.terasology.math.geom.Vector2f;
import org.terasology.polyworld.distribution.Distribution;
import org.terasology.polyworld.graph.Corner;
import org.terasology.polyworld.graph.Graph;
import org.terasology.polyworld.graph.Region;
import com.google.common.collect.Maps;
/**
* Uses a {@link Distribution} to define how water is distributed in the graph.
* The result is normalized.
*/
public class DefaultWaterModel implements WaterModel {
private final Map<Corner, Boolean> cornerWater = Maps.newHashMap();
private final Map<Corner, Boolean> cornerOcean = Maps.newHashMap();
private final Map<Corner, Boolean> cornerCoast = Maps.newHashMap();
private final Map<Region, Boolean> regionWater = Maps.newHashMap();
private final Map<Region, Boolean> regionOcean = Maps.newHashMap();
private final Map<Region, Boolean> regionCoast = Maps.newHashMap();
/**
* @param graph the graph to use
* @param dist the distribution of water
*/
public DefaultWaterModel(Graph graph, Distribution dist) {
final float waterThreshold = .3f;
for (Corner c : graph.getCorners()) {
Rect2i bounds = graph.getBounds();
BaseVector2f p2 = c.getLocation();
float nx = (p2.getX() - bounds.minX()) / bounds.width();
float ny = (p2.getY() - bounds.minY()) / bounds.height();
setWater(c, dist.isInside(new Vector2f(nx, ny)));
}
Deque<Region> queue = new LinkedList<>();
for (final Region region : graph.getRegions()) {
int numWater = 0;
Collection<Corner> corners = region.getCorners();
for (final Corner c : corners) {
if (c.isBorder()) {
setWater(region, true);
setOcean(region, true);
queue.add(region);
}
if (isWater(c)) {
numWater++;
}
}
setWater(region, isOcean(region) || ((float) numWater / corners.size() >= waterThreshold));
}
while (!queue.isEmpty()) {
final Region region = queue.pop();
for (final Region n : region.getNeighbors()) {
if (isWater(n) && !isOcean(n)) {
setOcean(n, true);
queue.add(n);
}
}
}
for (Region region : graph.getRegions()) {
boolean oceanNeighbor = false;
boolean landNeighbor = false;
for (Region n : region.getNeighbors()) {
oceanNeighbor |= isOcean(n);
landNeighbor |= !isWater(n);
}
setCoast(region, oceanNeighbor && landNeighbor);
}
for (Corner c : graph.getCorners()) {
int numOcean = 0;
int numLand = 0;
for (Region region : c.getTouches()) {
numOcean += isOcean(region) ? 1 : 0;
numLand += !isWater(region) ? 1 : 0;
}
setOcean(c, numOcean == c.getTouches().size());
setCoast(c, numOcean > 0 && numLand > 0);
setWater(c, c.isBorder() || ((numLand != c.getTouches().size()) && !isCoast(c)));
}
}
private void setCoast(Corner c, boolean coast) {
cornerCoast.put(c, Boolean.valueOf(coast));
}
private void setOcean(Corner c, boolean ocean) {
cornerOcean.put(c, Boolean.valueOf(ocean));
}
private void setWater(Corner c, boolean water) {
cornerWater.put(c, Boolean.valueOf(water));
}
private void setCoast(Region c, boolean coast) {
regionCoast.put(c, Boolean.valueOf(coast));
}
private void setOcean(Region c, boolean ocean) {
regionOcean.put(c, Boolean.valueOf(ocean));
}
private void setWater(Region c, boolean water) {
regionWater.put(c, Boolean.valueOf(water));
}
@Override
public boolean isWater(Corner c) {
return cornerWater.get(c);
}
@Override
public boolean isCoast(Corner c) {
return cornerCoast.get(c);
}
@Override
public boolean isOcean(Corner c) {
return cornerOcean.get(c);
}
@Override
public boolean isWater(Region c) {
Boolean val = regionWater.get(c);
return val == null ? false : val.booleanValue();
}
@Override
public boolean isCoast(Region c) {
Boolean val = regionCoast.get(c);
return val == null ? false : val.booleanValue();
}
@Override
public boolean isOcean(Region c) {
Boolean val = regionOcean.get(c);
return val == null ? false : val.booleanValue();
}
}