package org.pepsoft.worldpainter.heightMaps; import org.pepsoft.util.MathUtils; import org.pepsoft.worldpainter.HeightMap; import java.util.Arrays; import java.util.HashMap; import java.util.Map; /** * Created by pepijn on 26-3-16. */ public abstract class DelegatingHeightMap extends AbstractHeightMap { protected DelegatingHeightMap(String... roles) { this.roles = roles; children = new HeightMap[roles.length]; for (int index = 0; index < roles.length; index++) { indices.put(roles[index], index); } } /** * Recursively recalculate the constant value, if any, of the tree of height * maps. */ public void recalculateConstantValue() { for (HeightMap child: children) { if (child instanceof DelegatingHeightMap) { ((DelegatingHeightMap) child).recalculateConstantValue(); } } determineConstant(); } public final int getHeightMapCount() { return children.length; } public final String getRole(int index) { return roles[index]; } public final int getIndex(String role) { return indices.get(role); } public final int getIndex(HeightMap heightMap) { for (int index = 0; index < children.length; index++) { if (children[index] == heightMap) { return index; } } throw new IllegalArgumentException(); } public final HeightMap getHeightMap(int index) { return children[index]; } public final HeightMap getHeightMap(String role) { return children[indices.get(role)]; } public final void setHeightMap(int index, HeightMap child) { if (children[index] != null) { throw new IllegalStateException(); } children[index] = child; if (child instanceof AbstractHeightMap) { ((AbstractHeightMap) child).parent = this; } childrenChanged(); } public final void setHeightMap(String role, HeightMap child) { setHeightMap(getIndex(role), child); } public final HeightMap replace(int index, HeightMap newChild) { HeightMap oldChild = children[index]; children[index] = newChild; if (oldChild instanceof AbstractHeightMap) { ((AbstractHeightMap) oldChild).parent = null; } if (newChild instanceof AbstractHeightMap) { ((AbstractHeightMap) newChild).parent = this; } childrenChanged(); return oldChild; } public final int replace(HeightMap oldChild, HeightMap newChild) { int index = getIndex(oldChild); children[index] = newChild; if (oldChild instanceof AbstractHeightMap) { ((AbstractHeightMap) oldChild).parent = null; } if (newChild instanceof AbstractHeightMap) { ((AbstractHeightMap) newChild).parent = this; } childrenChanged(); return index; } // HeightMap @Override public final long getSeed() { return children[0].getSeed(); } @Override public final void setSeed(long seed) { for (HeightMap child: children) { child.setSeed(seed); } } @Override public boolean isConstant() { return constant; } @Override public float getConstantValue() { return constantValue; } @Override public final float getHeight(float x, float y) { if (constant) { return constantValue; } else { return doGetHeight(x, y); } } @Override public final float getHeight(int x, int y) { if (constant) { return constantValue; } else { return doGetHeight(x, y); } } @Override public final int getColour(int x, int y) { if (constant) { return constantColour; } else { return doGetColour(x, y); } } protected void childrenChanged() { determineConstant(); } /** * Determines whether this height map can be constant by checking that all * its children are constant height maps. Invoked automatically whenever the * children of this height map change; should also be invoked by subclasses * whenever some property which affects the calculated value of the height * map changes. */ protected void determineConstant() { if (Arrays.stream(children).allMatch(m -> (m != null) && m.isConstant())) { constant = true; constantValue = doGetHeight(0, 0); constantColour = doGetColour(0, 0); } else { constant = false; } } protected int doGetColour(int x, int y) { int value = MathUtils.clamp(0, (int) (doGetHeight(x, y) + 0.5f), 255); return (value << 16) | (value << 8) | value; } protected float doGetHeight(float x, float y) { return doGetHeight((int) (x + 0.5f), (int) (y + 0.5f)); } protected float doGetHeight(int x, int y) { return doGetHeight((float) x, (float) y); } protected final HeightMap[] children; private final String[] roles; private final Map<String, Integer> indices = new HashMap<>(); protected float constantValue; protected boolean constant; protected int constantColour; private static final long serialVersionUID = 1L; }