Importing source code for MicropolisJ, the Java rewrite of Micropolis.
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
This commit is contained in:
parent
99e2b7dd01
commit
ed6795dfca
189 changed files with 13184 additions and 0 deletions
320
src/micropolisj/gui/GraphsPane.java
Normal file
320
src/micropolisj/gui/GraphsPane.java
Normal file
|
@ -0,0 +1,320 @@
|
|||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in a new issue