/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.pepsoft.worldpainter.heightMaps;
import org.pepsoft.util.IconUtils;
import org.pepsoft.util.MathUtils;
import javax.swing.*;
/**
* A heightmap creating a square with rounded corners, with a smoothly descending border on the outside.
*
* <p>The <code>innerSize</code> is the inner size of the square (from the centre, so half the actual width) without the rounded corners.<br>
* The <code>borderSize</code> is the radius of the rounded corners.<br>
* The <code>coastSize</code> is the distance over which the height decreases to zero outside the border.<br>
* The <code>height</code> is the height of the part described by <code>innerSize + borderSize</code>. From there the height sinuously decreases to zero over <code>coastSize</code> pixels.
*
* <p><img src="doc-files/ninepatch.png"/>
*
* <p>By setting the <code>innerSize</code> to zero you can create a completely circular shape. The two constructors that take only two <code>int</code>s are convenience constructors to do that.
*
* <p>To have entirely sharp, perpendicular corners, set the <code>borderSize</code> to zero.
*
* @author pepijn
*/
public final class NinePatchHeightMap extends AbstractHeightMap {
public NinePatchHeightMap(int innerRadius, int coastSize, float height) {
this(null, 0, innerRadius, coastSize, height);
}
public NinePatchHeightMap(int innerSize, int borderSize, int coastSize, float height) {
this(null, innerSize, borderSize, coastSize, height);
}
public NinePatchHeightMap(String name, int innerRadius, int coastSize, float height) {
this(name, 0, innerRadius, coastSize, height);
}
public NinePatchHeightMap(String name, int innerSize, int borderSize, int coastSize, float height) {
super(name);
if ((innerSize < 0) || (borderSize < 0) || (coastSize < 0) || (height <= 0.0f)) {
throw new IllegalArgumentException();
}
if ((innerSize == 0) && (borderSize == 0) && (coastSize == 0)) {
throw new IllegalArgumentException();
}
this.innerSizeX = innerSize;
this.innerSizeY = innerSize;
this.borderSize = borderSize;
this.coastSize = coastSize;
this.height = height;
sizesChanged();
}
public int getInnerSizeX() {
return innerSizeX;
}
public int getInnerSizeY() {
return innerSizeY;
}
public int getBorderSize() {
return borderSize;
}
public int getCoastSize() {
return coastSize;
}
public float getHeight() {
return height;
}
public void setBorderSize(int borderSize) {
this.borderSize = borderSize;
sizesChanged();
}
public void setCoastSize(int coastSize) {
this.coastSize = coastSize;
sizesChanged();
}
public void setHeight(float height) {
this.height = height;
sizesChanged();
}
public void setInnerSizeX(int innerSizeX) {
this.innerSizeX = innerSizeX;
sizesChanged();
}
public void setInnerSizeY(int innerSizeY) {
this.innerSizeY = innerSizeY;
sizesChanged();
}
public void setInnerSize(int innerSize) {
this.innerSizeX = innerSize;
this.innerSizeY = innerSize;
sizesChanged();
}
// HeightMap
@Override
public float getHeight(float x, float y) {
x = Math.abs(x);
y = Math.abs(y);
if (x < innerSizeX) {
if (y < innerSizeY) {
// On the continent
return height;
} else if (y < borderTotalY) {
// Border
return height;
} else if (y < coastTotalY) {
// Coast
return (float) (Math.cos((y - borderTotalY) / coastSize * Math.PI) * halfHeight) + halfHeight;
} else {
// Outside the continent
return 0;
}
} else if (x < borderTotalX) {
if (y < innerSizeY) {
// Border
return height;
} else if (y < coastTotalY) {
// Corner
float distanceFromCorner = MathUtils.getDistance(x - innerSizeX, y - innerSizeY);
if (distanceFromCorner < borderSize) {
// Border
return height;
} else if (distanceFromCorner - borderSize < coastSize) {
return (float) (Math.cos((distanceFromCorner - borderSize) / coastSize * Math.PI) * halfHeight) + halfHeight;
// Coast
} else {
// Outside the continent
return 0;
}
} else {
// Outside the continent
return 0;
}
} else if (x < coastTotalX) {
if (y < innerSizeY) {
// Coast
return (float) (Math.cos((x - borderTotalX) / coastSize * Math.PI) * halfHeight) + halfHeight;
} else if (y < coastTotalY) {
// Corner
float distanceFromCorner = MathUtils.getDistance(x - innerSizeX, y - innerSizeY);
if (distanceFromCorner < borderSize) {
// Border
return height;
} else if (distanceFromCorner - borderSize < coastSize) {
return (float) (Math.cos((distanceFromCorner - borderSize) / coastSize * Math.PI) * halfHeight) + halfHeight;
// Coast
} else {
// Outside the continent
return 0;
}
} else {
// Outside the continent
return 0;
}
} else {
// Outside the continent
return 0;
}
}
@Override
public Icon getIcon() {
return ICON_NINE_PATCH_HEIGHTMAP;
}
@Override
public float[] getRange() {
return new float[] {0.0f, height};
}
private void sizesChanged() {
halfHeight = height / 2;
borderTotalX = innerSizeX + borderSize;
borderTotalY = innerSizeY + borderSize;
coastTotalX = borderTotalX + coastSize;
coastTotalY = borderTotalY + coastSize;
}
private int innerSizeX, innerSizeY, borderSize, coastSize;
private int borderTotalX, borderTotalY, coastTotalX, coastTotalY;
private float height, halfHeight;
private static final long serialVersionUID = 1L;
private static final Icon ICON_NINE_PATCH_HEIGHTMAP = IconUtils.loadScaledIcon("org/pepsoft/worldpainter/icons/nine_patch.png");
}