Initial Voronoi commit
parent
f6a1cf52e4
commit
c03d39eed6
|
@ -26,6 +26,7 @@ import amidst.gui.main.menu.MovePlayerPopupMenu;
|
|||
import amidst.gui.main.viewer.ViewerFacade;
|
||||
import amidst.gui.seedsearcher.SeedSearcherWindow;
|
||||
import amidst.gui.text.TextWindow;
|
||||
import amidst.gui.voronoi.VoronoiWindow;
|
||||
import amidst.logging.AmidstLogger;
|
||||
import amidst.minetest.world.mapgen.DefaultBiomes;
|
||||
import amidst.mojangapi.world.WorldSeed;
|
||||
|
@ -330,6 +331,19 @@ public class Actions {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is more useful for Minetest than Minecraft, as Minetest has biomes
|
||||
* determined by heat and humidy, which Minecraft hasn't had since early betas
|
||||
*/
|
||||
@CalledOnlyBy(AmidstThread.EDT)
|
||||
public void displayBiomeProfileVoronoi() {
|
||||
if (gameEngineDetails.getType() != GameEngineType.MINECRAFT) {
|
||||
VoronoiWindow.showDiagram(biomeAuthority.getBiomeProfileSelection());
|
||||
} else {
|
||||
dialogs.displayInfo("Biome profile Voronoi diagram", "Minecraft biomes are not determined by heat and humidity.");
|
||||
}
|
||||
}
|
||||
|
||||
@CalledOnlyBy(AmidstThread.EDT)
|
||||
public void about() {
|
||||
dialogs.displayInfo(
|
||||
|
|
|
@ -128,7 +128,8 @@ public class AmidstMenuBuilder {
|
|||
if (biomeAuthority.getBiomeProfileDirectory().isValid()) {
|
||||
result.add(create_Settings_BiomeProfile());
|
||||
}
|
||||
Menus.item(result, actions::displayGeneratorOptions, "View MapGen params", KeyEvent.VK_V);
|
||||
Menus.item(result, actions::displayBiomeProfileVoronoi, "Biome Voronoi", KeyEvent.VK_V);
|
||||
Menus.item(result, actions::displayGeneratorOptions, "View MapGen params", KeyEvent.VK_G);
|
||||
|
||||
result.addSeparator();
|
||||
// @formatter:off
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package amidst.gui.voronoi;
|
||||
|
||||
public class VoronoiGraph {
|
||||
|
||||
}
|
|
@ -0,0 +1,356 @@
|
|||
package amidst.gui.voronoi;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.Stroke;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import amidst.documentation.AmidstThread;
|
||||
import amidst.documentation.CalledByAny;
|
||||
import amidst.documentation.CalledOnlyBy;
|
||||
import amidst.fragment.Fragment;
|
||||
import amidst.gameengineabstraction.world.biome.IBiome;
|
||||
import amidst.logging.AmidstLogger;
|
||||
import amidst.minetest.world.mapgen.MinetestBiome;
|
||||
import amidst.minetest.world.mapgen.MinetestBiomeProfileImpl;
|
||||
import amidst.mojangapi.world.Dimension;
|
||||
import amidst.mojangapi.world.coordinates.CoordinatesInWorld;
|
||||
|
||||
public class VoronoiPanel extends JPanel {
|
||||
|
||||
public static final int FLAG_SHOWLABELS = 0x01;
|
||||
public static final int FLAG_SHOWAXIS = 0x02;
|
||||
public static final int FLAG_SHOWNODES = 0x04;
|
||||
public static final int FLAG_SHOWDISTRIBUTION = 0x08;
|
||||
|
||||
public static final boolean GRAPHICS_DEBUG = false;
|
||||
|
||||
private static final float AXIS_WIDTH = 0.5f;
|
||||
private static final int TICKMARK_WIDTH_SMALL = 3;
|
||||
private static final int TICKMARK_WIDTH_LARGE = 6;
|
||||
private static final int TICKMARK_LABEL_SPACE = 1;
|
||||
private static final int NODE_RADIUS = 0;
|
||||
private static final int NODE_LABEL_SPACE = 0;
|
||||
private static final int NODE_LABEL_FONTSIZE = 3;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static Stroke stroke_capButt = new BasicStroke(AXIS_WIDTH, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
|
||||
private static Stroke stroke_capSquare = new BasicStroke(AXIS_WIDTH, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND);
|
||||
private static AffineTransform noTransform = new AffineTransform();
|
||||
|
||||
public int axis_min = -40;
|
||||
public int axis_max = 140;
|
||||
public int graph_resolution = 1000;
|
||||
|
||||
private MinetestBiome[] nodes;
|
||||
private int renderFlags;
|
||||
private BufferedImage graph;
|
||||
private int[] rgbArray;
|
||||
|
||||
@Override
|
||||
@CalledOnlyBy(AmidstThread.EDT)
|
||||
public void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
|
||||
createBufferedImage(graph_resolution);
|
||||
|
||||
this.setBorder(null);
|
||||
Rectangle panelBounds = getBounds();
|
||||
if (panelBounds.width > 0 && panelBounds.height > 0) {
|
||||
|
||||
Graphics2D g2d = (Graphics2D) g.create();
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
|
||||
if (GRAPHICS_DEBUG) {
|
||||
g2d.setColor(Color.RED);
|
||||
g2d.fillRect(0, 0, panelBounds.width, panelBounds.height);
|
||||
}
|
||||
|
||||
int graphSize;
|
||||
int desiredAxisLength = axis_max - axis_min;
|
||||
AffineTransform graphTransform = new AffineTransform(g2d.getTransform());
|
||||
// set up a square render area of (graphSize by graphSize) that's centered
|
||||
if (panelBounds.width > panelBounds.height) {
|
||||
graphSize = panelBounds.height;
|
||||
graphTransform.translate((panelBounds.width - graphSize) / 2.0, 0);
|
||||
} else {
|
||||
graphSize = panelBounds.width;
|
||||
graphTransform.translate(0, (panelBounds.height - graphSize) / 2.0);
|
||||
}
|
||||
// Flip the axis to y points up, and scale desiredAxisLength to full the centered render area
|
||||
graphTransform.scale(graphSize / (double)desiredAxisLength, -graphSize / (double)desiredAxisLength);
|
||||
graphTransform.translate(-axis_min, -axis_max);
|
||||
g2d.setTransform(graphTransform);
|
||||
g2d.setFont(new Font("SansSerif", Font.PLAIN, Math.round(NODE_LABEL_FONTSIZE * graphSize / (float)desiredAxisLength)));
|
||||
|
||||
// set graph background to white
|
||||
g2d.setColor(Color.WHITE);
|
||||
g2d.fillRect(axis_min, axis_min, axis_max - axis_min, axis_max - axis_min);
|
||||
|
||||
//drawVoronoi(g2d);
|
||||
drawVoronoiGraph(rgbArray, graph_resolution, graph_resolution);
|
||||
graph.setRGB(0, 0, graph_resolution, graph_resolution, rgbArray, 0, graph_resolution);
|
||||
g2d.drawImage(graph, axis_min, axis_min, desiredAxisLength, desiredAxisLength, Color.WHITE, null);
|
||||
|
||||
// draw nodes
|
||||
drawNodesOrNodeLabels(g2d);
|
||||
|
||||
// draw axis
|
||||
if ((renderFlags & FLAG_SHOWAXIS) > 0) drawAxes(g2d);
|
||||
}
|
||||
}
|
||||
|
||||
@CalledOnlyBy(AmidstThread.EDT)
|
||||
private void drawNodesOrNodeLabels(Graphics2D g2d) {
|
||||
|
||||
AffineTransform currentTransform = g2d.getTransform();
|
||||
FontMetrics metrics = g2d.getFontMetrics(g2d.getFont());
|
||||
boolean showNodes = (renderFlags & FLAG_SHOWNODES) > 0;
|
||||
boolean showLabels = (renderFlags & FLAG_SHOWLABELS) > 0;
|
||||
|
||||
for (short i = 0; i < nodes.length; i++) {
|
||||
MinetestBiome biome = nodes[i];
|
||||
int x = Math.round(biome.heat_point);
|
||||
int y = Math.round(biome.humidity_point);
|
||||
|
||||
if (perceivedBrightness(biome.getDefaultColor().getColor()) < 120) {
|
||||
g2d.setColor(Color.WHITE);
|
||||
} else {
|
||||
g2d.setColor(Color.BLACK);
|
||||
}
|
||||
|
||||
if (showNodes) {
|
||||
g2d.fillOval(x - NODE_RADIUS, y - NODE_RADIUS, 1 + NODE_RADIUS * 2, 1 + NODE_RADIUS * 2);
|
||||
}
|
||||
|
||||
if (showLabels) {
|
||||
Point2D fontPos = new Point(x, y - (showNodes ? NODE_LABEL_SPACE : 0));
|
||||
Point2D fontPosTransformed = currentTransform.transform(fontPos, null);
|
||||
|
||||
g2d.setTransform(noTransform);
|
||||
g2d.drawString(
|
||||
biome.getName(),
|
||||
(float)fontPosTransformed.getX() - (metrics.stringWidth(biome.getName()) / 2),
|
||||
(float)fontPosTransformed.getY() + (metrics.getAscent() / (showNodes ? 1 : -4))
|
||||
);
|
||||
g2d.setTransform(currentTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CalledOnlyBy(AmidstThread.EDT)
|
||||
private void drawAxes(Graphics2D g2d) {
|
||||
|
||||
AffineTransform currentTransform = g2d.getTransform();
|
||||
FontMetrics metrics = g2d.getFontMetrics(g2d.getFont());
|
||||
|
||||
g2d.setStroke(stroke_capSquare);
|
||||
g2d.setColor(Color.BLACK);
|
||||
g2d.drawLine(axis_min, 0, axis_max, 0);
|
||||
g2d.drawLine(0, axis_min, 0, axis_max);
|
||||
|
||||
g2d.setStroke(stroke_capButt);
|
||||
for (int i = axis_min; i <= axis_max; i++) {
|
||||
if (i % 10 == 0 && i != 0) {
|
||||
if (i % 100 == 0) {
|
||||
g2d.drawLine(0, i, -TICKMARK_WIDTH_LARGE, i);
|
||||
g2d.drawLine(i, 0, i, -TICKMARK_WIDTH_LARGE);
|
||||
|
||||
Point2D fontPosTemp = new Point(-TICKMARK_WIDTH_LARGE - TICKMARK_LABEL_SPACE, i);
|
||||
Point2D fontPosHumidy = new Point(i, -TICKMARK_WIDTH_LARGE - TICKMARK_LABEL_SPACE);
|
||||
|
||||
g2d.setTransform(noTransform);
|
||||
Point2D fontPosTransformed = currentTransform.transform(fontPosTemp, null);
|
||||
g2d.drawString(
|
||||
String.valueOf(i),
|
||||
(float)fontPosTransformed.getX() - metrics.stringWidth(String.valueOf(i)),
|
||||
(float)fontPosTransformed.getY() + metrics.getAscent() / 3
|
||||
);
|
||||
fontPosTransformed = currentTransform.transform(fontPosHumidy, null);
|
||||
g2d.drawString(
|
||||
String.valueOf(i),
|
||||
(float)fontPosTransformed.getX() - metrics.stringWidth(String.valueOf(i)) / 2,
|
||||
(float)fontPosTransformed.getY() + metrics.getAscent()
|
||||
);
|
||||
g2d.setTransform(currentTransform);
|
||||
} else {
|
||||
g2d.drawLine(0, i, -TICKMARK_WIDTH_SMALL, i);
|
||||
g2d.drawLine(i, 0, i, -TICKMARK_WIDTH_SMALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((renderFlags & FLAG_SHOWLABELS) > 0) {
|
||||
// label the axes
|
||||
g2d.setFont(new Font("Serif", Font.PLAIN, (int)Math.round(NODE_LABEL_FONTSIZE * 1.7f * currentTransform.getScaleX())));
|
||||
metrics = g2d.getFontMetrics(g2d.getFont());
|
||||
|
||||
Point2D fontPosHumidy = new Point(-TICKMARK_WIDTH_LARGE * 2, axis_max / 2);
|
||||
Point2D fontPosTemp = new Point(axis_max / 2, -TICKMARK_WIDTH_LARGE * 2);
|
||||
|
||||
g2d.setTransform(noTransform);
|
||||
Point2D fontPosTransformed = currentTransform.transform(fontPosTemp, null);
|
||||
String value = "Temperature";
|
||||
g2d.drawString(
|
||||
value,
|
||||
(float)fontPosTransformed.getX() - metrics.stringWidth(value),
|
||||
(float)fontPosTransformed.getY() + metrics.getAscent() / 4
|
||||
);
|
||||
|
||||
fontPosTransformed = currentTransform.transform(fontPosHumidy, null);
|
||||
value = "Humidity";
|
||||
drawRotatedText(
|
||||
g2d,
|
||||
(float)fontPosTransformed.getX() + metrics.getAscent() / 4,
|
||||
(float)fontPosTransformed.getY() + metrics.stringWidth(value),
|
||||
270,
|
||||
value
|
||||
);
|
||||
|
||||
g2d.setTransform(currentTransform);
|
||||
}
|
||||
}
|
||||
|
||||
public static void drawRotatedText(Graphics2D g2d, double x, double y, int angle, String text)
|
||||
{
|
||||
g2d.translate((float)x,(float)y);
|
||||
g2d.rotate(Math.toRadians(angle));
|
||||
g2d.drawString(text,0,0);
|
||||
g2d.rotate(-Math.toRadians(angle));
|
||||
g2d.translate(-(float)x,-(float)y);
|
||||
}
|
||||
|
||||
|
||||
/** returns the perceived brightness of col, between 0 (dark) and 255 (bight) */
|
||||
@CalledByAny
|
||||
public double perceivedBrightness(Color col)
|
||||
{
|
||||
return Math.sqrt(
|
||||
.299 * Math.pow(col.getRed(), 2) +
|
||||
.587 * Math.pow(col.getGreen(), 2) +
|
||||
.114 * Math.pow(col.getBlue(), 2)
|
||||
);
|
||||
}
|
||||
|
||||
/** Create the graph BufferedImage if it doesn't already exist at the correct size */
|
||||
private void createBufferedImage(int size) {
|
||||
if (graph == null || graph.getWidth() != size) {
|
||||
graph = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
|
||||
rgbArray = new int[size * size];
|
||||
}
|
||||
}
|
||||
|
||||
@CalledByAny
|
||||
private void drawVoronoiGraph(int[] rgbArray, int width, int height) {
|
||||
|
||||
if (nodes == null) return;
|
||||
|
||||
MinetestBiome biome_closest;
|
||||
float dist_min;
|
||||
int nodeCount = nodes.length;
|
||||
float xScale = (axis_max - axis_min) / (float)width;
|
||||
float yScale = (axis_max - axis_min) / (float)height;
|
||||
int index = 0;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
float humidity = (y * yScale) + axis_min;
|
||||
|
||||
// TODO: break up rgbArray?
|
||||
|
||||
for (int x = 0; x < width; x++) {
|
||||
float heat = (x * xScale) + axis_min;
|
||||
|
||||
dist_min = Float.MAX_VALUE;
|
||||
biome_closest = null;
|
||||
|
||||
for (short i = 0; i < nodeCount; i++) {
|
||||
MinetestBiome b = nodes[i];
|
||||
float d_heat = heat - b.heat_point;
|
||||
float d_humidity = humidity - b.humidity_point;
|
||||
float dist = (d_heat * d_heat) + (d_humidity * d_humidity);
|
||||
|
||||
if (dist < dist_min) {
|
||||
dist_min = dist;
|
||||
biome_closest = b;
|
||||
}
|
||||
}
|
||||
rgbArray[index++] = (biome_closest == null) ? 0x00000000 : biome_closest.getDefaultColor().getRGB();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void drawVoronoi(Graphics2D g2d) {
|
||||
|
||||
if (nodes == null) return;
|
||||
|
||||
MinetestBiome biome_closest;
|
||||
float dist_min;
|
||||
int nodeCount = nodes.length;
|
||||
|
||||
for (int y = axis_min; y <= axis_max; y++) {
|
||||
for (int x = axis_min; x <= axis_max; x++) {
|
||||
|
||||
float heat = x;
|
||||
float humidity = y;
|
||||
dist_min = Float.MAX_VALUE;
|
||||
biome_closest = null;
|
||||
|
||||
for (short i = 0; i < nodeCount; i++) {
|
||||
MinetestBiome b = nodes[i];
|
||||
float d_heat = heat - b.heat_point;
|
||||
float d_humidity = humidity - b.humidity_point;
|
||||
float dist = (d_heat * d_heat) + (d_humidity * d_humidity);
|
||||
|
||||
if (dist < dist_min) {
|
||||
dist_min = dist;
|
||||
biome_closest = b;
|
||||
}
|
||||
}
|
||||
if (biome_closest != null) {
|
||||
g2d.setColor(biome_closest.getDefaultColor().getColor());
|
||||
g2d.drawRect(x, y, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CalledOnlyBy(AmidstThread.EDT)
|
||||
public void Update(MinetestBiomeProfileImpl biomeProfile, int height, int flags) {
|
||||
|
||||
ArrayList<MinetestBiome> biomes = new ArrayList<MinetestBiome>();
|
||||
if (biomeProfile != null) for (IBiome biome : biomeProfile.allBiomes()) {
|
||||
MinetestBiome mtBiome = (MinetestBiome)biome;
|
||||
if (height <= (mtBiome.y_max + mtBiome.vertical_blend) && height >= mtBiome.y_min) {
|
||||
biomes.add(mtBiome);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<MinetestBiome> currentNodes = nodes == null ? new ArrayList<MinetestBiome>() : new ArrayList<MinetestBiome>(Arrays.asList(nodes));
|
||||
if (flags != this.renderFlags || !currentNodes.equals(biomes)) {
|
||||
this.renderFlags = flags;
|
||||
this.nodes = biomes.toArray(new MinetestBiome[biomes.size()]);
|
||||
invalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public int getRenderFlags() { return renderFlags; }
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
package amidst.gui.voronoi;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import amidst.documentation.AmidstThread;
|
||||
import amidst.documentation.CalledOnlyBy;
|
||||
import amidst.minetest.world.mapgen.MinetestBiomeProfileImpl;
|
||||
import amidst.settings.biomeprofile.BiomeProfile;
|
||||
import amidst.settings.biomeprofile.BiomeProfileSelection;
|
||||
import amidst.settings.biomeprofile.BiomeProfileUpdateListener;
|
||||
|
||||
public class VoronoiWindow implements BiomeProfileUpdateListener, ChangeListener {
|
||||
|
||||
private static final int ALTITUDESLIDER_DEFAULT_LOW = -40;
|
||||
private static final int ALTITUDESLIDER_DEFAULT_HIGH = 200;
|
||||
|
||||
private static VoronoiWindow voronoiWindow = null;
|
||||
private BiomeProfileSelection biomeProfileSelection;
|
||||
|
||||
private final JFrame windowFrame;
|
||||
private VoronoiPanel voronoiPanel;
|
||||
private JSlider altitudeSlider;
|
||||
private JLabel graphHeading;
|
||||
private JCheckBox option_showAxis;
|
||||
private JCheckBox option_showLabels;
|
||||
private JCheckBox option_showNodes;
|
||||
private JSpinner altitudeOffset;
|
||||
|
||||
/** the profile being used by Amidstest **/
|
||||
private MinetestBiomeProfileImpl selectedProfile = null;
|
||||
|
||||
@CalledOnlyBy(AmidstThread.EDT)
|
||||
private VoronoiWindow() {
|
||||
this.windowFrame = createWindowFrame(800, 764);
|
||||
setOptionFlagsInDialog(VoronoiPanel.FLAG_SHOWLABELS | VoronoiPanel.FLAG_SHOWAXIS | VoronoiPanel.FLAG_SHOWNODES);
|
||||
}
|
||||
|
||||
@CalledOnlyBy(AmidstThread.EDT)
|
||||
private JFrame createWindowFrame(int width, int height) {
|
||||
JFrame result = new JFrame();
|
||||
|
||||
result.getContentPane().setLayout(new MigLayout());
|
||||
result.setSize(width, height);
|
||||
result.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
result.setVisible(false);
|
||||
}
|
||||
});
|
||||
|
||||
this.voronoiPanel = new VoronoiPanel();
|
||||
result.add(voronoiPanel, "grow, pushx, spany 2");// the next row (which we span) will be "push", so don't do it here - the rest of this row needs to be thin
|
||||
|
||||
JLabel heightLabel = new JLabel("Altitude");
|
||||
result.add(heightLabel, "center, wrap");
|
||||
|
||||
altitudeSlider = new JSlider(JSlider.VERTICAL, ALTITUDESLIDER_DEFAULT_LOW, ALTITUDESLIDER_DEFAULT_HIGH, 1);
|
||||
altitudeSlider.addChangeListener(this);
|
||||
altitudeSlider.setMajorTickSpacing(10);
|
||||
altitudeSlider.setMinorTickSpacing(5);
|
||||
altitudeSlider.setPaintTicks(true);
|
||||
altitudeSlider.setPaintLabels(true);
|
||||
result.add(altitudeSlider, "grow, pushy, wrap");
|
||||
|
||||
graphHeading = new JLabel();
|
||||
result.add(graphHeading, "center");
|
||||
|
||||
JLabel offsetLabel = new JLabel("Altitude offset:");
|
||||
result.add(offsetLabel, "left, wrap");
|
||||
|
||||
option_showAxis = new JCheckBox("Show axes");
|
||||
option_showNodes = new JCheckBox("Show nodes");
|
||||
option_showLabels = new JCheckBox("Show labels");
|
||||
option_showAxis.addChangeListener(this);
|
||||
option_showNodes.addChangeListener(this);
|
||||
option_showLabels.addChangeListener(this);
|
||||
result.add(option_showAxis, "center, split 3");
|
||||
result.add(option_showNodes, "center");
|
||||
result.add(option_showLabels, "center");
|
||||
|
||||
altitudeOffset = new JSpinner(new SpinnerNumberModel(0, Short.MIN_VALUE - ALTITUDESLIDER_DEFAULT_LOW, Short.MAX_VALUE - ALTITUDESLIDER_DEFAULT_HIGH, 100));
|
||||
altitudeOffset.addChangeListener(this);
|
||||
result.add(altitudeOffset);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBiomeProfileUpdate(BiomeProfile newBiomeProfile) {
|
||||
UpdateSelectedBiomeProfile(newBiomeProfile);
|
||||
}
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
if (e.getSource() == altitudeOffset) {
|
||||
updateHeightSlider((int)altitudeOffset.getValue());
|
||||
}
|
||||
updateVoronoiDiagram();
|
||||
}
|
||||
|
||||
private void UpdateSelectedBiomeProfile(BiomeProfile newProfile) {
|
||||
|
||||
MinetestBiomeProfileImpl minetestProfile = (newProfile instanceof MinetestBiomeProfileImpl) ? (MinetestBiomeProfileImpl)newProfile : null;
|
||||
|
||||
boolean changed = (minetestProfile == null) ? (selectedProfile != null) : !minetestProfile.equals(selectedProfile);
|
||||
selectedProfile = minetestProfile;
|
||||
|
||||
if (changed) updateVoronoiDiagram();
|
||||
this.windowFrame.setTitle(selectedProfile == null ? "Biome profile Voronoi graph" : "Voronoi graph for " + selectedProfile.getName());
|
||||
}
|
||||
|
||||
private void updateVoronoiDiagram() {
|
||||
EventQueue.invokeLater(
|
||||
() -> {
|
||||
int altitude = getAltitudeFromDialog();
|
||||
voronoiPanel.Update(selectedProfile, altitude, getOptionFlagsFromDialog());
|
||||
graphHeading.setText("Biomes at altitude " + altitude);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@CalledOnlyBy(AmidstThread.EDT)
|
||||
private void updateHeightSlider(int offset) {
|
||||
int oldHeightPosition = altitudeSlider.getValue() - altitudeSlider.getMinimum();
|
||||
altitudeSlider.setMinimum(ALTITUDESLIDER_DEFAULT_LOW + offset);
|
||||
altitudeSlider.setMaximum(ALTITUDESLIDER_DEFAULT_HIGH + offset);
|
||||
altitudeSlider.setValue(ALTITUDESLIDER_DEFAULT_LOW + offset + oldHeightPosition);
|
||||
}
|
||||
|
||||
@CalledOnlyBy(AmidstThread.EDT)
|
||||
private int getAltitudeFromDialog() {
|
||||
return altitudeSlider.getValue();
|
||||
}
|
||||
|
||||
@CalledOnlyBy(AmidstThread.EDT)
|
||||
private void setOptionFlagsInDialog(int optionFlags) {
|
||||
option_showAxis.setSelected((optionFlags & VoronoiPanel.FLAG_SHOWAXIS) > 0);
|
||||
option_showNodes.setSelected((optionFlags & VoronoiPanel.FLAG_SHOWNODES) > 0);
|
||||
option_showLabels.setSelected((optionFlags & VoronoiPanel.FLAG_SHOWLABELS) > 0);
|
||||
}
|
||||
|
||||
@CalledOnlyBy(AmidstThread.EDT)
|
||||
private int getOptionFlagsFromDialog() {
|
||||
int result = 0;
|
||||
if (option_showAxis.isSelected()) result |= VoronoiPanel.FLAG_SHOWAXIS;
|
||||
if (option_showNodes.isSelected()) result |= VoronoiPanel.FLAG_SHOWNODES;
|
||||
if (option_showLabels.isSelected()) result |= VoronoiPanel.FLAG_SHOWLABELS;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void showDiagram(BiomeProfileSelection biomeProfileSelection) {
|
||||
|
||||
if (voronoiWindow == null) {
|
||||
voronoiWindow = new VoronoiWindow();
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> voronoiWindow.show(biomeProfileSelection));
|
||||
}
|
||||
|
||||
@CalledOnlyBy(AmidstThread.EDT)
|
||||
public void show(BiomeProfileSelection biomeProfileSelection) {
|
||||
|
||||
BiomeProfile newProfile = null;
|
||||
|
||||
if (this.biomeProfileSelection != null) {
|
||||
this.biomeProfileSelection.removeUpdateListener(this);
|
||||
}
|
||||
this.biomeProfileSelection = biomeProfileSelection;
|
||||
if (biomeProfileSelection != null) {
|
||||
this.biomeProfileSelection.addUpdateListener(this);
|
||||
newProfile = this.biomeProfileSelection.getCurrentBiomeProfile();
|
||||
}
|
||||
|
||||
UpdateSelectedBiomeProfile(newProfile);
|
||||
this.windowFrame.setVisible(true);
|
||||
}
|
||||
}
|
|
@ -33,6 +33,34 @@ public class MinetestBiome extends BiomeBase {
|
|||
super.setIndex(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) return false;
|
||||
if (!MinetestBiome.class.isAssignableFrom(obj.getClass())) return false;
|
||||
final MinetestBiome other = (MinetestBiome)obj;
|
||||
if ((this.getName() == null) ? (other.getName() != null) : !this.getName().equals(other.getName())) return false;
|
||||
if ((this.getDefaultColor() == null) ? (other.getDefaultColor() != null) : !this.getDefaultColor().equals(other.getDefaultColor())) return false;
|
||||
|
||||
return y_min == other.y_min &&
|
||||
y_max == other.y_max &&
|
||||
heat_point == other.heat_point &&
|
||||
humidity_point == other.humidity_point &&
|
||||
vertical_blend == other.vertical_blend;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
hash = 37 * hash + (this.getName() != null ? this.getName().hashCode() : 0);
|
||||
hash = 37 * hash + (this.getDefaultColor() != null ? this.getDefaultColor().hashCode() : 0);
|
||||
hash = 37 * hash + (int)y_min;
|
||||
hash = 37 * hash + (int)y_max;
|
||||
hash = 37 * hash + (int)Float.floatToIntBits(heat_point);
|
||||
hash = 37 * hash + (int)Float.floatToIntBits(humidity_point);
|
||||
hash = 37 * hash + (int)vertical_blend;
|
||||
return hash;
|
||||
}
|
||||
|
||||
public short y_min;
|
||||
public short y_max;
|
||||
public float heat_point;
|
||||
|
|
|
@ -159,6 +159,26 @@ public class MinetestBiomeProfileImpl implements BiomeProfile {
|
|||
public boolean save(File file) {
|
||||
return writeToFile(file, serialize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) return false;
|
||||
if (!MinetestBiomeProfileImpl.class.isAssignableFrom(obj.getClass())) return false;
|
||||
final MinetestBiomeProfileImpl other = (MinetestBiomeProfileImpl)obj;
|
||||
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) return false;
|
||||
if ((this.shortcut == null) ? (other.shortcut != null) : !this.shortcut.equals(other.shortcut)) return false;
|
||||
if ((this.biomeList == null) ? (other.biomeList != null) : !this.biomeList.equals(other.biomeList)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
hash = 37 * hash + this.name.hashCode();
|
||||
hash = 37 * hash + this.shortcut.hashCode();
|
||||
hash = 37 * hash + this.biomeList.hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
private String serialize() {
|
||||
String output = "{ \"name\":\"" + name + "\", \"biomeList\":[\r\n";
|
||||
|
|
|
@ -117,4 +117,16 @@ public class BiomeColor {
|
|||
private int lighten(int x) {
|
||||
return Math.min(x + LIGHTEN_BRIGHTNESS, 0xFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) return false;
|
||||
if (!BiomeColor.class.isAssignableFrom(obj.getClass())) return false;
|
||||
return rgb == ((BiomeColor)obj).rgb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return rgb;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue