This edition of Micropolis, written for the Java desktop platform, is fairly feature complete. I believe the only missing functionality is that of loading the built-in scenarios, and this can be implemented if there is any demand for it. I will soon update the home page at http://code.google.com/p/micropolis/ with downloadable packages of this edition of the software. git-svn-id: https://micropolis.googlecode.com/svn/trunk/micropolis-java@528 d9718cc8-9f43-0410-858b-315f434eb58c
320 lines
9 KiB
Java
320 lines
9 KiB
Java
// This file is part of MicropolisJ.
|
|
// Copyright (C) 2013 Jason Long
|
|
// Portions Copyright (C) 1989-2007 Electronic Arts Inc.
|
|
//
|
|
// MicropolisJ is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU GPLv3, with additional terms.
|
|
// See the README file, included in this distribution, for details.
|
|
|
|
package micropolisj.gui;
|
|
|
|
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import java.awt.geom.Path2D;
|
|
import java.text.*;
|
|
import java.util.*;
|
|
import javax.swing.*;
|
|
|
|
import micropolisj.engine.*;
|
|
import static micropolisj.gui.ColorParser.parseColor;
|
|
|
|
public class GraphsPane extends JPanel
|
|
implements Micropolis.Listener
|
|
{
|
|
Micropolis engine;
|
|
|
|
JToggleButton tenYearsBtn;
|
|
JToggleButton onetwentyYearsBtn;
|
|
GraphArea graphArea;
|
|
|
|
static enum TimePeriod
|
|
{
|
|
TEN_YEARS,
|
|
ONETWENTY_YEARS;
|
|
}
|
|
|
|
static enum GraphData
|
|
{
|
|
RESPOP,
|
|
COMPOP,
|
|
INDPOP,
|
|
MONEY,
|
|
CRIME,
|
|
POLLUTION;
|
|
}
|
|
EnumMap<GraphData,JToggleButton> dataBtns = new EnumMap<>(GraphData.class);
|
|
|
|
static ResourceBundle strings = MainWindow.strings;
|
|
static final int LEFT_MARGIN = 4;
|
|
static final int RIGHT_MARGIN = 4;
|
|
static final int TOP_MARGIN = 2;
|
|
static final int BOTTOM_MARGIN = 2;
|
|
static final int LEGEND_PADDING = 6;
|
|
|
|
public GraphsPane(Micropolis engine)
|
|
{
|
|
super(new BorderLayout());
|
|
|
|
assert engine != null;
|
|
this.engine = engine;
|
|
engine.addListener(this);
|
|
|
|
JButton dismissBtn = new JButton(strings.getString("dismiss_graph"));
|
|
dismissBtn.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent evt) {
|
|
onDismissClicked();
|
|
}});
|
|
add(dismissBtn, BorderLayout.SOUTH);
|
|
|
|
JPanel b1 = new JPanel(new BorderLayout());
|
|
add(b1, BorderLayout.CENTER);
|
|
|
|
JPanel toolsPane = new JPanel(new GridBagLayout());
|
|
b1.add(toolsPane, BorderLayout.WEST);
|
|
|
|
GridBagConstraints c = new GridBagConstraints();
|
|
c.gridx = c.gridy = 0;
|
|
c.gridwidth = 2;
|
|
c.fill = GridBagConstraints.BOTH;
|
|
c.insets = new Insets(1,1,1,1);
|
|
tenYearsBtn = new JToggleButton(strings.getString("ten_years"));
|
|
tenYearsBtn.setMargin(new Insets(0,0,0,0));
|
|
tenYearsBtn.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent evt) {
|
|
setTimePeriod(TimePeriod.TEN_YEARS);
|
|
}});
|
|
toolsPane.add(tenYearsBtn, c);
|
|
|
|
c.gridy++;
|
|
onetwentyYearsBtn = new JToggleButton(strings.getString("onetwenty_years"));
|
|
onetwentyYearsBtn.setMargin(new Insets(0,0,0,0));
|
|
onetwentyYearsBtn.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent evt) {
|
|
setTimePeriod(TimePeriod.ONETWENTY_YEARS);
|
|
}});
|
|
toolsPane.add(onetwentyYearsBtn, c);
|
|
|
|
c.gridx = 0;
|
|
c.gridy = 2;
|
|
c.gridwidth = 1;
|
|
c.anchor = GridBagConstraints.NORTH;
|
|
c.weightx = 0.5;
|
|
toolsPane.add(makeDataBtn(GraphData.RESPOP), c);
|
|
|
|
c.gridy = 3;
|
|
toolsPane.add(makeDataBtn(GraphData.COMPOP), c);
|
|
|
|
c.gridy = 4;
|
|
toolsPane.add(makeDataBtn(GraphData.INDPOP), c);
|
|
|
|
c.gridx = 1;
|
|
c.gridy = 2;
|
|
toolsPane.add(makeDataBtn(GraphData.MONEY), c);
|
|
|
|
c.gridy = 3;
|
|
toolsPane.add(makeDataBtn(GraphData.CRIME), c);
|
|
|
|
c.gridy = 4;
|
|
toolsPane.add(makeDataBtn(GraphData.POLLUTION), c);
|
|
|
|
graphArea = new GraphArea();
|
|
b1.add(graphArea, BorderLayout.CENTER);
|
|
|
|
setTimePeriod(TimePeriod.TEN_YEARS);
|
|
dataBtns.get(GraphData.MONEY).setSelected(true);
|
|
dataBtns.get(GraphData.POLLUTION).setSelected(true);
|
|
}
|
|
|
|
public void setEngine(Micropolis newEngine)
|
|
{
|
|
if (engine != null) { //old engine
|
|
engine.removeListener(this);
|
|
}
|
|
engine = newEngine;
|
|
if (engine != null) { //new engine
|
|
engine.addListener(this);
|
|
graphArea.repaint();
|
|
}
|
|
}
|
|
|
|
private void onDismissClicked()
|
|
{
|
|
setVisible(false);
|
|
}
|
|
|
|
//implements Micropolis.Listener
|
|
public void cityMessage(MicropolisMessage message, CityLocation loc, boolean isPic) {}
|
|
public void citySound(Sound sound, CityLocation loc) {}
|
|
public void demandChanged() {}
|
|
public void evaluationChanged() {}
|
|
public void fundsChanged() {}
|
|
public void optionsChanged() {}
|
|
|
|
//implements Micropolis.Listener
|
|
public void censusChanged()
|
|
{
|
|
graphArea.repaint();
|
|
}
|
|
|
|
private JToggleButton makeDataBtn(GraphData graph)
|
|
{
|
|
String icon1name = strings.getString("graph_button."+graph.name());
|
|
String icon2name = strings.getString("graph_button."+graph.name()+".selected");
|
|
|
|
ImageIcon icon1 = new ImageIcon(getClass().getResource("/"+icon1name));
|
|
ImageIcon icon2 = new ImageIcon(getClass().getResource("/"+icon2name));
|
|
|
|
JToggleButton btn = new JToggleButton();
|
|
btn.setIcon(icon1);
|
|
btn.setSelectedIcon(icon2);
|
|
btn.setBorder(null);
|
|
btn.setBorderPainted(false);
|
|
btn.setFocusPainted(false);
|
|
btn.setContentAreaFilled(false);
|
|
btn.setMargin(new Insets(0,0,0,0));
|
|
|
|
btn.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent evt) {
|
|
graphArea.repaint();
|
|
}});
|
|
|
|
dataBtns.put(graph, btn);
|
|
return btn;
|
|
}
|
|
|
|
int getHistoryValue(GraphData graph, int pos)
|
|
{
|
|
assert pos >= 0 && pos < 240;
|
|
switch(graph) {
|
|
case RESPOP: return engine.history.res[pos];
|
|
case COMPOP: return engine.history.com[pos];
|
|
case INDPOP: return engine.history.ind[pos];
|
|
case MONEY: return engine.history.money[pos];
|
|
case CRIME: return engine.history.crime[pos];
|
|
case POLLUTION: return engine.history.pollution[pos];
|
|
default: throw new Error("unexpected");
|
|
}
|
|
}
|
|
|
|
void setTimePeriod(TimePeriod period)
|
|
{
|
|
tenYearsBtn.setSelected(period == TimePeriod.TEN_YEARS);
|
|
onetwentyYearsBtn.setSelected(period == TimePeriod.ONETWENTY_YEARS);
|
|
graphArea.repaint();
|
|
}
|
|
|
|
class GraphArea extends JComponent
|
|
{
|
|
GraphArea()
|
|
{
|
|
setBorder(BorderFactory.createLoweredBevelBorder());
|
|
}
|
|
|
|
@Override
|
|
public void paintComponent(Graphics gr1)
|
|
{
|
|
Graphics2D gr = (Graphics2D)gr1;
|
|
FontMetrics fm = gr.getFontMetrics();
|
|
|
|
gr.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
gr.setColor(Color.WHITE);
|
|
gr.fill(gr.getClipBounds());
|
|
|
|
// determine length of longest label
|
|
int maxLabelWidth = 0;
|
|
for (GraphData gd : GraphData.values())
|
|
{
|
|
String labelStr = strings.getString("graph_label."+gd.name());
|
|
int adv = fm.stringWidth(labelStr);
|
|
if (adv > maxLabelWidth) {
|
|
maxLabelWidth = adv;
|
|
}
|
|
}
|
|
|
|
int leftEdge = getInsets().left + LEFT_MARGIN;
|
|
int topEdge = getInsets().top + TOP_MARGIN + fm.getHeight()*2;
|
|
int bottomEdge = getHeight() - getInsets().bottom - getInsets().top - BOTTOM_MARGIN;
|
|
int rightEdge = getWidth() - getInsets().right - getInsets().left - RIGHT_MARGIN - maxLabelWidth - LEGEND_PADDING;
|
|
|
|
// draw graph lower, upper borders
|
|
gr.setColor(Color.BLACK);
|
|
gr.drawLine(leftEdge,topEdge,rightEdge,topEdge);
|
|
gr.drawLine(leftEdge,bottomEdge,rightEdge,bottomEdge);
|
|
|
|
// draw vertical bars and label the dates
|
|
boolean isOneTwenty = onetwentyYearsBtn.isSelected();
|
|
int unitPeriod = isOneTwenty ? 12*Micropolis.CENSUSRATE : Micropolis.CENSUSRATE;
|
|
int hashPeriod = isOneTwenty ? 10*unitPeriod : 12*unitPeriod;
|
|
int startTime = ((engine.history.cityTime / unitPeriod) - 119) * unitPeriod;
|
|
|
|
double x_interval = (rightEdge - leftEdge) / 120.0;
|
|
for (int i = 0; i < 120; i++) {
|
|
int t = startTime + i * unitPeriod; // t might be negative
|
|
if (t % hashPeriod == 0) {
|
|
// year
|
|
int year = 1900+(t/(12*Micropolis.CENSUSRATE));
|
|
int numHashes = t/hashPeriod;
|
|
int x = (int)Math.round(leftEdge+i*x_interval);
|
|
int y = getInsets().top + TOP_MARGIN +
|
|
(numHashes % 2 == 0 ? fm.getHeight() : 0) +
|
|
fm.getAscent();
|
|
gr.drawString(Integer.toString(year), x, y);
|
|
gr.drawLine(x,topEdge,x,bottomEdge);
|
|
}
|
|
}
|
|
|
|
int H = isOneTwenty ? 239 : 119;
|
|
final HashMap<GraphData, Path2D.Double> paths = new HashMap<>();
|
|
for (GraphData gd : GraphData.values())
|
|
{
|
|
if (dataBtns.get(gd).isSelected()) {
|
|
|
|
Path2D.Double path = new Path2D.Double();
|
|
for (int i = 0; i < 120; i++) {
|
|
double xp = leftEdge + i * x_interval;
|
|
double yp = bottomEdge - getHistoryValue(gd,H-i) * (bottomEdge-topEdge) / 256.0;
|
|
if (i == 0) {
|
|
path.moveTo(xp, yp);
|
|
} else {
|
|
path.lineTo(xp, yp);
|
|
}
|
|
}
|
|
paths.put(gd, path);
|
|
}
|
|
}
|
|
|
|
GraphData [] myGraphs = paths.keySet().toArray(new GraphData[0]);
|
|
Arrays.sort(myGraphs, new Comparator<GraphData>() {
|
|
public int compare(GraphData a, GraphData b) {
|
|
double y0 = paths.get(a).getCurrentPoint().getY();
|
|
double y1 = paths.get(b).getCurrentPoint().getY();
|
|
return -Double.compare(y0,y1);
|
|
}});
|
|
|
|
int lbottom = bottomEdge;
|
|
for (GraphData gd : myGraphs)
|
|
{
|
|
String labelStr = strings.getString("graph_label."+gd.name());
|
|
String colStr = strings.getString("graph_color."+gd.name());
|
|
Color col = parseColor(colStr);
|
|
Path2D.Double path = paths.get(gd);
|
|
|
|
gr.setColor(col);
|
|
gr.setStroke(new BasicStroke(2));
|
|
gr.draw(path);
|
|
|
|
int x = rightEdge + LEGEND_PADDING;
|
|
int y = (int)Math.round(path.getCurrentPoint().getY()+fm.getAscent()/2);
|
|
y = Math.min(lbottom, y);
|
|
lbottom = y - fm.getAscent();
|
|
|
|
gr.setColor(col);
|
|
gr.drawString(labelStr, x-1, y);
|
|
gr.drawString(labelStr, x, y-1);
|
|
|
|
gr.setColor(Color.BLACK);
|
|
gr.drawString(labelStr, x, y);
|
|
}
|
|
}
|
|
}
|
|
}
|