/*
* Copyright (C) 2014 pepijn
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.pepsoft.worldpainter.heightMaps;
import org.pepsoft.util.IconUtils;
import org.pepsoft.worldpainter.HeightMap;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
/**
* A height map which scales and/or translates another height map
*
* @author pepijn
*/
public class TransformingHeightMap extends DelegatingHeightMap {
public TransformingHeightMap(String name, HeightMap baseHeightMap, int scaleX, int scaleY, int offsetX, int offsetY, int rotation) {
super("baseHeightMap");
setName(name);
setHeightMap(0, baseHeightMap);
this.scaleX = scaleX / 100.0f;
this.scaleY = scaleY / 100.0f;
this.offsetX = offsetX;
this.offsetY = offsetY;
this.rotation = rotation;
recalculate();
}
public HeightMap getBaseHeightMap() {
return children[0];
}
public void setBaseHeightMap(HeightMap baseHeightMap) {
replace(0, baseHeightMap);
}
public int getOffsetX() {
return offsetX;
}
public void setOffsetX(int offsetX) {
this.offsetX = offsetX;
recalculate();
}
public int getOffsetY() {
return offsetY;
}
public void setOffsetY(int offsetY) {
this.offsetY = offsetY;
recalculate();
}
public int getScaleX() {
return (int) (scaleX * 100 + 0.5f);
}
public void setScaleX(int scaleX) {
this.scaleX = scaleX / 100.0f;
recalculate();
}
public int getScaleY() {
return (int) (scaleY * 100 + 0.5f);
}
public void setScaleY(int scaleY) {
this.scaleY = scaleY / 100.0f;
recalculate();
}
public int getRotation() {
return rotation;
}
public void setRotation(int rotation) {
this.rotation = rotation;
recalculate();
}
@Override
public float doGetHeight(float x, float y) {
Point2D.Float destPoint = (Point2D.Float) transform.transform(new Point2D.Float(x, y), null);
return children[0].getHeight(destPoint.x, destPoint.y);
}
@Override
public int doGetColour(int x, int y) {
Point2D.Float destPoint = (Point2D.Float) transform.transform(new Point2D.Float(x, y), null);
return children[0].getColour((int) (destPoint.x + 0.5f), (int) (destPoint.y + 0.5f));
}
@Override
public Rectangle getExtent() {
Rectangle extent = children[0].getExtent();
if (extent != null) {
Point2D p1 = new Point2D.Double(extent.getMinX(), extent.getMinY());
Point2D p2 = new Point2D.Double(extent.getMinX(), extent.getMaxY());
Point2D p3 = new Point2D.Double(extent.getMaxX(), extent.getMinY());
Point2D p4 = new Point2D.Double(extent.getMaxX(), extent.getMaxY());
try {
transform.inverseTransform(p1, p1);
transform.inverseTransform(p2, p2);
transform.inverseTransform(p3, p3);
transform.inverseTransform(p4, p4);
} catch (NoninvertibleTransformException e) {
throw new RuntimeException(e);
}
double minX = Math.min(Math.min(p1.getX(), p2.getX()), Math.min(p3.getX(), p4.getX()));
double maxX = Math.max(Math.max(p1.getX(), p2.getX()), Math.max(p3.getX(), p4.getX()));
double minY = Math.min(Math.min(p1.getY(), p2.getY()), Math.min(p3.getY(), p4.getY()));
double maxY = Math.max(Math.max(p1.getY(), p2.getY()), Math.max(p3.getY(), p4.getY()));
return new Rectangle((int) minX, (int) minY, (int) Math.ceil(maxX - minX), (int) Math.ceil(maxY - minY));
} else {
return null;
}
}
@Override
public Icon getIcon() {
return ICON_TRANSFORMING_HEIGHTMAP;
}
@Override
public float[] getRange() {
return children[0].getRange();
}
private void recalculate() {
transform = new AffineTransform();
if ((scaleX != 1.0f) || (scaleY != 1.0f)) {
transform.scale(1 / scaleX, 1 / scaleY);
}
if ((offsetX != 0) || (offsetY != 0)) {
transform.translate(-offsetX, -offsetY);
}
if (rotation != 0) {
transform.rotate(-rotation / DOUBLE_PI);
}
}
public static TransformingHeightMapBuilder build() {
return new TransformingHeightMapBuilder();
}
private int offsetX, offsetY, rotation;
private float scaleX, scaleY;
private AffineTransform transform;
private static final long serialVersionUID = 1L;
private static final double DOUBLE_PI = Math.PI * 2;
private static final Icon ICON_TRANSFORMING_HEIGHTMAP = IconUtils.loadScaledIcon("org/pepsoft/worldpainter/icons/transform.png");
public static class TransformingHeightMapBuilder {
public TransformingHeightMapBuilder withName(String name) {
this.name = name;
return this;
}
public TransformingHeightMapBuilder withHeightMap(HeightMap baseHeightMap) {
this.baseHeightMap = baseHeightMap;
return this;
}
public TransformingHeightMapBuilder withOffset(int offsetX, int offsetY) {
this.offsetX = offsetX;
this.offsetY = offsetY;
return this;
}
public TransformingHeightMapBuilder withScale(int scale) {
scaleX = scale;
scaleY = scale;
return this;
}
public TransformingHeightMapBuilder withScale(int scaleX, int scaleY) {
this.scaleX = scaleX;
this.scaleY = scaleY;
return this;
}
public TransformingHeightMapBuilder withRotation(int rotation) {
this.rotation = rotation;
return this;
}
public TransformingHeightMap now() {
return new TransformingHeightMap(name, baseHeightMap, scaleX, scaleY, offsetX, offsetY, rotation);
}
private String name;
private HeightMap baseHeightMap;
private int offsetX, offsetY;
private int scaleX = 100, scaleY = 100;
private int rotation;
}
}