package org.pepsoft.worldpainter.dynmap;
import org.pepsoft.util.Box;
import org.pepsoft.util.IconUtils;
import org.pepsoft.util.MathUtils;
import org.pepsoft.util.swing.TiledImageViewer;
import org.pepsoft.worldpainter.Configuration;
import org.pepsoft.worldpainter.layers.bo2.Bo2Object;
import org.pepsoft.worldpainter.layers.bo2.Bo3Object;
import org.pepsoft.worldpainter.layers.bo2.Schematic;
import org.pepsoft.worldpainter.objects.WPObject;
import org.slf4j.bridge.SLF4JBridgeHandler;
import javax.swing.*;
import javax.vecmath.Point3i;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
/**
* A component which can show isometric 3D views of arbitrary {@link WPObject}s
* using dynmap to render the view. The view can be zoomed and rotated using
* mouse and keyboard.
*
* <p>Created by Pepijn Schmitz on 05-06-15.
*/
public class DynMapPreviewer extends TiledImageViewer {
public DynMapPreviewer() {
this(135.0, 60.0, 0);
}
public DynMapPreviewer(double myAzimuth, double myInclination, int myZoom) {
super(true, Math.max(Runtime.getRuntime().availableProcessors() - 1, 1), false);
initialAzimuth = myAzimuth;
initialInclination = myInclination;
initialZoom = myZoom;
azimuth = myAzimuth;
inclination = myInclination;
setZoom(myZoom);
setFocusable(true);
addMouseWheelListener(e -> {
if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
int newZoom = getZoom() - e.getWheelRotation();
setZoom(Math.max(newZoom, -4));
}
});
InputMap inputMap = getInputMap(JComponent.WHEN_FOCUSED);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "rotateLeft");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "rotateRight");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "rotateUp");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "rotateDown");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), "rotateLeft");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0), "rotateRight");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), "rotateUp");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), "rotateDown");
inputMap.put(KeyStroke.getKeyStroke('-'), "zoomOut");
inputMap.put(KeyStroke.getKeyStroke('+'), "zoomIn");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0), "reset");
ActionMap actionMap = getActionMap();
actionMap.put("rotateLeft", rotateLeft);
actionMap.put("rotateRight", rotateRight);
actionMap.put("rotateUp", rotateUp);
actionMap.put("rotateDown", rotateDown);
actionMap.put("zoomIn", zoomIn);
actionMap.put("zoomOut", zoomOut);
actionMap.put("reset", reset);
setActionStates();
JPanel buttonPanel = new JPanel(new GridLayout(2, 3, 2, 2));
buttonPanel.setOpaque(false);
buttonPanel.add(createActionButton(rotateLeft));
buttonPanel.add(createActionButton(rotateUp));
buttonPanel.add(createActionButton(rotateRight));
buttonPanel.add(createActionButton(zoomIn));
buttonPanel.add(createActionButton(rotateDown));
buttonPanel.add(createActionButton(zoomOut));
buttonPanel.setSize(buttonPanel.getPreferredSize());
buttonPanel.setLocation(4, 4);
add(buttonPanel);
}
public WPObject getObject() {
return object;
}
public void setObject(WPObject object) {
this.object = object;
setActionStates();
if (object != null) {
WPObjectDynmapWorld dmWorld = new WPObjectDynmapWorld(object);
tileProvider = new DynMapTileProvider(dmWorld);
tileProvider.setAzimuth(azimuth);
tileProvider.setInclination(inclination);
tileProvider.setCaves(caves);
if (getTileProviderCount() == 0) {
// First time
addTileProvider(tileProvider);
} else {
replaceTileProvider(0, tileProvider);
}
} else {
tileProvider = null;
if (getTileProviderCount() > 0) {
removeAllTileProviders();
}
}
}
public double getAzimuth() {
return azimuth;
}
public void setAzimuth(double azimuth) {
this.azimuth = azimuth;
setActionStates();
if (tileProvider != null) {
tileProvider.setAzimuth(azimuth);
}
}
public double getInclination() {
return inclination;
}
public void setInclination(double inclination) {
this.inclination = inclination;
setActionStates();
if (tileProvider != null) {
tileProvider.setInclination(inclination);
}
}
public boolean isCaves() {
return caves;
}
public void setCaves(boolean caves) {
this.caves = caves;
if (tileProvider != null) {
tileProvider.setCaves(caves);
}
}
public BufferedImage createImage() {
Point3i offset = object.getOffset();
Point3i dimensions = object.getDimensions();
Rectangle tileCoords = tileProvider.getBounds(new Box(offset.x, offset.x + dimensions.x - 1, offset.y, offset.y + dimensions.y - 1, offset.z, offset.z + dimensions.z - 1));
BufferedImage image = new BufferedImage(tileCoords.width * 128, tileCoords.height * 128, BufferedImage.TYPE_INT_ARGB);
for (int dx = 0; dx < tileCoords.width; dx++) {
for (int dy = 0; dy < tileCoords.height; dy++) {
tileProvider.paintTile(image, tileCoords.x + dx, tileCoords.y + dy, dx * 128, dy * 128);
}
}
return image;
}
private JButton createActionButton(Action action) {
JButton button = new JButton(action);
button.setHideActionText(true);
button.setMargin(new Insets(0, 0, 0, 0));
return button;
}
private void setActionStates() {
zoomIn.setEnabled(object != null);
zoomOut.setEnabled((object != null) && (getZoom() > -4));
rotateLeft.setEnabled(object != null);
rotateRight.setEnabled(object != null);
rotateUp.setEnabled((object != null) && (inclination > 30.0));
rotateDown.setEnabled((object != null) && (inclination < 90.0));
}
public static void main(String[] args) throws IOException {
// Install java.util.logging -> slf4j bridge:
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
// Install default config
Configuration.setInstance(new Configuration());
JFrame frame = new JFrame("DynMapPreviewerTest");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
DynMapPreviewer viewer = new DynMapPreviewer();
WPObject object;
int p = args[0].lastIndexOf('.');
switch (args[0].substring(p + 1)) {
case "schematic":
object = Schematic.load(new File(args[0]));
break;
case "bo2":
object = Bo2Object.load(new File(args[0]));
break;
case "bo3":
object = Bo3Object.load(new File(args[0]));
break;
default:
throw new IllegalArgumentException(args[0]);
}
viewer.setObject(object);
frame.getContentPane().add(viewer, BorderLayout.CENTER);
frame.setSize(800, 600);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private final Action zoomIn = new AbstractAction("Zoom In", IconUtils.loadScaledIcon("org/pepsoft/worldpainter/icons/magnifier_zoom_in.png")) {
public void actionPerformed(ActionEvent e) {
setZoom(getZoom() + 1);
setActionStates();
}
};
private final Action zoomOut = new AbstractAction("Zoom In", IconUtils.loadScaledIcon("org/pepsoft/worldpainter/icons/magnifier_zoom_out.png")) {
public void actionPerformed(ActionEvent e) {
if (getZoom() > -4) {
setZoom(getZoom() - 1);
setActionStates();
}
}
};
private final Action rotateLeft = new AbstractAction("Zoom In", IconUtils.loadScaledIcon("org/pepsoft/worldpainter/icons/arrow_left.png")) {
public void actionPerformed(ActionEvent e) {
azimuth = MathUtils.mod(azimuth - 15.0, 360.0);
tileProvider.setAzimuth(azimuth);
refresh(true);
}
};
private final Action rotateRight = new AbstractAction("Zoom In", IconUtils.loadScaledIcon("org/pepsoft/worldpainter/icons/arrow_right.png")) {
public void actionPerformed(ActionEvent e) {
azimuth = MathUtils.mod(azimuth + 15.0, 360.0);
tileProvider.setAzimuth(azimuth);
refresh(true);
}
};
private final Action rotateUp = new AbstractAction("Zoom In", IconUtils.loadScaledIcon("org/pepsoft/worldpainter/icons/arrow_up.png")) {
public void actionPerformed(ActionEvent e) {
double oldInclination = inclination;
inclination = Math.max(inclination - 15.0, 30.0);
if (inclination != oldInclination) {
setActionStates();
tileProvider.setInclination(inclination);
refresh(true);
}
}
};
private final Action rotateDown = new AbstractAction("Zoom In", IconUtils.loadScaledIcon("org/pepsoft/worldpainter/icons/arrow_down.png")) {
public void actionPerformed(ActionEvent e) {
double oldInclination = inclination;
inclination = Math.min(inclination + 15.0, 90.0);
if (inclination != oldInclination) {
setActionStates();
tileProvider.setInclination(inclination);
refresh(true);
}
}
};
private final Action reset = new AbstractAction("Reset") {
public void actionPerformed(ActionEvent e) {
if ((azimuth != initialAzimuth) || (inclination != initialInclination) || (getZoom() != initialZoom)) {
if (azimuth != initialAzimuth) {
azimuth = initialAzimuth;
tileProvider.setAzimuth(azimuth);
}
if (inclination != initialInclination) {
inclination = initialInclination;
tileProvider.setInclination(inclination);
}
if (getZoom() != initialZoom) {
setZoom(initialZoom); // Will also refresh the view
} else {
refresh(true);
}
setActionStates();
}
}
};
private final double initialAzimuth, initialInclination;
private final int initialZoom;
private WPObject object;
private DynMapTileProvider tileProvider;
private double azimuth, inclination;
private boolean caves;
}