commit
b5fd2be761
|
@ -1,3 +1,5 @@
|
|||
bin
|
||||
release
|
||||
.DS_Store
|
||||
.idea
|
||||
target
|
||||
|
|
41
README.md
41
README.md
|
@ -1,53 +1,32 @@
|
|||
Automata Explorer
|
||||
=================
|
||||
|
||||
Autaomata Explorer was a weekend project of mine after a brief but
|
||||
Automata Explorer was a weekend project of mine after a brief but
|
||||
passionate interest in cellular automata back in 2003. This project
|
||||
should therefore _not_ be looked at as an example of my most recent
|
||||
(or best) work. Nevertheless, I am preserving it here on Github for
|
||||
historical interest and pure vanity!
|
||||
|
||||
These days, if you're interested in a much, much better Cellular
|
||||
Automata explorer, I would recommend Golly (http://golly.sourceforge.net/)
|
||||
Automata explorer, I would recommend [Golly](http://golly.sourceforge.net/) (http://golly.sourceforge.net/)
|
||||
|
||||
Building Automata
|
||||
-----------------
|
||||
|
||||
Automata uses the Ant Java build system. To build a JAR file, just type:
|
||||
Automata uses [Maven](https://maven.apache.org/) to build :
|
||||
|
||||
% ant
|
||||
% mvn package
|
||||
|
||||
|
||||
|
||||
To build a full release, including a versioned ZIP file, type:
|
||||
|
||||
% ant release
|
||||
|
||||
Building on OS X
|
||||
----------------
|
||||
|
||||
On OS X, this project uses the JarBundler ant task (another weekend
|
||||
project of mine) to create an executable Java bundle. Type:
|
||||
|
||||
% ant release-macosx
|
||||
|
||||
|
||||
Running Automata Explorer
|
||||
-------------------------
|
||||
|
||||
Automata requires the Java Runtime version 1.3 or later.
|
||||
|
||||
To run by hand from the command line, just type
|
||||
|
||||
UNIX or OS X:
|
||||
|
||||
% java -jar bin/automata.jar
|
||||
|
||||
WINDOWS
|
||||
|
||||
> java -jar BIN\automata.jar
|
||||
|
||||
|
||||
Mac OS X releases will produce a double-clickable bundle, as well.
|
||||
On OS X, this project includes an ICNS resource and defines packaging
|
||||
variables so that the native bundler will generate a disk image installer.
|
||||
To make this work, you'll want to define the property `jfxapp.mac.signing-key`
|
||||
either in `pom.xml` or in your `settings.xml`. To get a signing key, you'll
|
||||
need to register an Apple ID as an [Apple developer](https://developer.apple.com).
|
||||
|
||||
|
||||
License
|
||||
|
|
104
build.xml
104
build.xml
|
@ -1,104 +0,0 @@
|
|||
<project name="Automata" default="build-all" basedir=".">
|
||||
<property name="id" value="$Id: build.xml,v 1.16 2003/10/13 04:05:41 sethm Exp $"/>
|
||||
<property name="version" value="1.2.4"/>
|
||||
<property name="appname" value="automata"/>
|
||||
<property name="dir.classes" value="${basedir}/classes" />
|
||||
<property name="dir.release" value="${basedir}/release" />
|
||||
<property name="dir.resources" value="${basedir}/resources" />
|
||||
<property name="dir.bin" value="${basedir}/bin" />
|
||||
<property name="dir.src" value="${basedir}/src" />
|
||||
<property name="dir.doc" value="${basedir}/javadoc" />
|
||||
<property name="dir.lib" value="${basedir}/lib" />
|
||||
<property name="main.class" value="com.loomcom.automata.Launcher" />
|
||||
|
||||
<property name="lib.jarbundler" value="${dir.lib}/jarbundler-2.2.0.jar" />
|
||||
|
||||
<property name="jar" value="${appname}.jar" />
|
||||
<property name="zip" value="${appname}-${version}.zip" />
|
||||
<property name="tar" value="${appname}-${version}.tar" />
|
||||
|
||||
<taskdef name="jarbundler"
|
||||
classpath="${lib.jarbundler}"
|
||||
classname="net.sourceforge.jarbundler.JarBundler"/>
|
||||
|
||||
<target name="jar" depends="compile">
|
||||
<jar jarfile="${dir.bin}/${jar}"
|
||||
basedir="${dir.classes}">
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="${main.class}" />
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<target name="compile">
|
||||
<mkdir dir="${dir.classes}"/>
|
||||
<mkdir dir="${dir.bin}"/>
|
||||
<javac srcdir="${dir.src}"
|
||||
destdir="${dir.classes}"/>
|
||||
</target>
|
||||
|
||||
<target name="javadoc">
|
||||
<javadoc destdir="${dir.doc}"
|
||||
author="true"
|
||||
version="true"
|
||||
use="true"
|
||||
windowtitle="Automata">
|
||||
<packageset dir="${dir.src}" defaultexcludes="yes">
|
||||
<include name="com/loomcom/automata/**" />
|
||||
<exclude name="CVS" />
|
||||
</packageset>
|
||||
</javadoc>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="release" depends="build-all">
|
||||
<mkdir dir="${dir.release}"/>
|
||||
<zip destfile="${dir.release}/${zip}">
|
||||
<zipfileset dir="${basedir}"
|
||||
prefix="${appname}-${version}/"
|
||||
includes="LICENSE.TXT,README.TXT,build.xml,src/**,javadoc/**,bin/**,lib/**"/>
|
||||
</zip>
|
||||
<tar destfile="${dir.release}/${tar}">
|
||||
<tarfileset dir="${basedir}"
|
||||
prefix="${appname}-${version}/"
|
||||
includes="LICENSE.TXT,README.TXT,build.xml,src/**,javadoc/**,bin/**,lib/**"/>
|
||||
</tar>
|
||||
<!-- Compress the tar -->
|
||||
<gzip src="${dir.release}/${tar}" zipfile="${dir.release}/${tar}.gz"/>
|
||||
<!-- Remove the old tar, leave the gzip file -->
|
||||
<delete file="${dir.release}/${tar}" quiet="true" />
|
||||
</target>
|
||||
|
||||
<!-- Create a Mac OS X application bundle. This will not work on
|
||||
a non-MacOS X platform. -->
|
||||
<target name="release-macosx" depends="release">
|
||||
<jarbundler dir="${dir.release}"
|
||||
name="Automata ${version}"
|
||||
jars="${dir.bin}/${jar}"
|
||||
mainclass="com.loomcom.automata.Launcher"
|
||||
icon="${dir.resources}/automata.icns"
|
||||
version="${version}"
|
||||
infostring="Automata ${version}"
|
||||
jvmversion="1.4+"
|
||||
smalltabs="true"
|
||||
antialiasedgraphics="false"
|
||||
antialiasedtext="false"
|
||||
liveresize="false"
|
||||
growboxintrudes="false"
|
||||
screenmenu="true" />
|
||||
</target>
|
||||
|
||||
<target name="clean">
|
||||
<delete includeEmptyDirs="true" quiet="true">
|
||||
<fileset dir="${basedir}" includes="**/.DS_Store"/>
|
||||
<fileset dir="${dir.doc}" includes="**" />
|
||||
<fileset dir="${dir.release}" includes="**" />
|
||||
<fileset dir="${dir.classes}" includes="**" />
|
||||
<fileset dir="${dir.bin}" includes="*.jar" />
|
||||
</delete>
|
||||
</target>
|
||||
|
||||
<target name="build-all" depends="jar,javadoc">
|
||||
</target>
|
||||
|
||||
</project>
|
Binary file not shown.
|
@ -0,0 +1,163 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<!-- For all the properties we could use during packaging, take a look at
|
||||
https://docs.oracle.com/javase/9/tools/javapackager.htm#GUID-E51F9601-E121-4A50-BCA7-C7F8730078B2__OSXAPPLICATIONBUNDLERARGUMENTS-26C98D56
|
||||
since the javafx-maven-plugin winds up calling through to the javapackager tool. -->
|
||||
|
||||
<groupId>com.loomcom</groupId>
|
||||
<artifactId>Automata</artifactId>
|
||||
<version>1.2.5</version>
|
||||
<description>Cellular automata explorer</description>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<organization>
|
||||
<name>loomcom</name>
|
||||
<url>https://loomcom.com/</url>
|
||||
</organization>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<name>Seth Morabito</name>
|
||||
<email>web@loomcom.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<contributors>
|
||||
<contributor>
|
||||
<name>Stephen Beitzel</name>
|
||||
<email>sbeitzel@pobox.com</email>
|
||||
<organization>QBCPS</organization>
|
||||
</contributor>
|
||||
</contributors>
|
||||
|
||||
<properties>
|
||||
<javafx-maven-plugin.version>8.8.3</javafx-maven-plugin.version>
|
||||
<jfx.bundler>dmg</jfx.bundler>
|
||||
<jfxapp.mac.bundleIdentifier>com.loomcom.automata</jfxapp.mac.bundleIdentifier>
|
||||
<jfxapp.mac.signing-key /> <!-- This should be defined in settings.xml on the build machine. It's the Apple Developer signing key ID. -->
|
||||
<jfxapp.name>Automata</jfxapp.name>
|
||||
<logback.version>1.2.3</logback.version>
|
||||
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
|
||||
<maven-enforcer-plugin.version>3.0.0-M1</maven-enforcer-plugin.version>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven.version.required>3.0</maven.version.required>
|
||||
<native-release-version>1.2.5</native-release-version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.bundle.mainclass>com.loomcom.automata.Launcher</project.bundle.mainclass>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<slf4j.version>1.7.25</slf4j.version>
|
||||
<versions-maven-plugin.version>2.4</versions-maven-plugin.version>
|
||||
</properties>
|
||||
|
||||
<prerequisites>
|
||||
<maven>${maven.version.required}</maven>
|
||||
</prerequisites>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${jfxapp.name}</finalName>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<version>${maven-enforcer-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>enforce-maven</id>
|
||||
<goals>
|
||||
<goal>enforce</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<rules>
|
||||
<requireMavenVersion>
|
||||
<version>${maven.version.required}</version>
|
||||
</requireMavenVersion>
|
||||
</rules>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>versions-maven-plugin</artifactId>
|
||||
<version>${versions-maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>display-dependency-updates</goal>
|
||||
<goal>display-plugin-updates</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.zenjava</groupId>
|
||||
<artifactId>javafx-maven-plugin</artifactId>
|
||||
<version>${javafx-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<verbose>false</verbose>
|
||||
<mainClass>${project.bundle.mainclass}</mainClass>
|
||||
<identifier>${jfxapp.name}</identifier>
|
||||
<skipJNLP>true</skipJNLP>
|
||||
<nativeReleaseVersion>${native-release-version}</nativeReleaseVersion>
|
||||
<bundleArguments>
|
||||
<systemwide>true</systemwide>
|
||||
<mac.CFBundleName>${jfxapp.name}</mac.CFBundleName>
|
||||
<mac.CFBundleIdentifier>${jfxapp.mac.bundleIdentifier}</mac.CFBundleIdentifier>
|
||||
<mac.dmg.simple>false</mac.dmg.simple>
|
||||
<mac.signing-key-developer-id-app>${jfxapp.mac.signing-key}</mac.signing-key-developer-id-app>
|
||||
</bundleArguments>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<!-- required before build-native -->
|
||||
<id>create-jfxjar</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>build-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>create-native</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>build-native</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -1,110 +0,0 @@
|
|||
/*
|
||||
* Automata, a Cellular Automata explorer.
|
||||
*
|
||||
* Copyright (c) 2003, Seth J. Morabito <sethm@loomcom.com> All rights reserved.
|
||||
*
|
||||
* 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 2 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, write to the Free Software Foundation, Inc., 59 Temple
|
||||
* Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
package com.loomcom.automata;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* Information about the application. A standard "About" box, to stroke ego.
|
||||
*
|
||||
* @author Seth Morabito
|
||||
* @version $Id: AboutDialog.java,v 1.11 2003/10/04 00:37:59 sethm Exp $
|
||||
*/
|
||||
public class AboutDialog extends JFrame {
|
||||
|
||||
private static String dateString = "October 3, 2003";
|
||||
private static String versionString = "1.2.4";
|
||||
|
||||
/**
|
||||
* Construct an About Box frame.
|
||||
*/
|
||||
public AboutDialog() {
|
||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
|
||||
JPanel infoPanel = new JPanel();
|
||||
infoPanel.setBorder(BorderFactory.createEmptyBorder(18,18,18,18));
|
||||
infoPanel.setLayout(new BoxLayout(infoPanel, BoxLayout.Y_AXIS));
|
||||
|
||||
Font largeFont = new Font("SansSerif", Font.BOLD, 18);
|
||||
Font midFont = new Font("SansSerif", Font.BOLD, 14);
|
||||
Font textFont = new Font("SansSerif", Font.PLAIN, 14);
|
||||
Font smallFont = new Font("SansSerif", Font.PLAIN, 10);
|
||||
|
||||
JLabel nameLabel = new JLabel("Cellular Automata Explorer");
|
||||
nameLabel.setFont(largeFont);
|
||||
nameLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
JLabel authorLabel = new JLabel("Seth Morabito");
|
||||
authorLabel.setFont(midFont);
|
||||
authorLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
JLabel companyLabel = new JLabel("Loom Communications");
|
||||
companyLabel.setFont(textFont);
|
||||
companyLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
JLabel urlLabel = new JLabel("http://www.loomcom.com/");
|
||||
urlLabel.setFont(textFont);
|
||||
urlLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
JLabel versionLabel = new JLabel("Version " + versionString);
|
||||
versionLabel.setFont(smallFont);
|
||||
versionLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
JLabel dateLabel = new JLabel(dateString);
|
||||
dateLabel.setFont(smallFont);
|
||||
dateLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
JTextArea license =
|
||||
new JTextArea("Distributed under the terms of the GNU\n" +
|
||||
"Public License. For details, see the file\n" +
|
||||
"LICENSE.TXT, or http://www.gnu.org/licenses/gpl.html");
|
||||
license.setFont(smallFont);
|
||||
license.setEditable(false);
|
||||
license.setBackground(getBackground());
|
||||
license.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
|
||||
|
||||
infoPanel.add(nameLabel);
|
||||
infoPanel.add(Box.createRigidArea(new Dimension(0,10)));
|
||||
infoPanel.add(authorLabel);
|
||||
infoPanel.add(companyLabel);
|
||||
infoPanel.add(urlLabel);
|
||||
infoPanel.add(versionLabel);
|
||||
infoPanel.add(dateLabel);
|
||||
infoPanel.add(Box.createRigidArea(new Dimension(0,10)));
|
||||
infoPanel.add(license);
|
||||
|
||||
JButton okButton = new JButton("OK");
|
||||
okButton.setSelected(true);
|
||||
|
||||
okButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
dispose();
|
||||
}
|
||||
});
|
||||
|
||||
JPanel buttonBar = new JPanel();
|
||||
buttonBar.setLayout(new FlowLayout(FlowLayout.CENTER));
|
||||
|
||||
buttonBar.add(okButton);
|
||||
|
||||
getContentPane().add(infoPanel, BorderLayout.CENTER);
|
||||
getContentPane().add(buttonBar, BorderLayout.SOUTH);
|
||||
|
||||
setResizable(false);
|
||||
}
|
||||
}
|
|
@ -1,788 +0,0 @@
|
|||
/*
|
||||
* Automata, a Cellular Automata explorer.
|
||||
*
|
||||
* Copyright (c) 2003, Seth J. Morabito <sethm@loomcom.com> All rights reserved.
|
||||
*
|
||||
* 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 2 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, write to the Free Software Foundation, Inc., 59 Temple
|
||||
* Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
package com.loomcom.automata;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* The main Cellular Automata application window.
|
||||
*
|
||||
* @author Seth Morabito
|
||||
* @version $Id: AutomataFrame.java,v 1.14 2003/10/13 04:00:23 sethm Exp $
|
||||
*/
|
||||
public class AutomataFrame extends JFrame implements Observer {
|
||||
|
||||
// Constants
|
||||
private static final String FRAME_TITLE = "Automata";
|
||||
|
||||
// Pseudo-Constants defined during initialization
|
||||
private String DEFAULT_SPEED;
|
||||
private JRadioButtonMenuItem DEFAULT_RULESET;
|
||||
private JRadioButtonMenuItem DEFAULT_SHAPE;
|
||||
|
||||
// The size of our cells, in pixels
|
||||
int mCellSize;
|
||||
|
||||
// Thread which will update the Cell Panel
|
||||
private UpdateThread mUpdateThread;
|
||||
|
||||
// The shape we want to draw, or null for no shape.
|
||||
private boolean[][] mDrawGlider;
|
||||
|
||||
// Cell panel mouse and mousemotion listener
|
||||
private CellSelectListener mCellSelectListener;
|
||||
|
||||
// The cell data model
|
||||
private CellModel mCellModel;
|
||||
|
||||
// Components
|
||||
private CellPanel mCellPanel;
|
||||
private ColorChooserDialog mColorChooser;
|
||||
private AboutDialog mAboutDialog;
|
||||
private JLabel mGenerationCountLabel;
|
||||
private JButton mStepButton;
|
||||
private JToggleButton mStartStopButton;
|
||||
private JComboBox mSpeedSelector;
|
||||
|
||||
// The menu bar and associated menu items
|
||||
private JMenuBar mMenuBar;
|
||||
|
||||
private JMenu mFileMenu;
|
||||
private JMenuItem mAboutMenuItem;
|
||||
private JMenuItem mNewAutomataMenuItem;
|
||||
private JMenuItem mQuitMenuItem;
|
||||
|
||||
private JMenu mDrawMenu;
|
||||
private JCheckBoxMenuItem mShowOutlinesMenuItem;
|
||||
private JMenuItem mEditColorsMenuItem;
|
||||
private JCheckBoxMenuItem mShowAgingMenuItem;
|
||||
private JMenuItem mResetMenuItem;
|
||||
private JMenu mGliderSubMenu;
|
||||
private ButtonGroup mGliderButtonGroup;
|
||||
|
||||
private JMenu mRulesMenu;
|
||||
private ButtonGroup mRulesButtonGroup;
|
||||
|
||||
// Containers associated with the menu items
|
||||
private TreeMap mGliderMap;
|
||||
private TreeMap mRulesMap;
|
||||
|
||||
// UI layout containers
|
||||
private JPanel mCellPanelContainer;
|
||||
private JPanel mButtonContainer;
|
||||
private JPanel mUIButtons;
|
||||
private JPanel mStatusContainer;
|
||||
|
||||
/**
|
||||
* Construct a new frame to hold our automata simulation.
|
||||
* Just a pass-through to setupUI() at the moment.
|
||||
*/
|
||||
public AutomataFrame() {
|
||||
setupUI();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new cell panel to the main app window.
|
||||
*/
|
||||
public void addCells(int cols, int rows, int cellSize) {
|
||||
mCellSize = cellSize;
|
||||
|
||||
mCellModel = new CellModel(cols, rows);
|
||||
mCellModel.addObserver(this);
|
||||
|
||||
// Remove any existing cell panel
|
||||
if (mCellPanel != null) {
|
||||
mCellPanelContainer.remove(mCellPanel);
|
||||
}
|
||||
|
||||
mCellPanel = new CellPanel(mCellModel, cellSize);
|
||||
|
||||
mCellSelectListener = new CellSelectListener();
|
||||
|
||||
mCellPanel.addMouseListener(mCellSelectListener);
|
||||
mCellPanel.addMouseMotionListener(mCellSelectListener);
|
||||
|
||||
mCellPanelContainer.add(mCellPanel);
|
||||
|
||||
mUpdateThread = new UpdateThread(mCellModel);
|
||||
|
||||
// set initial conditions
|
||||
setDefaults();
|
||||
|
||||
// Register with the color chooser
|
||||
if (mColorChooser != null) {
|
||||
mColorChooser.setCellPanel(mCellPanel);
|
||||
}
|
||||
|
||||
// Start the thread which updates the cell model.
|
||||
mUpdateThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the current cell panel from the main app window.
|
||||
*/
|
||||
public void removeCells() {
|
||||
mCellSize = 0;
|
||||
|
||||
if (mUpdateThread != null) {
|
||||
mUpdateThread.doStop();
|
||||
}
|
||||
|
||||
if (mCellPanelContainer != null) {
|
||||
mCellPanelContainer.remove(mCellPanel);
|
||||
}
|
||||
|
||||
if (mCellPanel != null) {
|
||||
mCellPanel.removeMouseListener(mCellSelectListener);
|
||||
mCellPanel.removeMouseMotionListener(mCellSelectListener);
|
||||
}
|
||||
|
||||
mCellModel = null;
|
||||
mCellSelectListener = null;
|
||||
mCellPanel = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Each time a new cell panel is added, reset the UI
|
||||
* widgets to default values.
|
||||
*/
|
||||
private void setDefaults() {
|
||||
// Make sure the Start/Stop button is reset
|
||||
stopCellUpdates();
|
||||
|
||||
if (mCellSize > 3) {
|
||||
mShowOutlinesMenuItem.setSelected(true);
|
||||
mCellPanel.showCellOutlines(true);
|
||||
} else {
|
||||
mShowOutlinesMenuItem.setSelected(false);
|
||||
mShowOutlinesMenuItem.setEnabled(false);
|
||||
mCellPanel.showCellOutlines(false);
|
||||
}
|
||||
|
||||
mSpeedSelector.setSelectedItem(DEFAULT_SPEED);
|
||||
|
||||
// Programatically select the default ruleset
|
||||
DEFAULT_RULESET.setSelected(true);
|
||||
DEFAULT_RULESET.doClick();
|
||||
|
||||
// Programatically select the default ruleset
|
||||
DEFAULT_SHAPE.setSelected(true);
|
||||
DEFAULT_SHAPE.doClick();
|
||||
|
||||
mGenerationCountLabel.setText("0");
|
||||
|
||||
mShowAgingMenuItem.setSelected(true);
|
||||
mCellPanel.setCellAging(true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of the Observer interface.
|
||||
*/
|
||||
public void update(Observable t, Object o) {
|
||||
mGenerationCountLabel.setText(
|
||||
Integer.toString(mCellModel.getGeneration()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to cleanly shut down the application in a polite way.
|
||||
*/
|
||||
private void shutdown() {
|
||||
if (mUpdateThread != null) {
|
||||
try {
|
||||
// Offer a short window to politely join with
|
||||
// the thread before shutting down, good manners
|
||||
mUpdateThread.doStop();
|
||||
mUpdateThread.join(500);
|
||||
} catch (InterruptedException ex) {
|
||||
;
|
||||
}
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lay out the UI.
|
||||
*/
|
||||
private void setupUI() {
|
||||
setTitle(FRAME_TITLE);
|
||||
|
||||
// Components
|
||||
JComboBox speedComboBox;
|
||||
JCheckBox cellOutlineSelector;
|
||||
JLabel cellTypeLabel;
|
||||
JLabel speedLabel;
|
||||
|
||||
// The main menu bar.
|
||||
mMenuBar = new JMenuBar();
|
||||
|
||||
int menuKeyMask =
|
||||
java.awt.Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
|
||||
|
||||
/*
|
||||
* The "File" Menu
|
||||
*/
|
||||
mFileMenu = new JMenu("File");
|
||||
mFileMenu.setMnemonic(KeyEvent.VK_F);
|
||||
|
||||
mAboutMenuItem = new JMenuItem("About");
|
||||
mAboutMenuItem.setMnemonic(KeyEvent.VK_A);
|
||||
mAboutMenuItem.addActionListener(new AboutListener());
|
||||
|
||||
mNewAutomataMenuItem = new JMenuItem("New Automata...");
|
||||
mNewAutomataMenuItem.setMnemonic(KeyEvent.VK_N);
|
||||
mNewAutomataMenuItem.addActionListener(new NewAutomataListener());
|
||||
|
||||
mQuitMenuItem = new JMenuItem("Quit", KeyEvent.VK_Q);
|
||||
mQuitMenuItem.setAccelerator(KeyStroke.getKeyStroke(
|
||||
KeyEvent.VK_Q, menuKeyMask));
|
||||
|
||||
mQuitMenuItem.addActionListener(new QuitListener());
|
||||
|
||||
// Assemble the menu
|
||||
mFileMenu.add(mAboutMenuItem);
|
||||
mFileMenu.add(mNewAutomataMenuItem);
|
||||
mFileMenu.add(mQuitMenuItem);
|
||||
|
||||
/*
|
||||
* The "Draw" Menu
|
||||
*/
|
||||
mDrawMenu = new JMenu("Draw");
|
||||
mDrawMenu.setMnemonic(KeyEvent.VK_D);
|
||||
|
||||
// Show or hide cell outlines
|
||||
mShowOutlinesMenuItem = new JCheckBoxMenuItem("Show Cell Outlines");
|
||||
mShowOutlinesMenuItem.setMnemonic(KeyEvent.VK_O);
|
||||
mShowOutlinesMenuItem.addItemListener(new ShowOutlinesListener());
|
||||
|
||||
// Enable or disable the showing of cell "age"
|
||||
mShowAgingMenuItem = new JCheckBoxMenuItem("Show Cell Aging");
|
||||
mShowAgingMenuItem.setMnemonic(KeyEvent.VK_A);
|
||||
mShowAgingMenuItem.addItemListener(new ShowAgingListener());
|
||||
|
||||
// Set custom colors...
|
||||
mEditColorsMenuItem = new JMenuItem("Edit Colors...");
|
||||
mEditColorsMenuItem.setMnemonic(KeyEvent.VK_C);
|
||||
mEditColorsMenuItem.addActionListener(new EditColorsListener());
|
||||
|
||||
// Clear the panel and reset...
|
||||
mResetMenuItem = new JMenuItem("Clear Display");
|
||||
mResetMenuItem.setMnemonic(KeyEvent.VK_L);
|
||||
mResetMenuItem.addActionListener(new ResetPanelListener());
|
||||
|
||||
mGliderSubMenu = new JMenu("Draw Glider");
|
||||
|
||||
// A set of shapes we know how to draw...
|
||||
mGliderButtonGroup = new ButtonGroup();
|
||||
|
||||
mGliderMap = setupGliders();
|
||||
|
||||
final String noGlider = "None";
|
||||
|
||||
ShapeMenuListener shapeMenuListener = new ShapeMenuListener(noGlider);
|
||||
|
||||
JRadioButtonMenuItem noGliderItem = new JRadioButtonMenuItem(noGlider);
|
||||
|
||||
DEFAULT_SHAPE = noGliderItem;
|
||||
|
||||
noGliderItem.addActionListener(shapeMenuListener);
|
||||
mGliderButtonGroup.add(noGliderItem);
|
||||
mGliderSubMenu.add(noGliderItem);
|
||||
|
||||
for (Iterator i = mGliderMap.keySet().iterator(); i.hasNext(); ) {
|
||||
String s = (String) i.next();
|
||||
|
||||
JRadioButtonMenuItem item = new JRadioButtonMenuItem(s);
|
||||
item.addActionListener(shapeMenuListener);
|
||||
mGliderButtonGroup.add(item);
|
||||
mGliderSubMenu.add(item);
|
||||
}
|
||||
|
||||
// Populate the "Draw" menu
|
||||
mDrawMenu.add(mShowOutlinesMenuItem);
|
||||
mDrawMenu.add(mShowAgingMenuItem);
|
||||
mDrawMenu.addSeparator();
|
||||
mDrawMenu.add(mEditColorsMenuItem);
|
||||
mDrawMenu.addSeparator();
|
||||
mDrawMenu.add(mResetMenuItem);
|
||||
mDrawMenu.addSeparator();
|
||||
mDrawMenu.add(mGliderSubMenu);
|
||||
|
||||
/*
|
||||
* The "Rules" Menu
|
||||
*/
|
||||
mRulesMap = setupRuleSets();
|
||||
|
||||
mRulesMenu = new JMenu("Rules");
|
||||
mRulesButtonGroup = new ButtonGroup();
|
||||
|
||||
// Handle menu events from the Rules menu
|
||||
RulesMenuListener rulesMenuListener = new RulesMenuListener();
|
||||
|
||||
// Populate the "Rules" menu
|
||||
boolean first = true;
|
||||
for (Iterator i = mRulesMap.keySet().iterator(); i.hasNext();) {
|
||||
String s = (String) i.next();
|
||||
|
||||
JRadioButtonMenuItem item = new JRadioButtonMenuItem(s);
|
||||
item.addActionListener(rulesMenuListener);
|
||||
|
||||
// We should automatically choose a rule set to start with.
|
||||
// It should be either the first rule set on the menu, or "Life",
|
||||
// if it is found.
|
||||
if (first || "Life".equals(((RuleSet)mRulesMap.get(s)).getShortName())) {
|
||||
DEFAULT_RULESET = item;
|
||||
first = false;
|
||||
}
|
||||
|
||||
mRulesButtonGroup.add(item);
|
||||
mRulesMenu.add(item);
|
||||
}
|
||||
|
||||
// Assembling the menu bar
|
||||
mMenuBar.add(mFileMenu);
|
||||
mMenuBar.add(mDrawMenu);
|
||||
mMenuBar.add(mRulesMenu);
|
||||
|
||||
this.setJMenuBar(mMenuBar);
|
||||
|
||||
// The "Start/Pause" button
|
||||
mStartStopButton = new JToggleButton("Start", false);
|
||||
mStartStopButton.addItemListener(new StartButtonListener());
|
||||
|
||||
// Combo Box that determines the generation update speed
|
||||
mSpeedSelector = new JComboBox();
|
||||
mSpeedSelector.addItem("1");
|
||||
mSpeedSelector.addItem("2");
|
||||
mSpeedSelector.addItem("5");
|
||||
mSpeedSelector.addItem("10");
|
||||
mSpeedSelector.addItem("20");
|
||||
|
||||
// Set the default speed to be selected when creating a new
|
||||
// automata cell panel.
|
||||
DEFAULT_SPEED = "5";
|
||||
|
||||
mSpeedSelector.addActionListener(new SpeedSelectorListener());
|
||||
|
||||
// "Step" button
|
||||
mStepButton = new JButton("Step");
|
||||
mStepButton.addActionListener(new StepButtonListener());
|
||||
|
||||
// Container for the Cell Panel
|
||||
mCellPanelContainer = new JPanel();
|
||||
mCellPanelContainer.setLayout(new FlowLayout(FlowLayout.CENTER));
|
||||
// mCellPanelContainer.add(mCellPanel);
|
||||
|
||||
// Container for UI buttons and status labels
|
||||
mButtonContainer = new JPanel();
|
||||
|
||||
mUIButtons = new JPanel();
|
||||
|
||||
GridBagLayout gridBag = new GridBagLayout();
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
mUIButtons.setLayout(gridBag);
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
speedLabel = new JLabel("Generations per second");
|
||||
c.gridx = 0;
|
||||
c.gridy = 0;
|
||||
gridBag.setConstraints(speedLabel, c);
|
||||
mUIButtons.add(speedLabel);
|
||||
|
||||
c.gridx = 0;
|
||||
c.gridy = 1;
|
||||
gridBag.setConstraints(mSpeedSelector, c);
|
||||
mUIButtons.add(mSpeedSelector);
|
||||
|
||||
c.gridx = 1;
|
||||
c.gridy = 1;
|
||||
gridBag.setConstraints(mStartStopButton, c);
|
||||
mUIButtons.add(mStartStopButton);
|
||||
|
||||
c.gridx = 2;
|
||||
c.gridy = 1;
|
||||
gridBag.setConstraints(mStepButton, c);
|
||||
mUIButtons.add(mStepButton);
|
||||
|
||||
// Status label
|
||||
mStatusContainer = new JPanel();
|
||||
|
||||
mGenerationCountLabel = new JLabel("0", JLabel.CENTER);
|
||||
mGenerationCountLabel.setForeground(Color.red);
|
||||
|
||||
mStatusContainer.setLayout(new BorderLayout());
|
||||
mStatusContainer.add(new JLabel("Generations"), BorderLayout.NORTH);
|
||||
mStatusContainer.add(mGenerationCountLabel, BorderLayout.SOUTH);
|
||||
|
||||
mButtonContainer.add(mUIButtons, BorderLayout.WEST);
|
||||
mButtonContainer.add(mStatusContainer, BorderLayout.EAST);
|
||||
|
||||
getContentPane().add(mCellPanelContainer, BorderLayout.CENTER);
|
||||
getContentPane().add(mButtonContainer, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause refreshing the cell generations.
|
||||
*/
|
||||
private void stopCellUpdates() {
|
||||
if (mUpdateThread != null) {
|
||||
mStepButton.setEnabled(true);
|
||||
mStepButton.doClick();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update one cell generation, then stop.
|
||||
*/
|
||||
private void stepCellUpdates() {
|
||||
if (mCellPanel != null && mUpdateThread.isPaused()) {
|
||||
mCellModel.transform();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of built-in shapes we can draw. Most of these are
|
||||
* gliders in the Life rule set, as well as some others.
|
||||
*
|
||||
* @return A map of pattern names and arrays
|
||||
*/
|
||||
private TreeMap setupGliders() {
|
||||
|
||||
TreeMap gliderMap = new TreeMap();
|
||||
|
||||
// "The Glider"
|
||||
gliderMap.put("The Glider", new boolean[][] {
|
||||
{true, false, false}, // Row 1
|
||||
{true, false, true}, // Row 2
|
||||
{true, true, false} // Row 3
|
||||
});
|
||||
|
||||
// A "Fountain" glider
|
||||
gliderMap.put("Fountain", new boolean[][] {
|
||||
{true, true, true, false, false, false},
|
||||
{true, false, false, false, true, true},
|
||||
{false, true, true, true, true, true},
|
||||
{false, false, false, false, false, false},
|
||||
{false, true, true, true, true, true},
|
||||
{true, false, false, false, true, true},
|
||||
{true, true, true, false, false, false}
|
||||
});
|
||||
|
||||
// "The Coe Ship" glider
|
||||
gliderMap.put("The Coe Ship", new boolean[][] {
|
||||
{false, true, true, false, false, false, false, false, false, false},
|
||||
{true, true, false, true, true, false, false, false, false, false},
|
||||
{false, true, true, true, true, false, false, false, false, false},
|
||||
{false, false, true, true, false, false, false, false, false, false},
|
||||
{false, false, false, true, false, false, false, false, false, false},
|
||||
{false, true, false, false, false, true, false, false, false, false},
|
||||
{true, false, false, false, false, false, true, false, true, true},
|
||||
{true, false, false, false, false, false, true, true, false, false},
|
||||
{true, true, true, true, true, true, false, false, false, false},
|
||||
});
|
||||
|
||||
// "Lightweight Spaceship" glider
|
||||
gliderMap.put("Lightweight Spaceship", new boolean[][] {
|
||||
{false, true, true, true},
|
||||
{true, false, false, true},
|
||||
{false, false, false, true},
|
||||
{false, false, false, true},
|
||||
{true, false, true, false}
|
||||
});
|
||||
|
||||
// More, as time permits...
|
||||
return gliderMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the list of rule sets.
|
||||
*
|
||||
* NOTE: To add new rule sets to the application, list 'em here!
|
||||
* Future versions will allow creation of new rule sets on the fly at
|
||||
* through a nifty dialog.
|
||||
*/
|
||||
private TreeMap setupRuleSets() {
|
||||
|
||||
LinkedList rules = new LinkedList();
|
||||
TreeMap rulesMap = new TreeMap();
|
||||
|
||||
// Start with the classic.
|
||||
rules.add(new RuleSet("Life", new int[]{3}, new int[]{2,3}));
|
||||
|
||||
rules.add(new RuleSet("Amoeba", new int[]{3,5,7}, new int[]{1,3,5,8}));
|
||||
rules.add(new RuleSet("Assimilation", new int[]{3,4,5},
|
||||
new int[]{4,5,6,7}));
|
||||
rules.add(new RuleSet("Bacteria", new int[]{3,4}, new int[]{4,5,6}));
|
||||
rules.add(new RuleSet("Blinkers", new int[]{3,4,5}, new int[]{2}));
|
||||
rules.add(new RuleSet("Blossom", new int[]{2,3}, new int[]{2,3}));
|
||||
rules.add(new RuleSet("Bugs", new int[]{3,5,6,7},
|
||||
new int[]{1,5,6,7,8}));
|
||||
rules.add(new RuleSet("Coagulations", new int[]{3,7,8},
|
||||
new int[]{2,3,5,6,7,8}));
|
||||
rules.add(new RuleSet("Coral", new int[]{3},
|
||||
new int[]{4,5,6,7,8}));
|
||||
rules.add(new RuleSet("Day & Night", new int[]{3,6,7,8},
|
||||
new int[]{3,4,6,7,8}));
|
||||
rules.add(new RuleSet("Diamoeba", new int[]{3,5,6,7,8},
|
||||
new int[]{5,6,7,8}));
|
||||
rules.add(new RuleSet("Gnarl", new int[]{1}, new int[]{1}));
|
||||
rules.add(new RuleSet("H-trees", new int[]{1},
|
||||
new int[]{0,1,2,3,4,5,6,7,8}));
|
||||
rules.add(new RuleSet("HighLife", new int[]{3,6}, new int[]{2,3}));
|
||||
rules.add(new RuleSet("Holstein", new int[]{3,5,6,7,8},
|
||||
new int[]{4,6,7,8}));
|
||||
rules.add(new RuleSet("Iceballs", new int[]{2,5,6,7,8},
|
||||
new int[]{5,6,7,8}));
|
||||
rules.add(new RuleSet("Land Rush", new int[]{3,6},
|
||||
new int[]{2,3,4,5,7,8}));
|
||||
rules.add(new RuleSet("Life Without Death", new int[]{3},
|
||||
new int[]{0,1,2,3,4,5,6,7,8}));
|
||||
rules.add(new RuleSet("LongLife", new int[]{3,4,5}, new int[]{5}));
|
||||
rules.add(new RuleSet("Majority", new int[]{4,5,6,7,8},
|
||||
new int[]{5,6,7,8}));
|
||||
rules.add(new RuleSet("Maze", new int[]{3}, new int[]{1,2,3,4,5}));
|
||||
rules.add(new RuleSet("Move", new int[]{3,6,8}, new int[]{2,4,5}));
|
||||
rules.add(new RuleSet("Pseudo Life", new int[]{3,5,7},
|
||||
new int[]{2,3,8}));
|
||||
rules.add(new RuleSet("Replicator", new int[]{1,3,5,7},
|
||||
new int[]{1,3,5,7}));
|
||||
rules.add(new RuleSet("Seeds", new int[]{2},
|
||||
new int[]{}));
|
||||
rules.add(new RuleSet("Serviettes", new int[]{2,3,4},
|
||||
new int[]{}));
|
||||
|
||||
// Rulesets without nicknames
|
||||
rules.add(new RuleSet(new int[]{2,4,8}, new int[]{2,4,8}));
|
||||
rules.add(new RuleSet(new int[]{3,4,5}, new int[]{2,4,5}));
|
||||
rules.add(new RuleSet(new int[]{3,4}, new int[]{2,5}));
|
||||
rules.add(new RuleSet(new int[]{3,5}, new int[]{2,4}));
|
||||
|
||||
// "Probabilistic" rulesets
|
||||
rules.add(new ChanceRuleSet("Randomized Life", new int[]{3},
|
||||
new int[]{2,3},
|
||||
99.0, 99.0));
|
||||
|
||||
for (Iterator i = rules.iterator(); i.hasNext(); ) {
|
||||
RuleSet r = (RuleSet) i.next();
|
||||
rulesMap.put(r.getName(), r);
|
||||
}
|
||||
|
||||
return rulesMap;
|
||||
}
|
||||
|
||||
/*
|
||||
* Event Listener Implementations
|
||||
*/
|
||||
private final class StartButtonListener implements ItemListener {
|
||||
public void itemStateChanged(ItemEvent evt) {
|
||||
JToggleButton b = (JToggleButton) evt.getSource();
|
||||
if (mUpdateThread != null) {
|
||||
if (evt.getStateChange() == ItemEvent.SELECTED) {
|
||||
mStartStopButton.setText("Pause");
|
||||
mStepButton.setEnabled(false);
|
||||
mUpdateThread.go();
|
||||
} else {
|
||||
mStartStopButton.setText("Start");
|
||||
mStepButton.setEnabled(true);
|
||||
mUpdateThread.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class StepButtonListener implements ActionListener {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
stepCellUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
private final class SpeedSelectorListener implements ActionListener {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
JComboBox source = (JComboBox) evt.getSource();
|
||||
String s = (String) source.getSelectedItem();
|
||||
int gps = Integer.valueOf(s).intValue();
|
||||
if (gps > 0 && gps < 1000) {
|
||||
mUpdateThread.setSleepInterval(1000 / gps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class RulesMenuListener implements ActionListener {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
String s = evt.getActionCommand();
|
||||
RuleSet rs = (RuleSet) mRulesMap.get(s);
|
||||
mCellModel.setRuleSet(rs);
|
||||
}
|
||||
}
|
||||
|
||||
private final class ShapeMenuListener implements ActionListener {
|
||||
private final String noGlider;
|
||||
private ShapeMenuListener(String noGlider) {
|
||||
super();
|
||||
this.noGlider = noGlider;
|
||||
}
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
String s = evt.getActionCommand();
|
||||
// Our special case is a single cell. If we're just
|
||||
// drawing single cells, there's no need to set any
|
||||
// glider shape.
|
||||
if (noGlider.equals(s)) {
|
||||
mDrawGlider = null;
|
||||
return;
|
||||
}
|
||||
|
||||
mDrawGlider = (boolean[][]) mGliderMap.get(s);
|
||||
}
|
||||
}
|
||||
|
||||
private final class ResetPanelListener implements ActionListener {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
stopCellUpdates();
|
||||
mCellModel.reset();
|
||||
}
|
||||
}
|
||||
|
||||
private final class EditColorsListener implements ActionListener {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
if (mColorChooser == null) {
|
||||
mColorChooser = new ColorChooserDialog(mCellPanel);
|
||||
Utilities.centerWindow(mColorChooser);
|
||||
}
|
||||
mColorChooser.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
private final class ShowAgingListener implements ItemListener {
|
||||
public void itemStateChanged(ItemEvent evt) {
|
||||
if (evt.getStateChange() == ItemEvent.SELECTED) {
|
||||
mCellPanel.setCellAging(true);
|
||||
} else {
|
||||
mCellPanel.setCellAging(false);
|
||||
}
|
||||
mCellPanel.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private final class ShowOutlinesListener implements ItemListener {
|
||||
public void itemStateChanged(ItemEvent evt) {
|
||||
if (evt.getStateChange() == ItemEvent.SELECTED) {
|
||||
mCellPanel.showCellOutlines(true);
|
||||
} else {
|
||||
mCellPanel.showCellOutlines(false);
|
||||
}
|
||||
mCellPanel.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private final class QuitListener implements ActionListener {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private final class NewAutomataListener implements ActionListener {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
// Hide ourselves...
|
||||
setVisible(false);
|
||||
|
||||
// Remove our cells.
|
||||
removeCells();
|
||||
|
||||
// Show the launcher.
|
||||
Launcher launcher = Launcher.getLauncher();
|
||||
launcher.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
private final class AboutListener implements ActionListener {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
if (mAboutDialog == null) {
|
||||
mAboutDialog = new AboutDialog();
|
||||
mAboutDialog.pack();
|
||||
}
|
||||
Utilities.centerWindow(mAboutDialog);
|
||||
mAboutDialog.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for mouse events in the Cell Panel.
|
||||
*/
|
||||
private final class CellSelectListener
|
||||
implements MouseListener, MouseMotionListener {
|
||||
int mLastCellX = 0;
|
||||
int mLastCellY = 0;
|
||||
|
||||
// MouseListener implementations
|
||||
|
||||
/*
|
||||
*Toggle the clicked-on cell
|
||||
*/
|
||||
public void mouseClicked(MouseEvent evt) {
|
||||
CellPanel source = (CellPanel)evt.getSource();
|
||||
int x = evt.getX();
|
||||
int y = evt.getY();
|
||||
int cellX = x / mCellSize;
|
||||
int cellY = y / mCellSize;
|
||||
|
||||
if (mDrawGlider != null) {
|
||||
mCellModel.drawShape(cellX, cellY, mDrawGlider);
|
||||
} else {
|
||||
mCellModel.flipCell(cellX, cellY);
|
||||
}
|
||||
}
|
||||
public void mouseEntered(MouseEvent evt) {
|
||||
}
|
||||
public void mouseExited(MouseEvent evt) {
|
||||
}
|
||||
public void mousePressed(MouseEvent evt) {
|
||||
}
|
||||
public void mouseReleased(MouseEvent evt) {
|
||||
}
|
||||
|
||||
// MouseMotionListener implementations
|
||||
|
||||
/*
|
||||
* Allow primitive pattern drawing while dragging the mouse.
|
||||
*/
|
||||
public void mouseDragged(MouseEvent evt) {
|
||||
int x = evt.getX();
|
||||
int y = evt.getY();
|
||||
int cellX = x / mCellSize;
|
||||
int cellY = y / mCellSize;
|
||||
boolean drawOrErase =
|
||||
!((evt.getModifiers()
|
||||
& (InputEvent.SHIFT_DOWN_MASK | InputEvent.SHIFT_MASK))
|
||||
!= 0);
|
||||
if (cellX != mLastCellX || cellY != mLastCellY) {
|
||||
mCellModel.setCell(cellX, cellY, drawOrErase);
|
||||
|
||||
mLastCellX = cellX;
|
||||
mLastCellY = cellY;
|
||||
}
|
||||
}
|
||||
public void mouseMoved(MouseEvent evt) {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,204 +0,0 @@
|
|||
/*
|
||||
* Automata, a Cellular Automata explorer.
|
||||
*
|
||||
* Copyright (c) 2003, Seth J. Morabito <sethm@loomcom.com> All rights reserved.
|
||||
*
|
||||
* 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 2 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, write to the Free Software Foundation, Inc., 59 Temple
|
||||
* Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
package com.loomcom.automata;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.*;
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* The visual representation of a two dimensional field of cells. Cells which
|
||||
* are alive are represented by a colored rectangle on a background field.
|
||||
*
|
||||
* @author Seth Morabito
|
||||
* @version $Id: CellPanel.java,v 1.10 2003/10/03 23:41:03 sethm Exp $
|
||||
*/
|
||||
public class CellPanel extends JComponent implements Observer {
|
||||
private int mCols;
|
||||
private int mRows;
|
||||
private int mCellSize;
|
||||
|
||||
private Color mOutlineColor; // Outline color
|
||||
|
||||
private CellModel mCellModel;
|
||||
|
||||
private boolean mShowCellOutlines = true; // Show outlines by default
|
||||
private boolean mShowAging;
|
||||
|
||||
/**
|
||||
* Construct a new CellPanel.
|
||||
*
|
||||
* @param model The cell model for this panel.
|
||||
* @param cellSize The size of the cells, in pixels.
|
||||
*/
|
||||
public CellPanel(CellModel model, int cellSize) {
|
||||
setDoubleBuffered(true);
|
||||
mCellModel = model;
|
||||
model.addObserver(this);
|
||||
mCols = model.getCols();
|
||||
mRows = model.getRows();
|
||||
mCellSize = cellSize;
|
||||
|
||||
// Default colors
|
||||
setForeground(Color.black);
|
||||
setBackground(Color.white);
|
||||
setOutline(Color.lightGray);
|
||||
|
||||
setSize(mCellSize * mCols, mCellSize * mRows);
|
||||
setPreferredSize(new Dimension(mCellSize * mCols, mCellSize * mRows));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cell outline color.
|
||||
*
|
||||
* @param c The color to use when drawing the cell outlines.
|
||||
*/
|
||||
public void setOutline(Color c) {
|
||||
mOutlineColor = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the panel whether to draw cell outlines or not.
|
||||
*
|
||||
* @param b True to show outlines, false otherwise.
|
||||
*/
|
||||
public void showCellOutlines(boolean b) {
|
||||
this.mShowCellOutlines = b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to remove the default update() behavior,
|
||||
* which clears the component on each paint call.
|
||||
*/
|
||||
public void update(Graphics g) {
|
||||
return; // do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the Observer interface.
|
||||
*/
|
||||
public void update(Observable t, Object o) {
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a lighter version of this color. Not to be confused
|
||||
* with the Color.brighen() method! This method will return a
|
||||
* color with a higher alpha value based on the percent parameter.
|
||||
*/
|
||||
private Color lightenColor(Color c, int age) {
|
||||
int red = c.getRed();
|
||||
int green = c.getGreen();
|
||||
int blue = c.getBlue();
|
||||
int alpha = c.getAlpha();
|
||||
|
||||
double p = 1.0 - (0.05 * age);
|
||||
|
||||
if (p > 0.20) {
|
||||
alpha = (int)(p * alpha);
|
||||
} else {
|
||||
alpha = (int)(0.20 * alpha);
|
||||
}
|
||||
|
||||
return new Color(red, green, blue, alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the cell field.
|
||||
*/
|
||||
public void paint(Graphics g) {
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
|
||||
int width = getSize().width;
|
||||
int height = getSize().height;
|
||||
|
||||
// Clear the panel.
|
||||
g2d.setPaint(getBackground());
|
||||
g2d.fill(new Rectangle(0, 0, width, height));
|
||||
|
||||
g2d.setPaint(getForeground());
|
||||
for (int i = 0; i < mCols; i++) {
|
||||
for (int j = 0; j < mRows; j++) {
|
||||
if (mCellModel.getCell(i, j)) {
|
||||
// If cell aging is enabled, draw aged cells
|
||||
int age = 0;
|
||||
if (mShowAging) {
|
||||
age = mCellModel.getCellAge(i, j);
|
||||
g2d.setPaint(lightenColor(getForeground(), age));
|
||||
}
|
||||
g2d.fillRect(i * mCellSize,
|
||||
j * mCellSize,
|
||||
mCellSize,
|
||||
mCellSize);
|
||||
if (mShowAging) {
|
||||
g2d.setPaint(getForeground());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show outlines if desired
|
||||
if (mShowCellOutlines) {
|
||||
drawCellOutlines(g2d);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the color used to draw the cell outlines.
|
||||
*
|
||||
* @return The color used to draw the cell outlines.
|
||||
*/
|
||||
public Color getOutline() {
|
||||
return mOutlineColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the display of cell "aging"
|
||||
*/
|
||||
public void setCellAging(boolean b) {
|
||||
mShowAging = b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a grid showing the outlines of the cells.
|
||||
*/
|
||||
private void drawCellOutlines(Graphics2D g2d) {
|
||||
g2d.setPaint(mOutlineColor);
|
||||
g2d.setStroke(new BasicStroke(1.0f));
|
||||
|
||||
int width = getSize().width;
|
||||
int height = getSize().height;
|
||||
|
||||
// Draw rectangle outlining the entire component
|
||||
g2d.draw(new Rectangle(0, 0, width - 1, height - 1));
|
||||
|
||||
// Draw the vertical lines
|
||||
for (int i = 0; i < mCols; i++) {
|
||||
g2d.drawLine(i * mCellSize, 0, i * mCellSize, height);
|
||||
}
|
||||
|
||||
// Draw the horizontal lines
|
||||
for (int j = 0; j < mRows; j++) {
|
||||
g2d.drawLine(0, j * mCellSize, width, j * mCellSize);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,201 +0,0 @@
|
|||
/*
|
||||
* Automata, a Cellular Automata explorer.
|
||||
*
|
||||
* Copyright (c) 2003, Seth J. Morabito <sethm@loomcom.com> All rights reserved.
|
||||
*
|
||||
* 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 2 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, write to the Free Software Foundation, Inc., 59 Temple
|
||||
* Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
package com.loomcom.automata;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
|
||||
/**
|
||||
* Color chooser dialog. This dialog allows the user to set colors for the
|
||||
* cells, background, and outlines.
|
||||
*
|
||||
* @author Seth Morabito
|
||||
* @version $Id: ColorChooserDialog.java,v 1.6 2003/08/23 21:09:15 sethm Exp $
|
||||
*/
|
||||
public class ColorChooserDialog extends JDialog {
|
||||
private CellPanel mCellPanel;
|
||||
private JColorChooser mColorChooser;
|
||||
private JComboBox mElementBox;
|
||||
|
||||
// The cell panel we actually update.
|
||||
private CellPanel mPreviewCells;
|
||||
|
||||
private Color mCellColor;
|
||||
private Color mBackgroundColor;
|
||||
private Color mOutlineColor;
|
||||
|
||||
boolean mSetCellColor;
|
||||
boolean mSetBackgroundColor;
|
||||
boolean mSetOutlineColor;
|
||||
|
||||
private static final String CELLS_STRING = "Cells";
|
||||
private static final String BG_STRING = "Background";
|
||||
private static final String OUTLINE_STRING = "Outline";
|
||||
|
||||
/**
|
||||
* Construct a color chooser dialog.
|
||||
*
|
||||
* @param p The cell panel to set colors for.
|
||||
*/
|
||||
public ColorChooserDialog(CellPanel p) {
|
||||
mCellPanel = p;
|
||||
|
||||
JPanel bannerPanel = new JPanel();
|
||||
bannerPanel.setLayout(new FlowLayout());
|
||||
bannerPanel.setBorder(BorderFactory.
|
||||
createTitledBorder("Set color for"));
|
||||
|
||||
mElementBox = new JComboBox();
|
||||
mElementBox.addItem(CELLS_STRING);
|
||||
mElementBox.addItem(BG_STRING);
|
||||
mElementBox.addItem(OUTLINE_STRING);
|
||||
|
||||
mElementBox.addItemListener(new SelectElementListener());
|
||||
|
||||
bannerPanel.add(mElementBox);
|
||||
|
||||
mColorChooser = new JColorChooser();
|
||||
|
||||
// Set the preview panel.
|
||||
// Cheat! Since we already have this component handy, make a
|
||||
// small 3x3 one with 20px cells as our preview panel.
|
||||
JPanel previewPanel = new JPanel();
|
||||
|
||||
previewPanel.setLayout(new FlowLayout());
|
||||
|
||||
CellModel model = new CellModel(3,3);
|
||||
mPreviewCells = new CellPanel(model, 20);
|
||||
model.flipCell(1, 1);
|
||||
|
||||
previewPanel.add(mPreviewCells);
|
||||
|
||||
// Some VMs seem to require an explicit size setting here in
|
||||
// order for the preview panel component to be displayed
|
||||
// properly.
|
||||
previewPanel.setSize(mPreviewCells.getSize());
|
||||
previewPanel.setBorder(BorderFactory.createEmptyBorder(0,0,1,0));
|
||||
|
||||
mColorChooser.setPreviewPanel(previewPanel);
|
||||
|
||||
mColorChooser.getSelectionModel().addChangeListener(
|
||||
new PreviewListener());
|
||||
|
||||
// Dialog OK/Cancel buttons
|
||||
JPanel buttonBar = new JPanel();
|
||||
buttonBar.setLayout(new FlowLayout(FlowLayout.RIGHT));
|
||||
|
||||
JButton okButton = new JButton("OK");
|
||||
JButton cancelButton = new JButton("Cancel");
|
||||
|
||||
okButton.addActionListener(new SetColorListener());
|
||||
|
||||
cancelButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
setVisible(false);
|
||||
}
|
||||
});
|
||||
|
||||
buttonBar.add(okButton);
|
||||
buttonBar.add(cancelButton);
|
||||
|
||||
getContentPane().add(bannerPanel, BorderLayout.NORTH);
|
||||
getContentPane().add(mColorChooser, BorderLayout.CENTER);
|
||||
getContentPane().add(buttonBar, BorderLayout.SOUTH);
|
||||
|
||||
pack();
|
||||
setResizable(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Target a new cell panel.
|
||||
*/
|
||||
public void setCellPanel(CellPanel p) {
|
||||
mCellPanel = p;
|
||||
p.setForeground(mCellColor);
|
||||
p.setBackground(mBackgroundColor);
|
||||
p.setOutline(mOutlineColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the new colors to the CellPanel we're updating, then hide.
|
||||
*/
|
||||
final private class SetColorListener implements ActionListener {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
if (mSetCellColor) {
|
||||
mCellPanel.setForeground(mCellColor);
|
||||
}
|
||||
if (mSetBackgroundColor) {
|
||||
mCellPanel.setBackground(mBackgroundColor);
|
||||
}
|
||||
if (mSetOutlineColor) {
|
||||
mCellPanel.setOutline(mOutlineColor);
|
||||
}
|
||||
mCellPanel.repaint();
|
||||
// Hide, but stick around.
|
||||
setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the user selects a new element to change the color for,
|
||||
* update the color chooser's model to reflect the color that's
|
||||
* already there.
|
||||
*/
|
||||
final private class SelectElementListener implements ItemListener {
|
||||
public void itemStateChanged(ItemEvent evt) {
|
||||
if (mElementBox.getSelectedItem().equals(CELLS_STRING)) {
|
||||
mColorChooser.setColor(mPreviewCells.getForeground());
|
||||
} else if (mElementBox.getSelectedItem().equals(BG_STRING)) {
|
||||
mColorChooser.setColor(mPreviewCells.getBackground());
|
||||
} else if (mElementBox.getSelectedItem().equals(OUTLINE_STRING)) {
|
||||
mColorChooser.setColor(mPreviewCells.getOutline());
|
||||
}
|
||||
|
||||
mPreviewCells.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the user selects a new color, update the preview panel
|
||||
* and instance colors appropriately.
|
||||
*/
|
||||
final private class PreviewListener implements ChangeListener {
|
||||
public void stateChanged(ChangeEvent evt) {
|
||||
if (mElementBox.getSelectedItem().equals(CELLS_STRING)) {
|
||||
mCellColor = mColorChooser.getColor();
|
||||
mSetCellColor = true;
|
||||
mPreviewCells.setForeground(mCellColor);
|
||||
} else if (mElementBox.getSelectedItem().equals(BG_STRING)) {
|
||||
mBackgroundColor = mColorChooser.getColor();
|
||||
mSetBackgroundColor = true;
|
||||
mPreviewCells.setBackground(mBackgroundColor);
|
||||
} else if (mElementBox.getSelectedItem().equals(OUTLINE_STRING)) {
|
||||
mOutlineColor = mColorChooser.getColor();
|
||||
mSetOutlineColor = true;
|
||||
mPreviewCells.setOutline(mOutlineColor);
|
||||
}
|
||||
|
||||
mPreviewCells.repaint();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,292 +0,0 @@
|
|||
/*
|
||||
* Automata, a Cellular Automata explorer.
|
||||
*
|
||||
* Copyright (c) 2003, Seth J. Morabito <sethm@loomcom.com> All rights reserved.
|
||||
*
|
||||
* 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 2 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, write to the Free Software Foundation, Inc., 59 Temple
|
||||
* Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
package com.loomcom.automata;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* Application setup and launch dialog.
|
||||
*
|
||||
* @author Seth Morabito
|
||||
* @version $Id: Launcher.java,v 1.7 2003/10/13 04:00:24 sethm Exp $
|
||||
*/
|
||||
public class Launcher extends JFrame {
|
||||
|
||||
// Cell panel size, and default values
|
||||
private int mCellSize = 10; // Size of cells in pixels
|
||||
private int mRows = 64; // Rows
|
||||
private int mCols = 64; // Columns
|
||||
|
||||
private AutomataFrame mAutomataFrame; // Main window.
|
||||
|
||||
private Dimension mScreenSize;
|
||||
|
||||
// Containers
|
||||
private JPanel mIntroPanel;
|
||||
private JPanel mInputPanel;
|
||||
private JPanel mButtonPanel;
|
||||
|
||||
// Input
|
||||
private JTextField mColsField;
|
||||
private JTextField mRowsField;
|
||||
private JTextField mCellSizeField;
|
||||
|
||||
// Labels
|
||||
private JLabel mIntroLabel;
|
||||
private JLabel mColsLabel;
|
||||
private JLabel mRowsLabel;
|
||||
private JLabel mCellSizeLabel;
|
||||
private JLabel mPixelLabel;
|
||||
|
||||
// Buttons
|
||||
private JButton mOKButton;
|
||||
private JButton mCancelButton;
|
||||
|
||||
// Static reference to ourself
|
||||
private static Launcher mLauncher;
|
||||
|
||||
private static final String FRAME_TITLE = "Setup";
|
||||
|
||||
/**
|
||||
* Construct a default application launcher.
|
||||
*/
|
||||
public Launcher() {
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setupUI();
|
||||
pack();
|
||||
setResizable(false);
|
||||
Utilities.centerWindow(this);
|
||||
}
|
||||
|
||||
public static Launcher getLauncher() {
|
||||
if (mLauncher == null) {
|
||||
mLauncher = new Launcher();
|
||||
}
|
||||
|
||||
return mLauncher;
|
||||
}
|
||||
/**
|
||||
* Lay out the visual look.
|
||||
*/
|
||||
private void setupUI() {
|
||||
setTitle(FRAME_TITLE);
|
||||
|
||||
// Set the system look and feel
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (Exception e) {
|
||||
; // Swallow this exception, because there's really no other
|
||||
// useful action to take here.
|
||||
}
|
||||
|
||||
mScreenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
|
||||
mIntroPanel = new JPanel();
|
||||
mInputPanel = new JPanel();
|
||||
mButtonPanel = new JPanel();
|
||||
|
||||
mButtonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
|
||||
|
||||
Font largeFont = new Font("SansSerif", Font.BOLD, 16);
|
||||
Font smallFont = new Font("SansSerif", Font.PLAIN, 10);
|
||||
|
||||
mIntroLabel = new JLabel("Create a New Cellular Automata");
|
||||
mIntroLabel.setFont(largeFont);
|
||||
|
||||
mIntroPanel.add(mIntroLabel);
|
||||
|
||||
mCellSizeLabel = new JLabel("Cell size");
|
||||
mPixelLabel = new JLabel("(in pixels)");
|
||||
mPixelLabel.setFont(smallFont);
|
||||
|
||||
mColsLabel = new JLabel("Columns");
|
||||
mRowsLabel = new JLabel("Rows");
|
||||
|
||||
mColsField = new JTextField(Integer.toString(mCols));
|
||||
mRowsField = new JTextField(Integer.toString(mRows));
|
||||
mCellSizeField = new JTextField(Integer.toString(mCellSize));
|
||||
|
||||
mOKButton = new JButton("OK");
|
||||
mOKButton.addActionListener(new CreateCellPanelListener());
|
||||
|
||||
mCancelButton = new JButton("Cancel");
|
||||
mCancelButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
System.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
mColsField.setColumns(4);
|
||||
mRowsField.setColumns(4);
|
||||
mCellSizeField.setColumns(2);
|
||||
|
||||
GridBagLayout gb = new GridBagLayout();
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
|
||||
c.insets = new Insets(5,5,5,5);
|
||||
|
||||
mInputPanel.setLayout(gb);
|
||||
|
||||
c.gridx = 0;
|
||||
c.gridy = 0;
|
||||
c.weightx = 0.0;
|
||||
c.gridwidth = 1;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.EAST;
|
||||
gb.setConstraints(mColsLabel, c);
|
||||
mInputPanel.add(mColsLabel);
|
||||
|
||||
c.gridx = 1;
|
||||
c.gridy = 0;
|
||||
c.weightx = 0.0;
|
||||
c.gridwidth = 2;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.WEST;
|
||||
gb.setConstraints(mColsField, c);
|
||||
mInputPanel.add(mColsField);
|
||||
|
||||
c.gridx = 0;
|
||||
c.gridy = 1;
|
||||
c.weightx = 0.0;
|
||||
c.gridwidth = 1;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.EAST;
|
||||
gb.setConstraints(mRowsLabel, c);
|
||||
mInputPanel.add(mRowsLabel);
|
||||
|
||||
c.gridx = 1;
|
||||
c.gridy = 1;
|
||||
c.weightx = 0.0;
|
||||
c.gridwidth = 2;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.WEST;
|
||||
gb.setConstraints(mRowsField, c);
|
||||
mInputPanel.add(mRowsField);
|
||||
|
||||
c.gridx = 0;
|
||||
c.gridy = 2;
|
||||
c.weightx = 0.0;
|
||||
c.gridwidth = 1;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.EAST;
|
||||
gb.setConstraints(mCellSizeLabel, c);
|
||||
mInputPanel.add(mCellSizeLabel);
|
||||
|
||||
c.gridx = 1;
|
||||
c.gridy = 2;
|
||||
c.weightx = 0.0;
|
||||
c.gridwidth = 1;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.WEST;
|
||||
gb.setConstraints(mCellSizeField, c);
|
||||
mInputPanel.add(mCellSizeField);
|
||||
|
||||
c.gridx = 2;
|
||||
c.gridy = 2;
|
||||
c.weightx = 0.0;
|
||||
c.gridwidth = 1;
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.anchor = GridBagConstraints.WEST;
|
||||
gb.setConstraints(mPixelLabel, c);
|
||||
mInputPanel.add(mPixelLabel);
|
||||
|
||||
mButtonPanel.add(mOKButton);
|
||||
mButtonPanel.add(mCancelButton);
|
||||
|
||||
getContentPane().add(mIntroPanel, BorderLayout.NORTH);
|
||||
getContentPane().add(mInputPanel, BorderLayout.CENTER);
|
||||
getContentPane().add(mButtonPanel, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
private int getCols() {
|
||||
try {
|
||||
return Integer.parseInt(mColsField.getText());
|
||||
} catch (NumberFormatException ex) {
|
||||
return mCols;
|
||||
}
|
||||
}
|
||||
|
||||
private int getRows() {
|
||||
try {
|
||||
return Integer.parseInt(mRowsField.getText());
|
||||
} catch (NumberFormatException ex) {
|
||||
return mRows;
|
||||
}
|
||||
}
|
||||
|
||||
private int getCellSize() {
|
||||
try {
|
||||
return Integer.parseInt(mCellSizeField.getText());
|
||||
} catch (NumberFormatException ex) {
|
||||
return mCellSize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main application entry point.
|
||||
*
|
||||
* @param args Application arguments (not used).
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
Launcher launcher = Launcher.getLauncher();
|
||||
launcher.setVisible(true);
|
||||
}
|
||||
|
||||
private final class CreateCellPanelListener implements ActionListener {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
int cols = getCols();
|
||||
int rows = getRows();
|
||||
int cellSize = getCellSize();
|
||||
|
||||
if (cellSize < 0 || cols < 0 || rows < 0) {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"Please use positive numbers.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cellSize * cols > mScreenSize.width ||
|
||||
cellSize * rows > mScreenSize.height) {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
"The values you've chosen would create\n" +
|
||||
"a window too large to fit on the screen.\n" +
|
||||
"Please choose smaller values.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAutomataFrame == null) {
|
||||
mAutomataFrame = new AutomataFrame();
|
||||
}
|
||||
|
||||
mAutomataFrame.addCells(cols, rows, cellSize);
|
||||
|
||||
mAutomataFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
mAutomataFrame.pack();
|
||||
mAutomataFrame.setResizable(false);
|
||||
Utilities.centerWindow(mAutomataFrame);
|
||||
mAutomataFrame.setVisible(true);
|
||||
|
||||
// hide ourselves
|
||||
setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Automata, a Cellular Automata explorer.
|
||||
*
|
||||
* Copyright (c) 2003, Seth J. Morabito <sethm@loomcom.com> All rights reserved.
|
||||
*
|
||||
* 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 2 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, write to the Free Software Foundation, Inc., 59 Temple
|
||||
* Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
package com.loomcom.automata;
|
||||
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.control.ButtonBar;
|
||||
import javafx.scene.control.ButtonType;
|
||||
import javafx.scene.control.Dialog;
|
||||
import javafx.scene.control.DialogPane;
|
||||
import javafx.scene.web.WebView;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Information about the application. A standard "About" box, to stroke ego.
|
||||
*
|
||||
* @author Seth Morabito
|
||||
* @version $Id: AboutDialog.java,v 1.11 2003/10/04 00:37:59 sethm Exp $
|
||||
*/
|
||||
public class AboutDialog {
|
||||
private static final Logger __l = LoggerFactory.getLogger(AboutDialog.class);
|
||||
|
||||
@FXML public WebView _webView;
|
||||
|
||||
public static void display() {
|
||||
try {
|
||||
URL layout = Thread.currentThread().getContextClassLoader()
|
||||
.getResource("AboutDialog.fxml");
|
||||
FXMLLoader loader = new FXMLLoader();
|
||||
loader.setLocation(layout);
|
||||
DialogPane content = loader.load();
|
||||
|
||||
ButtonType ok = new ButtonType(UIStrings.getString(UIStrings.BUTTON_OK), ButtonBar.ButtonData.OK_DONE);
|
||||
content.getButtonTypes().add(ok);
|
||||
|
||||
Dialog<ButtonType> theDialog = new Dialog<>();
|
||||
theDialog.setTitle(UIStrings.getString(UIStrings.DIALOG_ABOUT_TITLE));
|
||||
theDialog.setDialogPane(content);
|
||||
theDialog.showAndWait();
|
||||
} catch (Exception e) {
|
||||
__l.error("Unrecoverable exception while showing the About dialog", e);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
try {
|
||||
URL aboutURL = Thread.currentThread().getContextClassLoader().getResource("AboutPage.html");
|
||||
assert aboutURL != null;
|
||||
_webView.getEngine().load(aboutURL.toString());
|
||||
} catch (Exception e) {
|
||||
__l.error("Exception loading the about page into the dialog", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* Automata, a Cellular Automata explorer.
|
||||
*
|
||||
* Copyright (c) 2003, Seth J. Morabito <sethm@loomcom.com> All rights reserved.
|
||||
*
|
||||
* 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 2 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, write to the Free Software Foundation, Inc., 59 Temple
|
||||
* Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
package com.loomcom.automata;
|
||||
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.canvas.Canvas;
|
||||
import javafx.scene.canvas.GraphicsContext;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
/**
|
||||
* The visual representation of a two dimensional field of cells. Cells which
|
||||
* are alive are represented by a colored rectangle on a background field.
|
||||
*
|
||||
* @author Seth Morabito
|
||||
* @version $Id: CellPanel.java,v 1.10 2003/10/03 23:41:03 sethm Exp $
|
||||
*/
|
||||
public class CellPanel extends Canvas implements Observer {
|
||||
private int mCols;
|
||||
private int mRows;
|
||||
private int mCellSize;
|
||||
|
||||
private Color mOutlineColor; // Outline color
|
||||
private Color mBackground;
|
||||
private Color mForeground;
|
||||
|
||||
private CellModel mCellModel;
|
||||
|
||||
private boolean mShowCellOutlines = true; // Show outlines by default
|
||||
private boolean mShowAging = false;
|
||||
|
||||
public CellPanel() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the cell field with a model. This method will cause the CellPanel
|
||||
* to resize itself.
|
||||
*
|
||||
* @param model the model to use
|
||||
* @param cellSize the size of a single cell
|
||||
*/
|
||||
public void setModel(CellModel model, int cellSize) {
|
||||
if (mCellModel != null) {
|
||||
mCellModel.deleteObserver(this);
|
||||
}
|
||||
mCellModel = model;
|
||||
model.addObserver(this);
|
||||
mCols = model.getCols();
|
||||
mRows = model.getRows();
|
||||
mCellSize = cellSize;
|
||||
|
||||
// Default colors
|
||||
mBackground = Color.WHITE;
|
||||
mForeground = Color.BLACK;
|
||||
setOutline(Color.LIGHTGRAY);
|
||||
|
||||
setWidth(mCellSize * mCols);
|
||||
setHeight(mCellSize * mRows);
|
||||
}
|
||||
|
||||
public CellModel getModel() {
|
||||
return mCellModel;
|
||||
}
|
||||
|
||||
public int getCellSize() {
|
||||
return mCellSize;
|
||||
}
|
||||
|
||||
public Color getBackground() {
|
||||
return mBackground;
|
||||
}
|
||||
|
||||
public void setBackground(Color c) {
|
||||
mBackground = c;
|
||||
schedulePaint();
|
||||
}
|
||||
|
||||
public Color getForeground() {
|
||||
return mForeground;
|
||||
}
|
||||
|
||||
public void setForeground(Color c) {
|
||||
mForeground = c;
|
||||
schedulePaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cell outline color.
|
||||
*
|
||||
* @param c The color to use when drawing the cell outlines.
|
||||
*/
|
||||
public void setOutline(Color c) {
|
||||
mOutlineColor = c;
|
||||
schedulePaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the panel whether to draw cell outlines or not.
|
||||
*
|
||||
* @param b True to show outlines, false otherwise.
|
||||
*/
|
||||
public void showCellOutlines(boolean b) {
|
||||
this.mShowCellOutlines = b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the Observer interface.
|
||||
*/
|
||||
@Override
|
||||
public void update(Observable t, Object o) {
|
||||
schedulePaint();
|
||||
}
|
||||
|
||||
public void schedulePaint() {
|
||||
// cause the cell field to be redrawn.
|
||||
if (Platform.isFxApplicationThread()) {
|
||||
paint();
|
||||
} else {
|
||||
Platform.runLater(this::paint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a lighter version of this color. Not to be confused
|
||||
* with the Color.brighen() method! This method will return a
|
||||
* color with a higher alpha value based on the percent parameter.
|
||||
*/
|
||||
private Color lightenColor(Color c, int age) {
|
||||
double red = c.getRed();
|
||||
double green = c.getGreen();
|
||||
double blue = c.getBlue();
|
||||
double alpha = c.getOpacity();
|
||||
|
||||
double p = 1.0 - (0.05 * age);
|
||||
|
||||
if (p > 0.20) {
|
||||
alpha = (p * alpha);
|
||||
} else {
|
||||
alpha = (0.20 * alpha);
|
||||
}
|
||||
|
||||
return new Color(red, green, blue, alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the cell field. This should only be called on the FX thread
|
||||
*/
|
||||
private void paint() {
|
||||
if (Platform.isFxApplicationThread()) {
|
||||
GraphicsContext g2d = getGraphicsContext2D();
|
||||
|
||||
double width = getWidth();
|
||||
double height = getHeight();
|
||||
|
||||
// Clear the panel.
|
||||
g2d.setFill(getBackground());
|
||||
g2d.fillRect(0, 0, width, height);
|
||||
|
||||
g2d.setFill(getForeground());
|
||||
for (int i = 0; i < mCols; i++) {
|
||||
for (int j = 0; j < mRows; j++) {
|
||||
if (mCellModel.getCell(i, j)) {
|
||||
// If cell aging is enabled, draw aged cells
|
||||
int age = 0;
|
||||
if (mShowAging) {
|
||||
age = mCellModel.getCellAge(i, j);
|
||||
g2d.setFill(lightenColor(getForeground(), age));
|
||||
}
|
||||
g2d.fillRect(i * mCellSize,
|
||||
j * mCellSize,
|
||||
mCellSize,
|
||||
mCellSize);
|
||||
if (mShowAging) {
|
||||
g2d.setFill(getForeground());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show outlines if desired
|
||||
if (mShowCellOutlines) {
|
||||
drawCellOutlines(g2d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the color used to draw the cell outlines.
|
||||
*
|
||||
* @return The color used to draw the cell outlines.
|
||||
*/
|
||||
public Color getOutline() {
|
||||
return mOutlineColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the display of cell "aging"
|
||||
*/
|
||||
public void setCellAging(boolean b) {
|
||||
mShowAging = b;
|
||||
schedulePaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a grid showing the outlines of the cells.
|
||||
*/
|
||||
private void drawCellOutlines(GraphicsContext g2d) {
|
||||
g2d.setStroke(mOutlineColor);
|
||||
g2d.setLineWidth(1);
|
||||
|
||||
double width = getWidth();
|
||||
double height = getHeight();
|
||||
|
||||
// Draw rectangle outlining the entire component
|
||||
g2d.strokeRect(0, 0, width - 1, height - 1);
|
||||
|
||||
// Draw the vertical lines
|
||||
for (int i = 0; i < mCols; i++) {
|
||||
g2d.strokeLine(i * mCellSize, 0, i * mCellSize, height);
|
||||
}
|
||||
|
||||
// Draw the horizontal lines
|
||||
for (int j = 0; j < mRows; j++) {
|
||||
g2d.strokeLine(0, j * mCellSize, width, j * mCellSize);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package com.loomcom.automata;
|
||||
/*
|
||||
* Copyright 7/12/18 by Stephen Beitzel
|
||||
*/
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.control.ButtonBar;
|
||||
import javafx.scene.control.ButtonType;
|
||||
import javafx.scene.control.ColorPicker;
|
||||
import javafx.scene.control.Dialog;
|
||||
import javafx.scene.control.DialogPane;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* FX version of the color chooser dialog
|
||||
*
|
||||
* @author Stephen Beitzel <sbeitzel@pobox.com>
|
||||
*/
|
||||
public class ColorDialog {
|
||||
private static final Logger __l = LoggerFactory.getLogger(ColorDialog.class);
|
||||
|
||||
@FXML public Label _foregroundLabel;
|
||||
@FXML public ColorPicker _foreground;
|
||||
@FXML public Label _backgroundLabel;
|
||||
@FXML public ColorPicker _background;
|
||||
@FXML public Label _outlineLabel;
|
||||
@FXML public ColorPicker _outlinePicker;
|
||||
@FXML public Label _previewLabel;
|
||||
@FXML public CellPanel _preview;
|
||||
|
||||
public static void display(Color foreground, Color background, Color outline,
|
||||
Consumer<Color> fgConsumer, Consumer<Color> bgConsumer, Consumer<Color> olConsumer) {
|
||||
try {
|
||||
URL layout = Thread.currentThread().getContextClassLoader()
|
||||
.getResource("ColorDialog.fxml");
|
||||
FXMLLoader loader = new FXMLLoader();
|
||||
loader.setLocation(layout);
|
||||
DialogPane content = loader.load();
|
||||
ColorDialog cd = loader.getController();
|
||||
cd._preview.setForeground(foreground);
|
||||
cd._preview.setBackground(background);
|
||||
cd._preview.setOutline(outline);
|
||||
cd._preview.getModel().flipCell(1, 1);
|
||||
|
||||
ButtonType ok = new ButtonType(UIStrings.getString(UIStrings.BUTTON_OK), ButtonBar.ButtonData.OK_DONE);
|
||||
ButtonType cancel = new ButtonType(UIStrings.getString(UIStrings.BUTTON_CANCEL), ButtonBar.ButtonData.CANCEL_CLOSE);
|
||||
content.getButtonTypes().addAll(ok, cancel);
|
||||
|
||||
Dialog<ButtonType> theDialog = new Dialog<>();
|
||||
theDialog.setTitle(UIStrings.getString(UIStrings.DIALOG_COLOR_TITLE));
|
||||
theDialog.setDialogPane(content);
|
||||
Optional<ButtonType> response = theDialog.showAndWait();
|
||||
if (response.isPresent() && response.get().getButtonData() == ButtonType.OK.getButtonData()) {
|
||||
fgConsumer.accept(cd._foreground.getValue());
|
||||
bgConsumer.accept(cd._background.getValue());
|
||||
olConsumer.accept(cd._outlinePicker.getValue());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
__l.error("Exception displaying the color chooser dialog", e);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
_foregroundLabel.setText(UIStrings.getString(UIStrings.LABEL_FOREGROUND));
|
||||
_backgroundLabel.setText(UIStrings.getString(UIStrings.LABEL_BACKGROUND));
|
||||
_outlineLabel.setText(UIStrings.getString(UIStrings.LABEL_OUTLINE));
|
||||
_preview.setModel(new CellModel(3, 3), 20);
|
||||
}
|
||||
|
||||
@FXML
|
||||
@SuppressWarnings("unused")
|
||||
public void onForeground(ActionEvent evt) {
|
||||
_preview.setForeground(_foreground.getValue());
|
||||
}
|
||||
|
||||
@FXML
|
||||
@SuppressWarnings("unused")
|
||||
public void onBackground(ActionEvent evt) {
|
||||
_preview.setBackground(_background.getValue());
|
||||
}
|
||||
|
||||
@FXML
|
||||
@SuppressWarnings("unused")
|
||||
public void onOutline(ActionEvent evt) {
|
||||
_preview.setOutline(_outlinePicker.getValue());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Automata, a Cellular Automata explorer.
|
||||
*
|
||||
* Copyright (c) 2003, Seth J. Morabito <sethm@loomcom.com> All rights reserved.
|
||||
*
|
||||
* 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 2 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, write to the Free Software Foundation, Inc., 59 Temple
|
||||
* Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
package com.loomcom.automata;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
/**
|
||||
* Application setup and launch dialog.
|
||||
*
|
||||
* @author Seth Morabito
|
||||
* @version $Id: Launcher.java,v 1.7 2003/10/13 04:00:24 sethm Exp $
|
||||
*/
|
||||
public class Launcher extends Application {
|
||||
@Override
|
||||
public void start(Stage primaryStage) {
|
||||
UIStrings.init(Locale.getDefault());
|
||||
// display the setup dialog
|
||||
Setup.display(primaryStage);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package com.loomcom.automata;
|
||||
/*
|
||||
* Copyright 7/10/18 by Stephen Beitzel
|
||||
*/
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.geometry.Rectangle2D;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Screen;
|
||||
import javafx.stage.Stage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Controller for the setup dialog.
|
||||
*
|
||||
* @author Stephen Beitzel <sbeitzel@pobox.com>
|
||||
*/
|
||||
public class Setup {
|
||||
private static final Logger __l = LoggerFactory.getLogger(Setup.class);
|
||||
|
||||
@FXML public Label _createLabel;
|
||||
@FXML public Label _columnLabel;
|
||||
@FXML public Label _rowLabel;
|
||||
@FXML public Label _sizeLabel;
|
||||
@FXML public Label _inPixelsLabel;
|
||||
@FXML public Button _okButton;
|
||||
@FXML public Button _cancelButton;
|
||||
@FXML public TextField _columnField;
|
||||
@FXML public TextField _rowField;
|
||||
@FXML public TextField _sizeField;
|
||||
|
||||
private Stage _stage;
|
||||
|
||||
static void display(Stage parent) {
|
||||
try {
|
||||
Stage stage = new Stage();
|
||||
stage.initOwner(parent);
|
||||
URL layout = Thread.currentThread().getContextClassLoader()
|
||||
.getResource("Setup.fxml");
|
||||
FXMLLoader loader = new FXMLLoader();
|
||||
loader.setLocation(layout);
|
||||
Parent root = loader.load();
|
||||
Setup setup = loader.getController();
|
||||
setup._stage = stage;
|
||||
|
||||
stage.setTitle(UIStrings.getString(UIStrings.WINDOW_SETUP_TITLE));
|
||||
stage.setScene(new javafx.scene.Scene(root, 370, 190));
|
||||
stage.initModality(Modality.WINDOW_MODAL);
|
||||
stage.show();
|
||||
stage.centerOnScreen();
|
||||
} catch (Exception e) {
|
||||
// nothing to do, really. We could log an error, but there's nothing a user can do to fix it.
|
||||
__l.error("Exception displaying the setup dialog", e);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
// set label text to the localized strings
|
||||
_createLabel.setText(UIStrings.getString(UIStrings.LABEL_CREATE));
|
||||
_columnLabel.setText(UIStrings.getString(UIStrings.LABEL_COLUMNS));
|
||||
_rowLabel.setText(UIStrings.getString(UIStrings.LABEL_ROWS));
|
||||
_sizeLabel.setText(UIStrings.getString(UIStrings.LABEL_CELLSIZE));
|
||||
_inPixelsLabel.setText(UIStrings.getString(UIStrings.LABEL_INPIXELS));
|
||||
|
||||
// set control text
|
||||
_okButton.setText(UIStrings.getString(UIStrings.BUTTON_OK));
|
||||
_cancelButton.setText(UIStrings.getString(UIStrings.BUTTON_CANCEL));
|
||||
}
|
||||
|
||||
@FXML
|
||||
@SuppressWarnings("unused")
|
||||
public void onOk(ActionEvent evt) {
|
||||
// validate that we've got reasonable numbers
|
||||
Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds();
|
||||
int rows;
|
||||
int columns;
|
||||
int cellSize;
|
||||
try {
|
||||
rows = Integer.parseInt(_rowField.getText());
|
||||
columns = Integer.parseInt(_columnField.getText());
|
||||
cellSize = Integer.parseInt(_sizeField.getText());
|
||||
if (rows < 1 || columns < 1 || cellSize < 1) {
|
||||
// zero or negative dimension. Not displayable.
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setContentText(UIStrings.getString(UIStrings.ERROR_TEXT_DIMENSIONS_SMALL));
|
||||
alert.showAndWait();
|
||||
} else {
|
||||
if (cellSize * columns > primaryScreenBounds.getWidth() ||
|
||||
cellSize * rows > primaryScreenBounds.getHeight()) {
|
||||
// dimension(s) too large to fit on screen
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setContentText(UIStrings.getString(UIStrings.ERROR_TEXT_DIMENSIONS_LARGE));
|
||||
alert.showAndWait();
|
||||
} else {
|
||||
// create a new sim window
|
||||
SimWindow.display((Stage) _stage.getOwner(), columns, rows, cellSize);
|
||||
// hide the setup window
|
||||
_stage.close();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// can't convert one or more of the dimensions into an integer
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setContentText(UIStrings.getString(UIStrings.ERROR_NUMBERS_ONLY));
|
||||
alert.showAndWait();
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
@SuppressWarnings("unused")
|
||||
public void onCancel(ActionEvent evt) {
|
||||
_stage.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,438 @@
|
|||
package com.loomcom.automata;
|
||||
/*
|
||||
* Copyright 7/10/18 by Stephen Beitzel
|
||||
*/
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.CheckMenuItem;
|
||||
import javafx.scene.control.ChoiceBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.control.MenuItem;
|
||||
import javafx.scene.control.RadioMenuItem;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.stage.Stage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Controller for the automata scene
|
||||
*
|
||||
* @author Stephen Beitzel <sbeitzel@pobox.com>
|
||||
*/
|
||||
public class SimWindow implements Observer {
|
||||
private static final Logger __l = LoggerFactory.getLogger(SimWindow.class);
|
||||
|
||||
@FXML public MenuItem _newItem;
|
||||
@FXML public MenuItem _quitItem;
|
||||
@FXML public CheckMenuItem _showOutlinesItem;
|
||||
@FXML public CheckMenuItem _showAgingItem;
|
||||
@FXML public MenuItem _editColorsItem;
|
||||
@FXML public MenuItem _clearDisplayItem;
|
||||
@FXML public Menu _menuGlider;
|
||||
@FXML public Menu _ruleMenu;
|
||||
@FXML public MenuItem _aboutItem;
|
||||
@FXML public Label _generationsLabel;
|
||||
@FXML public Button _startButton;
|
||||
@FXML public Label _generationLabel;
|
||||
@FXML public CellPanel _simCanvas;
|
||||
@FXML public RadioMenuItem _noneItem;
|
||||
@FXML public ChoiceBox<Integer> _speedBox;
|
||||
public Button _stepButton;
|
||||
|
||||
private Stage _stage;
|
||||
private UpdateThread _updateThread;
|
||||
private ToggleGroup _shapeGroup;
|
||||
private ToggleGroup _ruleGroup;
|
||||
|
||||
// these members are for keeping track of drawing state during user interaction
|
||||
private boolean[][] _shapeToDraw = null;
|
||||
private int _lastCellX = -1;
|
||||
private int _lastCellY = -1;
|
||||
|
||||
static void display(Stage parent, int columns, int rows, int cellSize) {
|
||||
try {
|
||||
Stage stage = new Stage();
|
||||
stage.initOwner(parent);
|
||||
URL layout = Thread.currentThread().getContextClassLoader()
|
||||
.getResource("SimWindow.fxml");
|
||||
FXMLLoader loader = new FXMLLoader();
|
||||
loader.setLocation(layout);
|
||||
Parent root = loader.load();
|
||||
SimWindow sw = loader.getController();
|
||||
sw._stage = stage;
|
||||
sw.initCellModel(rows, columns, cellSize);
|
||||
|
||||
int canvasHeight = cellSize * columns;
|
||||
stage.setTitle(UIStrings.getString(UIStrings.WINDOW_SIM_TITLE));
|
||||
stage.setScene(new javafx.scene.Scene(root, Math.max(canvasHeight, 410), 90 + canvasHeight));
|
||||
stage.show();
|
||||
stage.centerOnScreen();
|
||||
} catch (Exception e) {
|
||||
// nothing to do, really. We could log an error, but there's nothing a user can do to fix it.
|
||||
__l.error("Exception displaying the main automata window", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of built-in shapes we can draw. Most of these are
|
||||
* gliders in the Life rule set, as well as some others.
|
||||
*
|
||||
* @return A map of pattern names and arrays
|
||||
*/
|
||||
private static TreeMap<String, boolean[][]> setupGliders() {
|
||||
|
||||
TreeMap<String, boolean[][]> gliderMap = new TreeMap<>();
|
||||
|
||||
// "The Glider"
|
||||
gliderMap.put("The Glider", new boolean[][] {
|
||||
{true, false, false}, // Row 1
|
||||
{true, false, true}, // Row 2
|
||||
{true, true, false} // Row 3
|
||||
});
|
||||
|
||||
// A "Fountain" glider
|
||||
gliderMap.put("Fountain", new boolean[][] {
|
||||
{true, true, true, false, false, false},
|
||||
{true, false, false, false, true, true},
|
||||
{false, true, true, true, true, true},
|
||||
{false, false, false, false, false, false},
|
||||
{false, true, true, true, true, true},
|
||||
{true, false, false, false, true, true},
|
||||
{true, true, true, false, false, false}
|
||||
});
|
||||
|
||||
// "The Coe Ship" glider
|
||||
gliderMap.put("The Coe Ship", new boolean[][] {
|
||||
{false, true, true, false, false, false, false, false, false, false},
|
||||
{true, true, false, true, true, false, false, false, false, false},
|
||||
{false, true, true, true, true, false, false, false, false, false},
|
||||
{false, false, true, true, false, false, false, false, false, false},
|
||||
{false, false, false, true, false, false, false, false, false, false},
|
||||
{false, true, false, false, false, true, false, false, false, false},
|
||||
{true, false, false, false, false, false, true, false, true, true},
|
||||
{true, false, false, false, false, false, true, true, false, false},
|
||||
{true, true, true, true, true, true, false, false, false, false},
|
||||
});
|
||||
|
||||
// "Lightweight Spaceship" glider
|
||||
gliderMap.put("Lightweight Spaceship", new boolean[][] {
|
||||
{false, true, true, true},
|
||||
{true, false, false, true},
|
||||
{false, false, false, true},
|
||||
{false, false, false, true},
|
||||
{true, false, true, false}
|
||||
});
|
||||
|
||||
// More, as time permits...
|
||||
return gliderMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the list of rule sets.
|
||||
*
|
||||
* NOTE: To add new rule sets to the application, list 'em here!
|
||||
* Future versions will allow creation of new rule sets on the fly at
|
||||
* through a nifty dialog.
|
||||
*/
|
||||
private static TreeMap<String, RuleSet> setupRuleSets() {
|
||||
|
||||
LinkedList<RuleSet> rules = new LinkedList<>();
|
||||
TreeMap<String, RuleSet> rulesMap = new TreeMap<>();
|
||||
|
||||
// Start with the classic.
|
||||
rules.add(new RuleSet("Life", new int[]{3}, new int[]{2,3}));
|
||||
|
||||
rules.add(new RuleSet("Amoeba", new int[]{3,5,7}, new int[]{1,3,5,8}));
|
||||
rules.add(new RuleSet("Assimilation", new int[]{3,4,5},
|
||||
new int[]{4,5,6,7}));
|
||||
rules.add(new RuleSet("Bacteria", new int[]{3,4}, new int[]{4,5,6}));
|
||||
rules.add(new RuleSet("Blinkers", new int[]{3,4,5}, new int[]{2}));
|
||||
rules.add(new RuleSet("Blossom", new int[]{2,3}, new int[]{2,3}));
|
||||
rules.add(new RuleSet("Bugs", new int[]{3,5,6,7},
|
||||
new int[]{1,5,6,7,8}));
|
||||
rules.add(new RuleSet("Coagulations", new int[]{3,7,8},
|
||||
new int[]{2,3,5,6,7,8}));
|
||||
rules.add(new RuleSet("Coral", new int[]{3},
|
||||
new int[]{4,5,6,7,8}));
|
||||
rules.add(new RuleSet("Day & Night", new int[]{3,6,7,8},
|
||||
new int[]{3,4,6,7,8}));
|
||||
rules.add(new RuleSet("Diamoeba", new int[]{3,5,6,7,8},
|
||||
new int[]{5,6,7,8}));
|
||||
rules.add(new RuleSet("Gnarl", new int[]{1}, new int[]{1}));
|
||||
rules.add(new RuleSet("H-trees", new int[]{1},
|
||||
new int[]{0,1,2,3,4,5,6,7,8}));
|
||||
rules.add(new RuleSet("HighLife", new int[]{3,6}, new int[]{2,3}));
|
||||
rules.add(new RuleSet("Holstein", new int[]{3,5,6,7,8},
|
||||
new int[]{4,6,7,8}));
|
||||
rules.add(new RuleSet("Iceballs", new int[]{2,5,6,7,8},
|
||||
new int[]{5,6,7,8}));
|
||||
rules.add(new RuleSet("Land Rush", new int[]{3,6},
|
||||
new int[]{2,3,4,5,7,8}));
|
||||
rules.add(new RuleSet("Life Without Death", new int[]{3},
|
||||
new int[]{0,1,2,3,4,5,6,7,8}));
|
||||
rules.add(new RuleSet("LongLife", new int[]{3,4,5}, new int[]{5}));
|
||||
rules.add(new RuleSet("Majority", new int[]{4,5,6,7,8},
|
||||
new int[]{5,6,7,8}));
|
||||
rules.add(new RuleSet("Maze", new int[]{3}, new int[]{1,2,3,4,5}));
|
||||
rules.add(new RuleSet("Move", new int[]{3,6,8}, new int[]{2,4,5}));
|
||||
rules.add(new RuleSet("Pseudo Life", new int[]{3,5,7},
|
||||
new int[]{2,3,8}));
|
||||
rules.add(new RuleSet("Replicator", new int[]{1,3,5,7},
|
||||
new int[]{1,3,5,7}));
|
||||
rules.add(new RuleSet("Seeds", new int[]{2},
|
||||
new int[]{}));
|
||||
rules.add(new RuleSet("Serviettes", new int[]{2,3,4},
|
||||
new int[]{}));
|
||||
|
||||
// Rulesets without nicknames
|
||||
rules.add(new RuleSet(new int[]{2,4,8}, new int[]{2,4,8}));
|
||||
rules.add(new RuleSet(new int[]{3,4,5}, new int[]{2,4,5}));
|
||||
rules.add(new RuleSet(new int[]{3,4}, new int[]{2,5}));
|
||||
rules.add(new RuleSet(new int[]{3,5}, new int[]{2,4}));
|
||||
|
||||
// "Probabilistic" rulesets
|
||||
rules.add(new ChanceRuleSet("Randomized Life", new int[]{3},
|
||||
new int[]{2,3},
|
||||
99.0, 99.0));
|
||||
|
||||
for (RuleSet r : rules) {
|
||||
rulesMap.put(r.getName(), r);
|
||||
}
|
||||
|
||||
return rulesMap;
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
// setup the shape menu
|
||||
_shapeGroup = new ToggleGroup();
|
||||
_shapeGroup.getToggles().add(_noneItem);
|
||||
_noneItem.setUserData(null);
|
||||
_noneItem.setOnAction(this::onGlider);
|
||||
Map<String, boolean[][]> shapeMap = setupGliders();
|
||||
_shapeGroup.selectToggle(_noneItem);
|
||||
for (Map.Entry<String, boolean[][]> entry : shapeMap.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
boolean[][] shapeData = entry.getValue();
|
||||
RadioMenuItem shapeItem = new RadioMenuItem(name);
|
||||
shapeItem.setToggleGroup(_shapeGroup);
|
||||
shapeItem.setUserData(shapeData);
|
||||
_menuGlider.getItems().add(shapeItem);
|
||||
shapeItem.setOnAction(this::onGlider);
|
||||
}
|
||||
|
||||
// populate the ruleset menu
|
||||
Map<String, RuleSet> ruleMap = setupRuleSets();
|
||||
_ruleGroup = new ToggleGroup();
|
||||
boolean isSet = false;
|
||||
for (Map.Entry<String, RuleSet> entry : ruleMap.entrySet()) {
|
||||
RadioMenuItem ruleItem = new RadioMenuItem(entry.getKey());
|
||||
RuleSet rs = entry.getValue();
|
||||
ruleItem.setToggleGroup(_ruleGroup);
|
||||
ruleItem.setUserData(rs);
|
||||
if (!isSet || "Life".equals(entry.getValue().getShortName())) {
|
||||
isSet = true;
|
||||
ruleItem.setSelected(true);
|
||||
}
|
||||
_ruleMenu.getItems().add(ruleItem);
|
||||
ruleItem.setOnAction((event) -> ruleSetChanged(rs));
|
||||
}
|
||||
|
||||
// populate the speed choicebox
|
||||
_speedBox.setItems(FXCollections.observableArrayList(Integer.valueOf(1),
|
||||
Integer.valueOf(2),
|
||||
Integer.valueOf(5),
|
||||
Integer.valueOf(10),
|
||||
Integer.valueOf(20)));
|
||||
_speedBox.getSelectionModel().select(4);
|
||||
|
||||
// SBTODO set localized strings on menus, buttons, labels
|
||||
_stepButton.setText(UIStrings.getString(UIStrings.BUTTON_STEP));
|
||||
_menuGlider.setText(UIStrings.getString(UIStrings.MENU_GLIDER));
|
||||
}
|
||||
|
||||
private void initCellModel(int rows, int columns, int cellSize) {
|
||||
RuleSet selectedSet = (RuleSet) _ruleGroup.getSelectedToggle().getUserData();
|
||||
CellModel freshModel = new CellModel(rows, columns, selectedSet);
|
||||
freshModel.addObserver(this);
|
||||
if (_simCanvas.getModel() != null) {
|
||||
_simCanvas.getModel().deleteObserver(this);
|
||||
}
|
||||
_simCanvas.setModel(freshModel, cellSize);
|
||||
_simCanvas.schedulePaint();
|
||||
if (_updateThread != null) {
|
||||
_updateThread.doStop();
|
||||
}
|
||||
_updateThread = new UpdateThread(freshModel);
|
||||
onSetSpeed(null);
|
||||
_updateThread.start();
|
||||
}
|
||||
|
||||
private void ruleSetChanged(RuleSet rs) {
|
||||
if (_simCanvas.getModel() != null) {
|
||||
_simCanvas.getModel().setRuleSet(rs);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onDrag(MouseEvent event) {
|
||||
CellModel model = _simCanvas.getModel();
|
||||
double x = event.getX();
|
||||
double y = event.getY();
|
||||
int cellX = (int) (x / _simCanvas.getCellSize());
|
||||
int cellY = (int) (y / _simCanvas.getCellSize());
|
||||
|
||||
boolean drawOrErase = !event.isShiftDown(); // hold down shift to erase (set false)
|
||||
|
||||
if (cellX != _lastCellX || cellY != _lastCellY) {
|
||||
model.setCell(cellX, cellY, drawOrErase);
|
||||
_lastCellX = cellX;
|
||||
_lastCellY = cellY;
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onClick(MouseEvent event) {
|
||||
CellModel model = _simCanvas.getModel();
|
||||
double x = event.getX();
|
||||
double y = event.getY();
|
||||
int cellX = (int) (x / _simCanvas.getCellSize());
|
||||
int cellY = (int) (y / _simCanvas.getCellSize());
|
||||
|
||||
if (_shapeToDraw != null) {
|
||||
model.drawShape(cellX, cellY, _shapeToDraw);
|
||||
} else {
|
||||
model.flipCell(cellX, cellY);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void onPause(ActionEvent evt) {
|
||||
if (_updateThread != null) {
|
||||
_updateThread.pause();
|
||||
_startButton.setText(UIStrings.getString(UIStrings.BUTTON_START));
|
||||
_startButton.setOnAction(this::onStart);
|
||||
_stepButton.setDisable(false);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
@SuppressWarnings("unused")
|
||||
public void onStart(ActionEvent evt) {
|
||||
if (_updateThread != null && _updateThread.isPaused()) {
|
||||
_startButton.setText(UIStrings.getString(UIStrings.BUTTON_PAUSE));
|
||||
_startButton.setOnAction(this::onPause);
|
||||
_stepButton.setDisable(true);
|
||||
_updateThread.go();
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
@SuppressWarnings("unused")
|
||||
public void onAbout(ActionEvent evt) {
|
||||
AboutDialog.display();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for shape menu items. Since this is bound from code in the {@link #initialize()} method
|
||||
* and not from FXML we don't need to make it public and we don't need to annotate it.
|
||||
*
|
||||
* @param evt the menu selection event that we're handling
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private void onGlider(ActionEvent evt) {
|
||||
_shapeToDraw = (boolean[][]) _shapeGroup.getSelectedToggle().getUserData();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onClear(ActionEvent evt) {
|
||||
onPause(evt);
|
||||
_simCanvas.getModel().reset();
|
||||
}
|
||||
|
||||
@FXML
|
||||
@SuppressWarnings("unused")
|
||||
public void onEditColors(ActionEvent evt) {
|
||||
ColorDialog.display(_simCanvas.getForeground(), _simCanvas.getBackground(), _simCanvas.getOutline(),
|
||||
_simCanvas::setForeground, _simCanvas::setBackground, _simCanvas::setOutline);
|
||||
}
|
||||
|
||||
@FXML
|
||||
@SuppressWarnings("unused")
|
||||
public void onQuit(ActionEvent evt) {
|
||||
// shutdown any worker thread
|
||||
if (_updateThread != null) {
|
||||
try {
|
||||
// Offer a short window to politely join with
|
||||
// the thread before shutting down, good manners
|
||||
_updateThread.doStop();
|
||||
_updateThread.join(500);
|
||||
} catch (InterruptedException ex) {
|
||||
// if we had logging enabled, we would log this
|
||||
}
|
||||
}
|
||||
Platform.exit();
|
||||
}
|
||||
|
||||
@FXML
|
||||
@SuppressWarnings("unused")
|
||||
public void onNew(ActionEvent evt) {
|
||||
// display a setup window
|
||||
Setup.display((Stage) _stage.getOwner());
|
||||
// and close this window
|
||||
_stage.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Observable o, Object arg) {
|
||||
// the cell model has updated. time to redraw! Remember to make sure that happens on the FX thread.
|
||||
Runnable r = () -> _generationLabel.setText(Integer.toString(_simCanvas.getModel().getGeneration()));
|
||||
if (Platform.isFxApplicationThread()) {
|
||||
r.run();
|
||||
} else {
|
||||
Platform.runLater(r);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
@SuppressWarnings("unused")
|
||||
public void onShowAging(ActionEvent evt) {
|
||||
_simCanvas.setCellAging(_showAgingItem.isSelected());
|
||||
}
|
||||
|
||||
@FXML
|
||||
@SuppressWarnings("unused")
|
||||
public void onShowOutlines(ActionEvent evt) {
|
||||
_simCanvas.showCellOutlines(_showOutlinesItem.isSelected());
|
||||
}
|
||||
|
||||
@FXML
|
||||
@SuppressWarnings("unused")
|
||||
public void onSetSpeed(ActionEvent evt) {
|
||||
int speed = _speedBox.getValue().intValue();
|
||||
if (_updateThread != null) {
|
||||
_updateThread.setSleepInterval(1000 / speed);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
@SuppressWarnings("unused")
|
||||
public void onStep(ActionEvent evt) {
|
||||
_simCanvas.getModel().transform();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package com.loomcom.automata;
|
||||
/*
|
||||
* Copyright 7/10/18 by Stephen Beitzel
|
||||
*/
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* convenience class to load the uistrings resource bundle and then provide
|
||||
* exception-proof accessors for strings
|
||||
*
|
||||
* @author Stephen Beitzel <sbeitzel@pobox.com>
|
||||
*/
|
||||
public class UIStrings {
|
||||
public static final String BUTTON_CANCEL = "button.cancel";
|
||||
public static final String BUTTON_OK = "button.ok";
|
||||
public static final String BUTTON_PAUSE = "button.pause";
|
||||
public static final String BUTTON_START = "button.start";
|
||||
public static final String BUTTON_STEP = "button.step";
|
||||
|
||||
public static final String DIALOG_ABOUT_TITLE = "dialog.about.title";
|
||||
public static final String DIALOG_COLOR_TITLE = "dialog.color.title";
|
||||
|
||||
public static final String ERROR_TEXT_DIMENSIONS_SMALL = "error.text.dimensions.small";
|
||||
public static final String ERROR_TEXT_DIMENSIONS_LARGE = "error.text.dimensions.large";
|
||||
public static final String ERROR_NUMBERS_ONLY = "error.text.numbersOnly";
|
||||
|
||||
public static final String LABEL_BACKGROUND = "label.background";
|
||||
public static final String LABEL_CELLSIZE = "label.cellSize";
|
||||
public static final String LABEL_COLUMNS = "label.columns";
|
||||
public static final String LABEL_CREATE="label.create";
|
||||
public static final String LABEL_FOREGROUND = "label.foreground";
|
||||
public static final String LABEL_INPIXELS = "label.inPixels";
|
||||
public static final String LABEL_OUTLINE = "label.outline";
|
||||
public static final String LABEL_ROWS = "label.rows";
|
||||
|
||||
public static final String MENU_GLIDER = "menu.glider";
|
||||
|
||||
public static final String WINDOW_SETUP_TITLE = "window.setup.title";
|
||||
public static final String WINDOW_SIM_TITLE = "window.sim.title";
|
||||
|
||||
private static ResourceBundle BUNDLE;
|
||||
|
||||
public static void init(Locale uiLocale) {
|
||||
BUNDLE = ResourceBundle.getBundle("uiStrings", uiLocale);
|
||||
}
|
||||
|
||||
public static String getString(String key) {
|
||||
try {
|
||||
return BUNDLE.getString(key);
|
||||
} catch (Exception e) {
|
||||
// probably, it's a missing resource
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,9 @@
|
|||
|
||||
package com.loomcom.automata;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Thread which updates the cell model.
|
||||
*
|
||||
|
@ -27,37 +30,41 @@ package com.loomcom.automata;
|
|||
* @version $Id: UpdateThread.java,v 1.5 2003/10/04 01:23:14 sethm Exp $
|
||||
*/
|
||||
public class UpdateThread extends Thread {
|
||||
CellModel mCellModel;
|
||||
int mSleepInterval;
|
||||
boolean mStop = false;
|
||||
boolean mPause = true;
|
||||
private static final Logger __l = LoggerFactory.getLogger(UpdateThread.class);
|
||||
|
||||
public UpdateThread(CellModel m) {
|
||||
private CellModel mCellModel;
|
||||
private int mSleepInterval;
|
||||
private boolean mStop = false;
|
||||
private boolean mPause = true;
|
||||
|
||||
UpdateThread(CellModel m) {
|
||||
mCellModel = m;
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the delay between each cell data update.
|
||||
*
|
||||
* @param i The delay, in milliseconds.
|
||||
*/
|
||||
public void setSleepInterval(int i) {
|
||||
/**
|
||||
* Set the delay between each cell data update.
|
||||
*
|
||||
* @param i The delay, in milliseconds.
|
||||
*/
|
||||
void setSleepInterval(int i) {
|
||||
mSleepInterval = i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the cell model, then wait for a specified time and loop.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
while (!mStop) {
|
||||
while (mPause) {
|
||||
try {
|
||||
synchronized(this) {
|
||||
synchronized (this) {
|
||||
wait();
|
||||
mPause = false;
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
System.out.println(ex);
|
||||
__l.warn("Interrupted while pausing", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,7 +74,7 @@ public class UpdateThread extends Thread {
|
|||
try {
|
||||
Thread.sleep(mSleepInterval);
|
||||
} catch (InterruptedException ex) {
|
||||
System.out.println(ex);
|
||||
__l.warn("Interrupted while waiting during run", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,16 +82,16 @@ public class UpdateThread extends Thread {
|
|||
/**
|
||||
* Start updating the cell data if the thread is currently paused.
|
||||
*/
|
||||
public synchronized void go() {
|
||||
synchronized void go() {
|
||||
mPause = false;
|
||||
this.notifyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the thread. The thread can be started again using
|
||||
* the <tt>go()</tt> method.
|
||||
*
|
||||
*/
|
||||
public void pause() {
|
||||
void pause() {
|
||||
mPause = true;
|
||||
}
|
||||
|
||||
|
@ -92,17 +99,17 @@ public class UpdateThread extends Thread {
|
|||
* Stop the thread. The thread cannot be started again once
|
||||
* it has been stopped.
|
||||
*/
|
||||
public void doStop() {
|
||||
void doStop() {
|
||||
mStop = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true of the thread is paused (not updating the model).
|
||||
*
|
||||
* @return true if the thread is not currently updating
|
||||
* the cell data (i.e., in a wait state)
|
||||
* @return true if the thread is not currently updating
|
||||
* the cell data (i.e., in a wait state)
|
||||
*/
|
||||
public boolean isPaused() {
|
||||
boolean isPaused() {
|
||||
return mPause;
|
||||
}
|
||||
}
|
|
@ -34,7 +34,9 @@ public class Utilities {
|
|||
* Center a window on screen.
|
||||
*
|
||||
* @param w The window to center.
|
||||
* @deprecated This isn't necessary with JavaFX
|
||||
*/
|
||||
@Deprecated
|
||||
public static void centerWindow(Window w) {
|
||||
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.DialogPane?>
|
||||
<?import javafx.scene.control.ScrollPane?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.web.WebView?>
|
||||
|
||||
|
||||
<DialogPane xmlns="http://javafx.com/javafx/" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.loomcom.automata.AboutDialog">
|
||||
<content>
|
||||
<GridPane>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0" prefWidth="650.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="400.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<ScrollPane fitToHeight="true" fitToWidth="true" prefHeight="200.0" prefWidth="200.0">
|
||||
<WebView prefHeight="200.0" prefWidth="200.0" fx:id="_webView"/>
|
||||
</ScrollPane>
|
||||
</GridPane>
|
||||
</content>
|
||||
</DialogPane>
|
|
@ -0,0 +1,303 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Cellular Automata Explorer</title>
|
||||
</head>
|
||||
<body>
|
||||
<div style="text-align: center">
|
||||
Seth Morabito<br/>
|
||||
Loom Communications<br/>
|
||||
http://www.loomcom.com/<br/>
|
||||
<!-- TODO use some template engine (velocity?) to drop in the version and build date from pom.xml during build -->
|
||||
Version 1.2.5<br/>
|
||||
July 13, 2018<br/>
|
||||
</div>
|
||||
<div style="text-align: center">
|
||||
Distributed under the terms of the GNU Public License.
|
||||
</div>
|
||||
<div>
|
||||
<pre> GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
</pre>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.ColorPicker?>
|
||||
<?import javafx.scene.control.DialogPane?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import com.loomcom.automata.CellPanel?>
|
||||
|
||||
<DialogPane xmlns="http://javafx.com/javafx/" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.loomcom.automata.ColorDialog">
|
||||
<content>
|
||||
<GridPane>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="300.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="35.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="35.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="35.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="60.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<HBox prefHeight="100.0" prefWidth="200.0">
|
||||
<Label fx:id="_foregroundLabel" text="Foreground">
|
||||
<HBox.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</HBox.margin>
|
||||
</Label>
|
||||
<ColorPicker fx:id="_foreground" onAction="#onForeground" promptText="Foreground color">
|
||||
<HBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</HBox.margin>
|
||||
</ColorPicker>
|
||||
</HBox>
|
||||
<HBox prefHeight="100.0" prefWidth="200.0" GridPane.rowIndex="1">
|
||||
<Label fx:id="_backgroundLabel" text="Background">
|
||||
<HBox.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</HBox.margin>
|
||||
</Label>
|
||||
<ColorPicker fx:id="_background" onAction="#onBackground">
|
||||
<HBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</HBox.margin>
|
||||
</ColorPicker>
|
||||
</HBox>
|
||||
<HBox prefHeight="100.0" prefWidth="200.0" GridPane.rowIndex="2">
|
||||
<Label fx:id="_outlineLabel" text="Outline color">
|
||||
<HBox.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</HBox.margin>
|
||||
</Label>
|
||||
<ColorPicker fx:id="_outlinePicker" onAction="#onOutline">
|
||||
<HBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</HBox.margin>
|
||||
</ColorPicker>
|
||||
</HBox>
|
||||
<HBox prefHeight="100.0" prefWidth="200.0" GridPane.rowIndex="3">
|
||||
<Label fx:id="_previewLabel" text="Preview">
|
||||
<HBox.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</HBox.margin>
|
||||
</Label>
|
||||
<CellPanel fx:id="_preview" height="60.0" width="60.0">
|
||||
<HBox.margin>
|
||||
<Insets />
|
||||
</HBox.margin>
|
||||
</CellPanel>
|
||||
</HBox>
|
||||
</GridPane>
|
||||
</content>
|
||||
</DialogPane>
|
|
@ -0,0 +1,82 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="190.0"
|
||||
prefWidth="370.0" xmlns="http://javafx.com/javafx/" xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="com.loomcom.automata.Setup">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints maxHeight="30.0" minHeight="10.0" prefHeight="30.0" vgrow="NEVER" />
|
||||
<RowConstraints maxHeight="40.0" minHeight="40.0" prefHeight="40.0" vgrow="NEVER" />
|
||||
<RowConstraints maxHeight="40.0" minHeight="40.0" prefHeight="40.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="40.0" minHeight="40.0" prefHeight="40.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="40.0" minHeight="40.0" prefHeight="40.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<Label fx:id="_createLabel" text="Create a New Cellular Automata" GridPane.columnSpan="2" GridPane.halignment="CENTER">
|
||||
<font>
|
||||
<Font name="System Bold" size="24.0" />
|
||||
</font>
|
||||
</Label>
|
||||
<Label fx:id="_columnLabel" text="Columns" GridPane.halignment="RIGHT" GridPane.rowIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<Label fx:id="_rowLabel" text="Rows" GridPane.halignment="RIGHT" GridPane.rowIndex="2">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<Label fx:id="_sizeLabel" text="Cell size" GridPane.halignment="RIGHT" GridPane.rowIndex="3">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<ButtonBar prefHeight="40.0" prefWidth="200.0" GridPane.columnSpan="2" GridPane.rowIndex="4">
|
||||
<buttons>
|
||||
<Button fx:id="_okButton" defaultButton="true" mnemonicParsing="false" onAction="#onOk" text="Ok" />
|
||||
<Button fx:id="_cancelButton" cancelButton="true" mnemonicParsing="false" onAction="#onCancel" text="Cancel" />
|
||||
</buttons>
|
||||
<GridPane.margin>
|
||||
<Insets right="10.0" />
|
||||
</GridPane.margin>
|
||||
</ButtonBar>
|
||||
<TextField fx:id="_columnField" maxWidth="50.0" minWidth="50.0" prefWidth="50.0" text="64" GridPane.columnIndex="1" GridPane.rowIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</GridPane.margin>
|
||||
</TextField>
|
||||
<TextField fx:id="_rowField" maxWidth="50.0" minWidth="50.0" prefWidth="50.0" text="64" GridPane.columnIndex="1" GridPane.rowIndex="2">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</GridPane.margin>
|
||||
</TextField>
|
||||
<HBox prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="3" GridPane.valignment="CENTER">
|
||||
<TextField fx:id="_sizeField" maxWidth="50.0" minWidth="50.0" prefWidth="50.0" text="10">
|
||||
<HBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</HBox.margin>
|
||||
</TextField>
|
||||
<Label fx:id="_inPixelsLabel" text="(in pixels)">
|
||||
<HBox.margin>
|
||||
<Insets bottom="12.0" left="5.0" right="12.0" top="12.0" />
|
||||
</HBox.margin>
|
||||
<font>
|
||||
<Font size="10.0" />
|
||||
</font>
|
||||
</Label>
|
||||
</HBox>
|
||||
</GridPane>
|
|
@ -0,0 +1,109 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.CheckMenuItem?>
|
||||
<?import javafx.scene.control.ChoiceBox?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.Menu?>
|
||||
<?import javafx.scene.control.MenuBar?>
|
||||
<?import javafx.scene.control.MenuItem?>
|
||||
<?import javafx.scene.control.RadioMenuItem?>
|
||||
<?import javafx.scene.control.SeparatorMenuItem?>
|
||||
<?import javafx.scene.input.KeyCodeCombination?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import com.loomcom.automata.CellPanel?>
|
||||
|
||||
<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0"
|
||||
prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/"
|
||||
fx:controller="com.loomcom.automata.SimWindow">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="0.0" prefHeight="30.0" vgrow="NEVER" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="60.0" minHeight="60.0" prefHeight="60.0" vgrow="NEVER" />
|
||||
</rowConstraints>
|
||||
<MenuBar>
|
||||
<Menu mnemonicParsing="false" text="File">
|
||||
<MenuItem fx:id="_newItem" mnemonicParsing="false" onAction="#onNew" text="New...">
|
||||
<accelerator>
|
||||
<KeyCodeCombination alt="UP" code="N" control="UP" meta="DOWN" shift="UP" shortcut="UP" />
|
||||
</accelerator>
|
||||
</MenuItem>
|
||||
<MenuItem fx:id="_quitItem" mnemonicParsing="false" onAction="#onQuit" text="Quit">
|
||||
<accelerator>
|
||||
<KeyCodeCombination alt="UP" code="Q" control="UP" meta="DOWN" shift="UP" shortcut="UP" />
|
||||
</accelerator>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<Menu mnemonicParsing="false" text="Draw">
|
||||
<CheckMenuItem fx:id="_showOutlinesItem" mnemonicParsing="false" selected="true" text="Show Cell Outlines" onAction="#onShowOutlines"/>
|
||||
<CheckMenuItem fx:id="_showAgingItem" mnemonicParsing="false" selected="true" text="Show Cell Aging" onAction="#onShowAging"/>
|
||||
<SeparatorMenuItem mnemonicParsing="false" />
|
||||
<MenuItem fx:id="_editColorsItem" mnemonicParsing="false" onAction="#onEditColors" text="Edit Colors..." />
|
||||
<SeparatorMenuItem mnemonicParsing="false" />
|
||||
<MenuItem fx:id="_clearDisplayItem" mnemonicParsing="false" onAction="#onClear" text="Clear Display" />
|
||||
<SeparatorMenuItem mnemonicParsing="false" />
|
||||
<Menu fx:id="_menuGlider" mnemonicParsing="false" text="Draw Glider">
|
||||
<accelerator>
|
||||
<KeyCodeCombination alt="UP" code="G" control="UP" meta="DOWN" shift="UP" shortcut="UP" />
|
||||
</accelerator>
|
||||
<RadioMenuItem fx:id="_noneItem" mnemonicParsing="false" selected="true" text="None" />
|
||||
</Menu>
|
||||
</Menu>
|
||||
<Menu fx:id="_ruleMenu" mnemonicParsing="false" text="Rule" />
|
||||
<Menu mnemonicParsing="false" text="Help">
|
||||
<MenuItem fx:id="_aboutItem" mnemonicParsing="false" onAction="#onAbout" text="About" />
|
||||
</Menu>
|
||||
</MenuBar>
|
||||
<HBox prefHeight="100.0" prefWidth="200.0" GridPane.rowIndex="2">
|
||||
<Region prefHeight="200.0" prefWidth="200.0" />
|
||||
<VBox prefHeight="200.0" prefWidth="100.0">
|
||||
<Label fx:id="_generationsLabel" text="Generations per second">
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
</Label>
|
||||
<ChoiceBox maxWidth="150.0" minWidth="150.0" prefWidth="150.0" fx:id="_speedBox" onAction="#onSetSpeed">
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
</ChoiceBox>
|
||||
</VBox>
|
||||
<Button fx:id="_startButton" maxWidth="90.0" minWidth="60.0" mnemonicParsing="false" onAction="#onStart"
|
||||
prefWidth="60.0" text="Start">
|
||||
<HBox.margin>
|
||||
<Insets left="5.0" right="5.0" top="20.0" />
|
||||
</HBox.margin>
|
||||
</Button>
|
||||
<Button fx:id="_stepButton" maxWidth="60.0" minWidth="60.0" mnemonicParsing="false" onAction="#onStep"
|
||||
prefWidth="60.0" text="Step">
|
||||
<HBox.margin>
|
||||
<Insets left="5.0" right="5.0" top="20.0" />
|
||||
</HBox.margin>
|
||||
</Button>
|
||||
<VBox minWidth="100.0" prefHeight="200.0" prefWidth="100.0">
|
||||
<Label text="Generations">
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
</Label>
|
||||
<Label fx:id="_generationLabel" alignment="CENTER_RIGHT" prefHeight="17.0" prefWidth="60.0" text="0"
|
||||
textAlignment="RIGHT" textFill="#e13408">
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
</Label>
|
||||
</VBox>
|
||||
<Region prefHeight="200.0" prefWidth="200.0" />
|
||||
</HBox>
|
||||
<CellPanel fx:id="_simCanvas" height="200.0" onMouseClicked="#onClick" onMouseDragged="#onDrag" width="200.0"
|
||||
GridPane.rowIndex="1" />
|
||||
</GridPane>
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration xmlns="http://ch.qos.logback/xml/ns/logback" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback http://ch.qos.logback/xml/ns/logback/logback.xsd">
|
||||
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="CONSOLE" />
|
||||
</root>
|
||||
</configuration>
|
|
@ -0,0 +1,21 @@
|
|||
button.cancel=Cancel
|
||||
button.ok=Ok
|
||||
button.pause=Pause
|
||||
button.start=Start
|
||||
button.step=Step
|
||||
dialog.about.title=Cellular Automata Explorer
|
||||
dialog.color.title=Choose Colors
|
||||
error.text.dimensions.large=The values you've chosen would create a window too large to fit on the screen. Please choose smaller values.
|
||||
error.text.dimensions.small=Please use positive numbers.
|
||||
error.text.numbersOnly=Please use numbers.
|
||||
label.background=Background
|
||||
label.cellSize=Cell size
|
||||
label.columns=Columns
|
||||
label.create=Create a New Cellular Automata
|
||||
label.foreground=Foreground
|
||||
label.inPixels=(in pixels)
|
||||
label.outline=Outline
|
||||
label.rows=Rows
|
||||
menu.glider=Draw Glider
|
||||
window.setup.title=Setup
|
||||
window.sim.title=Automata
|
|
@ -0,0 +1,21 @@
|
|||
button.cancel=Cancel
|
||||
button.ok=Ok
|
||||
button.pause=Pause
|
||||
button.start=Start
|
||||
button.step=Step
|
||||
dialog.about.title=Cellular Automata Explorer
|
||||
dialog.color.title=Choose Colors
|
||||
error.text.dimensions.large=The values you've chosen would create a window too large to fit on the screen. Please choose smaller values.
|
||||
error.text.dimensions.small=Please use positive numbers.
|
||||
error.text.numbersOnly=Please use numbers.
|
||||
label.background=Dead cell
|
||||
label.cellSize=Cell size
|
||||
label.columns=Columns
|
||||
label.create=Create a New Cellular Automata
|
||||
label.foreground=Live cell
|
||||
label.inPixels=(in pixels)
|
||||
label.outline=Cell outline
|
||||
label.rows=Rows
|
||||
menu.glider=Shapes
|
||||
window.setup.title=Setup
|
||||
window.sim.title=Automata
|
Loading…
Reference in New Issue