diff --git a/.classpath b/.classpath
index fb50116..4c8d782 100644
--- a/.classpath
+++ b/.classpath
@@ -1,6 +1,7 @@
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b2dfb01
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright Tomas Uktveris (c) 2015
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index a42847b..d02e7b1 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,46 @@
-DotMatrixJava
-=============
+8x8x8 Led Cube control program
+---------
-Java PC animation generator for 3D8S 8x8x8 Led charlieplexed
+PC control program for generic 8x8x8 3D LED Cube found on eBay
+(e.g. 3D LightSquared 8x8x8 LED Cube DIY kit, ideasoft, etc.)
+
+
+
+* Written in Java (requires Java RE, download here: http://java.com)
+* Supports direct animation playback through Serial/UART interface (rxtx library)
+* Various GUI usability enhancements
+
+##### Firmware
+This program can control any packet format compatible LED cube (see below).
+Example firmware of a compatible LED cube with an STC12C5A60S2 MCU can be found here: [Source Code](https://github.com/tomazas/ledcube8x8x8)
+
+##### Using the program
+* Run run_x32.bat for 32-bit Windows
+* Run run_x64.bat for 64-bit Windows
+
+Check this YouTube video for example: https://youtu.be/UplJi7pdV_Y
+[](https://youtu.be/UplJi7pdV_Y)
+
+##### LED Cube control packet format
+
+8x8x8 LED Cubes that support below packet format can be controlled with the program via Serial console or other MCU such as an Atmega/Arduino.
+Example UART/Serial packet (in hex):
+```
+F2
+00 00 00 00 00 00 00 FF
+00 00 00 00 00 00 00 FF
+00 00 00 00 00 00 00 FF
+00 00 00 00 00 00 00 FF
+00 00 00 00 00 00 00 FF
+00 00 00 00 00 00 00 FF
+00 00 00 00 00 00 00 FF
+00 00 00 00 00 00 00 FF
+```
+
+* F2 - denotes packet header (aka. batch update)
+* next 64 bytes - (8x8x8 bits) of LED light states
+ * one byte - controls a LED row (8 LEDs)
+ * can be any value in range: 00-FF (i.e. 00 - all 8 LEDs in row are off, FF - all 8 LEDs are on)
+ * a single line (e.g. 00 00 00 00 00 00 00 FF) denotes a 64 LED layer
+
+`Implementation note:` to save energy/current consumption only a single layer (64 LEDs) in a LED cube is ON at one time. This is done for all layers and so fast (using a hardware timer), that the human eye does not recognize this. This hack allows to view the cube (all layers) as fully lit.
diff --git a/arduino-controller/SDAnimation.ino b/arduino-controller/SDAnimation.ino
index c5b5679..d201ed3 100644
--- a/arduino-controller/SDAnimation.ino
+++ b/arduino-controller/SDAnimation.ino
@@ -1,18 +1,15 @@
#include "SD.h"
#include "string.h"
-Sd2Card card;
-SdVolume volume;
-const int chipSelect = 4;
File root;
void setup()
{
- Serial.begin(57600);
+ Serial.begin(9600);
- pinMode(10, OUTPUT); // change this to 53 on a mega
+ pinMode(10, OUTPUT); // SS pin must be output, change this to 53 on a mega
- bool sd_ok = SD.begin(9);
+ bool sd_ok = SD.begin(9); // chip select pin
if (!sd_ok)
{
@@ -25,22 +22,13 @@ void setup()
void sendAnimation(byte *data)
{
- Serial.write(0xf3);
- Serial.write(data[1]);
-
- Serial.write(0xf4);
- Serial.write(data[2]);
-
- Serial.write(0xf5);
- Serial.write(data[3]);
-
- Serial.write(0xf2);
+ Serial.write(0xf2); // batch update supported
for (byte i = 8; i < 72; i++)
{
Serial.write(data[i]);
}
- delay(makeWord(data[6], data[7]));
+ delay(20);
}
void readAnimation(File & file)
@@ -61,7 +49,7 @@ void loop(void)
char *p = file.name();
char *p_dot = strchr(p, '.');
- if (p_dot != NULL && strcmp(p_dot, ".DAT") == 0)
+ if (p_dot != NULL && strcmp(p_dot, ".dat") == 0)
{
readAnimation(file);
}
diff --git a/dotmatrixjava.jar b/dotmatrixjava.jar
index 1670f59..80adff4 100644
Binary files a/dotmatrixjava.jar and b/dotmatrixjava.jar differ
diff --git a/examples.zip b/examples.zip
deleted file mode 100644
index dd5b0d2..0000000
Binary files a/examples.zip and /dev/null differ
diff --git a/help/program_view.png b/help/program_view.png
new file mode 100644
index 0000000..bd41783
Binary files /dev/null and b/help/program_view.png differ
diff --git a/libs/RXTXcomm.jar b/libs/RXTXcomm.jar
new file mode 100644
index 0000000..e1e7503
Binary files /dev/null and b/libs/RXTXcomm.jar differ
diff --git a/record.dat b/record.dat
deleted file mode 100644
index 541b6da..0000000
Binary files a/record.dat and /dev/null differ
diff --git a/record2.dat b/record2.dat
deleted file mode 100644
index 749cbdc..0000000
Binary files a/record2.dat and /dev/null differ
diff --git a/record3.dat b/record3.dat
deleted file mode 100644
index ab5c616..0000000
Binary files a/record3.dat and /dev/null differ
diff --git a/run_x32.bat b/run_x32.bat
new file mode 100644
index 0000000..e248c1f
--- /dev/null
+++ b/run_x32.bat
@@ -0,0 +1 @@
+java -Djava.library.path=./runtime/x32 -jar dotmatrixjava.jar
\ No newline at end of file
diff --git a/run_x64.bat b/run_x64.bat
new file mode 100644
index 0000000..92403b9
--- /dev/null
+++ b/run_x64.bat
@@ -0,0 +1 @@
+java -Djava.library.path=./runtime/x64 -jar dotmatrixjava.jar
\ No newline at end of file
diff --git a/runtime/x32/rxtxParallel.dll b/runtime/x32/rxtxParallel.dll
new file mode 100644
index 0000000..28d4d2a
Binary files /dev/null and b/runtime/x32/rxtxParallel.dll differ
diff --git a/runtime/x32/rxtxSerial.dll b/runtime/x32/rxtxSerial.dll
new file mode 100644
index 0000000..5cd55bd
Binary files /dev/null and b/runtime/x32/rxtxSerial.dll differ
diff --git a/runtime/x64/rxtxParallel.dll b/runtime/x64/rxtxParallel.dll
new file mode 100644
index 0000000..92666dd
Binary files /dev/null and b/runtime/x64/rxtxParallel.dll differ
diff --git a/runtime/x64/rxtxSerial.dll b/runtime/x64/rxtxSerial.dll
new file mode 100644
index 0000000..211e006
Binary files /dev/null and b/runtime/x64/rxtxSerial.dll differ
diff --git a/src/aguegu/dotmatrix/DMRecordFrame.java b/src/aguegu/dotmatrix/DMRecordFrame.java
index 92b3f12..aa26b7c 100644
--- a/src/aguegu/dotmatrix/DMRecordFrame.java
+++ b/src/aguegu/dotmatrix/DMRecordFrame.java
@@ -85,6 +85,15 @@ public byte[] getData() {
return data;
}
+
+ public byte[] getSimpleData() {
+ byte[] data = new byte[65];
+
+ data[0] = (byte) 0xf2;
+ System.arraycopy(dm.getCache(), 0, data, 1, DotMatrix.CACHE_LENGTH);
+
+ return data;
+ }
public String getCacheString() {
return DotMatrix.cacheString(getData());
diff --git a/src/aguegu/dotmatrix/DMRecordHeaderPanel.java b/src/aguegu/dotmatrix/DMRecordHeaderPanel.java
index 53a52a5..85bf2b9 100644
--- a/src/aguegu/dotmatrix/DMRecordHeaderPanel.java
+++ b/src/aguegu/dotmatrix/DMRecordHeaderPanel.java
@@ -1,157 +1,307 @@
package aguegu.dotmatrix;
+import gnu.io.CommPortIdentifier;
+import gnu.io.PortInUseException;
+import gnu.io.SerialPort;
+import gnu.io.UnsupportedCommOperationException;
+
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
import java.util.ResourceBundle;
+import java.util.Timer;
+import java.util.TimerTask;
import javax.swing.BoxLayout;
-import javax.swing.ButtonGroup;
-import javax.swing.ImageIcon;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JButton;
import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
import javax.swing.JLabel;
+import javax.swing.JOptionPane;
import javax.swing.JPanel;
-import javax.swing.JRadioButtonMenuItem;
-import javax.swing.JSlider;
+import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
public class DMRecordHeaderPanel extends JPanel {
private static final long serialVersionUID = 7530602456593370095L;
private DMRecordPanel parent;
- private JSlider sliderBrightness;
- private JSlider sliderSpan;
-
- private JCheckBox checkboxUpperLed;
- private JCheckBox checkboxBottomLed;
-
- private JRadioButtonMenuItem[] radiobuttonModes;
-
- private CL cl;
+ private SerialPort port;
+ private OutputStream outputStream;
+ private JComboBox baudBox;
+ private JComboBox portBox;
+ private Timer timer;
+ private JTextField delayBox;
+ private JCheckBox loopChk;
- public DMRecordHeaderPanel(DMRecordPanel dmrp, ResourceBundle res) {
+ public DMRecordHeaderPanel(DMRecordPanel dmrp, final ResourceBundle res) {
parent = dmrp;
this.setLayout(new FlowLayout(FlowLayout.LEFT));
this.setBorder(new EmptyBorder(new Insets(0, 4, 0, 0)));
-
+
JPanel panelMode = new JPanel(new FlowLayout(FlowLayout.LEFT));
-
- radiobuttonModes = new JRadioButtonMenuItem[3];
- ButtonGroup bgMode = new ButtonGroup();
- int i = 0;
- for (DMMode mode : DMMode.values()) {
- radiobuttonModes[i] = new JRadioButtonMenuItem(
- new ImageIcon(getClass().getResource(
- "/image/" + mode.toString().toLowerCase() + ".png")));
-
- radiobuttonModes[i].setActionCommand(mode.toString());
- radiobuttonModes[i].addActionListener(new ActionListenerMode());
- bgMode.add(radiobuttonModes[i]);
- panelMode.add(radiobuttonModes[i]);
- i++;
- }
+
panelMode.setAlignmentX(LEFT_ALIGNMENT);
JPanel panelAttachment = new JPanel(new FlowLayout(FlowLayout.LEFT));
- checkboxUpperLed = new JCheckBox(res.getString("upper_led"));
- checkboxUpperLed.addActionListener(new ActionListenerAttachment());
- panelAttachment.add(checkboxUpperLed);
+ panelAttachment.add(new JLabel(res.getString("comport") + ":"));
+
+ portBox = new JComboBox(enumeratePorts().toArray(new String[]{}));
+ portBox.setEditable(true);
+
+ panelAttachment.add(portBox);
+
+ final JButton refreshBtn = new JButton(res.getString("refresh"));
+ refreshBtn.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ portBox.removeAllItems();
+ portBox.setModel(new DefaultComboBoxModel(enumeratePorts().toArray(new String[]{})));
+ }
+ });
+ panelAttachment.add(refreshBtn);
+
+ panelAttachment.add(new JLabel(res.getString("baud") + ":"));
+ baudBox = new JComboBox(new String[]{"9600","19200","38400","57600","115200"});
+ baudBox.setEditable(true);
+ panelAttachment.add(baudBox);
+
+ final JButton playBtn = new JButton(res.getString("play"));
+ playBtn.setEnabled(false);
+
+ final JButton closeBtn = new JButton(res.getString("close_serial"));
+ closeBtn.setEnabled(false);
+
+ final JButton openBtn = new JButton(res.getString("open_serial"));
+ openBtn.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (portBox.getSelectedItem() == null) return;
+ String input = ((String)portBox.getSelectedItem()).trim();
+ if (!input.isEmpty()) {
+ if (openSerial(input)) {
+ closeBtn.setEnabled(true);
+ openBtn.setEnabled(false);
+ playBtn.setEnabled(true);
+ }
+ }
+ }
+ });
+ panelAttachment.add(openBtn);
+ panelAttachment.add(closeBtn);
+
+ closeBtn.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ closeSerial();
+ closeBtn.setEnabled(false);
+ openBtn.setEnabled(true);
+ playBtn.setEnabled(false);
+ }
+ });
- checkboxBottomLed = new JCheckBox(res.getString("bottom_led"));
- checkboxBottomLed.addActionListener(new ActionListenerAttachment());
- panelAttachment.add(checkboxBottomLed);
- panelAttachment.setAlignmentX(LEFT_ALIGNMENT);
+ playBtn.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (timer != null) {
+ timer.cancel();
+ timer = null;
+ playBtn.setText(res.getString("play"));
+ closeBtn.setEnabled(true);
+ return;
+ }
+
+ int delay = 20; // default ms
+ try {
+ delay = Integer.parseInt(delayBox.getText());
+ if (delay <= 0) {
+ throw new NumberFormatException("Not a positive number");
+ }
+ } catch (NumberFormatException ex) {
+ JOptionPane.showMessageDialog(null, "Invalid frame update delay!", "Play error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ if (parent.getNumFrames() > 0) {
+ TimerTask task = new TimerTask() {
+ private int counter = 0;
+ @Override
+ public void run() {
+ int num = parent.getNumFrames();
+ if (num > 0) {
+ // still have frames left
+ counter = (counter+1)%num;
+ parent.setFrame(counter);
+
+ try {
+ byte data[] = parent.getRecordFrame().getSimpleData();
+ outputStream.write(data);
+ } catch (IOException e) {
+ this.cancel();
+ JOptionPane.showMessageDialog(null, "Unable to play:\n"+e.getMessage(), "Play error", JOptionPane.ERROR_MESSAGE);
+ }
+ } else {
+ // e.g. new project was created and all frames are gone!
+ timer.cancel();
+ timer = null;
+ playBtn.setText(res.getString("play"));
+ closeBtn.setEnabled(true);
+ }
+ }
+ };
+
+ if (loopChk.isSelected()) {
+ playBtn.setText(res.getString("stop"));
+ closeBtn.setEnabled(false);
+
+ timer = new Timer();
+ timer.schedule(task, 0, delay);
+ } else {
+ task.run();
+ }
+ } else {
+ JOptionPane.showMessageDialog(null, "Nothing to play - add frames first!", "Play error", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ });
JPanel panelModeAndAttachment = new JPanel();
- panelModeAndAttachment.setLayout(new BoxLayout(panelModeAndAttachment,
- BoxLayout.Y_AXIS));
- panelModeAndAttachment.add(new JLabel(res.getString("mode") + ":"));
+ panelModeAndAttachment.setLayout(new BoxLayout(panelModeAndAttachment, BoxLayout.Y_AXIS));
panelModeAndAttachment.add(panelMode);
panelModeAndAttachment.add(panelAttachment);
- cl = new CL();
-
- sliderBrightness = new JSlider(0, 255, 255);
- sliderBrightness.setMinorTickSpacing(0x20);
- sliderBrightness.setMajorTickSpacing(0x40);
- sliderBrightness.setPaintTicks(true);
- sliderBrightness.setSnapToTicks(true);
- sliderBrightness.addChangeListener(cl);
- sliderBrightness.setAlignmentX(LEFT_ALIGNMENT);
-
- sliderSpan = new JSlider(0, 0x0800, 0x0080);
- sliderSpan.setMinorTickSpacing(0x10);
- sliderSpan.setSnapToTicks(true);
- sliderSpan.setPaintTicks(true);
- sliderSpan.addChangeListener(cl);
- sliderSpan.setAlignmentX(LEFT_ALIGNMENT);
-
+ JPanel panelBelow = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ panelBelow.add(new JLabel(res.getString("delay") + ": "));
+
+ delayBox = new JTextField(res.getString("delay_ms"), 10);
+ panelBelow.add(delayBox);
+ panelBelow.add(playBtn);
+
+ loopChk = new JCheckBox(res.getString("anim_loop"), true);
+ panelBelow.add(playBtn);
+ panelBelow.add(loopChk);
+ panelModeAndAttachment.add(panelBelow);
+
JPanel panelSliders = new JPanel();
panelSliders.setLayout(new BoxLayout(panelSliders, BoxLayout.Y_AXIS));
- panelSliders.add(new JLabel(res.getString("brightness") + ":"));
- panelSliders.add(sliderBrightness);
- panelSliders.add(new JLabel(res.getString("time_span") + ":"));
- panelSliders.add(sliderSpan);
this.add(panelModeAndAttachment);
this.add(panelSliders);
}
-
- private class CL implements ChangeListener {
- @Override
- public void stateChanged(ChangeEvent e) {
- if (e.getSource() instanceof JSlider) {
- if (e.getSource().equals(sliderBrightness)) {
- parent.getRecordFrame().setBrightness(
- (Integer) sliderBrightness.getValue());
- } else if (e.getSource().equals(sliderSpan)) {
- parent.getRecordFrame().setSpan(
- (Integer) sliderSpan.getValue());
- }
-
- parent.refresh(true);
+
+ public void updateParent(DMRecordPanel parent) {
+ this.parent = parent;
+ }
+
+ public List enumeratePorts() {
+ // scan available COM ports
+ List ports = new ArrayList();
+ System.out.println("enumerate serial ports");
+
+ try {
+ Enumeration> port_list = CommPortIdentifier.getPortIdentifiers();
+
+ while (port_list.hasMoreElements()) {
+ CommPortIdentifier port_id = (CommPortIdentifier) port_list.nextElement();
+ if (port_id.getPortType() == CommPortIdentifier.PORT_SERIAL) {
+ ports.add(port_id.getName());
+ }
+ }
+ } catch (UnsatisfiedLinkError e) {
+ JOptionPane.showMessageDialog(null, "Error initializing serial port:\n" + e.getMessage(), "Serial error", JOptionPane.ERROR_MESSAGE);
+ } finally {
+ System.out.println(ports.size()+" ports found: " + ports);
+ if (ports.isEmpty()) {
+ ports.add("No ports found!"); // default
}
}
+
+ return ports;
}
-
- private class ActionListenerAttachment implements ActionListener {
- @Override
- public void actionPerformed(ActionEvent e) {
- if (checkboxUpperLed.isSelected() && checkboxBottomLed.isSelected())
- parent.getRecordFrame().setAttachment(DMAttachment.BOTH);
- else if (checkboxUpperLed.isSelected())
- parent.getRecordFrame().setAttachment(DMAttachment.UPPER_LED);
- else if (checkboxBottomLed.isSelected())
- parent.getRecordFrame().setAttachment(DMAttachment.BOTTOM_LED);
- else
- parent.getRecordFrame().setAttachment(DMAttachment.NONE);
- parent.refresh(true);
+
+ public boolean openSerial(String name) {
+ try {
+ System.out.println("open serial: " + name);
+
+ Enumeration> port_list = CommPortIdentifier.getPortIdentifiers();
+ boolean found = false;
+
+ while (port_list.hasMoreElements()) {
+ // Get the list of ports
+ CommPortIdentifier port_id = (CommPortIdentifier) port_list.nextElement();
+
+ if (port_id.getPortType() == CommPortIdentifier.PORT_SERIAL && port_id.getName().equals(name)) {
+ found = true;
+
+ try {
+ // attempt to open
+ port = (SerialPort) port_id.open("PortListOpen", 20);
+ if (port == null) {
+ throw new Exception("Cannot open port: " + name);
+ }
+
+ System.out.println("serial port opened: " + name);
+
+ int baudRate = Integer.parseInt((String)baudBox.getSelectedItem());
+ port.setSerialPortParams(
+ baudRate,
+ SerialPort.DATABITS_8,
+ SerialPort.STOPBITS_1,
+ SerialPort.PARITY_NONE);
+ port.setDTR(true);
+
+ outputStream = port.getOutputStream();
+ return true;
+ } catch (UnsupportedCommOperationException e) {
+ JOptionPane.showMessageDialog(null, "Invalid serial parameters:\n" + e.getMessage(), "Serial error", JOptionPane.ERROR_MESSAGE);
+ } catch (PortInUseException e) {
+ String owner = port_id.getCurrentOwner();
+ JOptionPane.showMessageDialog(null, "The port is already in use! Owner: " + (owner != null ? owner : "unknown"),
+ "Serial error", JOptionPane.ERROR_MESSAGE);
+ } catch (Exception e) {
+ JOptionPane.showMessageDialog(null, "I/O error:\n" + e.getMessage(), "Serial error", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+
+ // not found
+ if (!found) {
+ JOptionPane.showMessageDialog(null, "Serial port not found: " + name, "Serial error", JOptionPane.ERROR_MESSAGE);
+ }
+ } catch (UnsatisfiedLinkError e) {
+ JOptionPane.showMessageDialog(null, "Error initializing serial port:\n" + e.getMessage(), "Serial error", JOptionPane.ERROR_MESSAGE);
}
- }
-
- private class ActionListenerMode implements ActionListener {
- @Override
- public void actionPerformed(ActionEvent e) {
- parent.getRecordFrame().setMode(
- DMMode.getMode(e.getActionCommand()));
- parent.refresh(true);
+
+ return false;
+ }
+
+ private void closeSerial() {
+ if (port != null) {
+ System.out.println("closing serial port");
+ port.close();
}
+ port = null;
+ outputStream = null;
}
public void refresh() {
- sliderBrightness.removeChangeListener(cl);
- sliderBrightness.setValue(parent.getRecordFrame().getBrightness());
- sliderBrightness.addChangeListener(cl);
+ //sliderBrightness.removeChangeListener(cl);
+ //sliderBrightness.setValue(parent.getRecordFrame().getBrightness());
+ //sliderBrightness.addChangeListener(cl);
- sliderSpan.removeChangeListener(cl);
- sliderSpan.setValue(parent.getRecordFrame().getSpan());
- sliderSpan.addChangeListener(cl);
+ //sliderSpan.removeChangeListener(cl);
+ //sliderSpan.setValue(parent.getRecordFrame().getSpan());
+ //sliderSpan.addChangeListener(cl);
- switch (parent.getRecordFrame().getAttachment()) {
+ /*switch (parent.getRecordFrame().getAttachment()) {
case BOTH:
checkboxUpperLed.setSelected(true);
checkboxBottomLed.setSelected(true);
@@ -172,7 +322,7 @@ public void refresh() {
}
radiobuttonModes[parent.getRecordFrame().getMode().ordinal()]
- .setSelected(true);
+ .setSelected(true);*/
}
}
diff --git a/src/aguegu/dotmatrix/DMRecordList.java b/src/aguegu/dotmatrix/DMRecordList.java
index 888bd97..831a5bd 100644
--- a/src/aguegu/dotmatrix/DMRecordList.java
+++ b/src/aguegu/dotmatrix/DMRecordList.java
@@ -32,6 +32,10 @@ public void syncToReocrd() {
lm.addElement(dmrf);
}
}
+
+ public int getNumFrames() {
+ return lm.getSize();
+ }
class DotMatrixRecordCellRender extends JLabel implements
ListCellRenderer {
diff --git a/src/aguegu/dotmatrix/DMRecordPanel.java b/src/aguegu/dotmatrix/DMRecordPanel.java
index 8b3609a..cb9db04 100644
--- a/src/aguegu/dotmatrix/DMRecordPanel.java
+++ b/src/aguegu/dotmatrix/DMRecordPanel.java
@@ -49,7 +49,7 @@ public class DMRecordPanel extends JPanel {
private static final String[] FRAME_OPERATION_COMMANDS = new String[] {
"on", "off", "x+", "x-", "y+", "y-", "z+", "z-", "3c", "3a", "2c",
- "2a", "1c", "1a", "0c", "0a", "xf", "yf", "zf", "r" };
+ "2a", "1c", "1a", "0c", "0a", "r" };
private boolean inLoop = true;
private static Font monoFont;
@@ -58,9 +58,11 @@ public class DMRecordPanel extends JPanel {
private JPanel panelFrameOperation;
private ResourceBundle res;
+ private DotMatrixTest parent;
- public DMRecordPanel(ResourceBundle res) {
+ public DMRecordPanel(DotMatrixTest parent, DMRecordPanel prev, ResourceBundle res) {
this.res = res;
+ this.parent = parent;
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
panelController = new JPanel();
@@ -84,8 +86,9 @@ public DMRecordPanel(ResourceBundle res) {
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
panelController.add(textAreaPane);
- panelHeader = new DMRecordHeaderPanel(this, res);
-
+
+ panelHeader = (prev != null) ? prev.getHeader() : new DMRecordHeaderPanel(this, res);
+ panelHeader.updateParent(this);
panelController.add(panelHeader);
this.add(panelController);
@@ -96,6 +99,18 @@ public DMRecordPanel(ResourceBundle res) {
initMenu();
initFrameOperationPanel();
}
+
+ public DMRecordHeaderPanel getHeader() {
+ return panelHeader;
+ }
+
+ public void setFrame(int index) {
+ parent.setActiveFrame(index);
+ }
+
+ public int getNumFrames() {
+ return parent.getNumFrames();
+ }
public void setFrame(DMRecordFrame dmrf) {
this.dmrf = new DMRecordFrame(dmrf.getIndex());
@@ -250,7 +265,7 @@ public void initFrameOperationPanel() {
panelFrameOperation.add(button);
}
- checkboxInLoop = new JCheckBox("", inLoop);
+ checkboxInLoop = new JCheckBox(res.getString("loop"), inLoop);
checkboxInLoop.addActionListener(new ActionListenerInLoop());
checkboxInLoop.setToolTipText(res.getString("loop"));
panelFrameOperation.add(checkboxInLoop);
@@ -319,15 +334,6 @@ public void actionPerformed(ActionEvent e) {
case "0a":
dm.rotate(0, false, recycle);
break;
- case "xf":
- dm.flip(DotMatrix.Direction.X_POSI);
- break;
- case "yf":
- dm.flip(DotMatrix.Direction.Y_POSI);
- break;
- case "zf":
- dm.flip(DotMatrix.Direction.Z_POSI);
- break;
}
refresh(true);
}
diff --git a/src/aguegu/dotmatrix/DotMatrix.java b/src/aguegu/dotmatrix/DotMatrix.java
index 3535fbf..68e721f 100644
--- a/src/aguegu/dotmatrix/DotMatrix.java
+++ b/src/aguegu/dotmatrix/DotMatrix.java
@@ -76,6 +76,8 @@ static public String cacheString(byte[] cache) {
String s = new String();
for (int i = 0; i < cache.length; i++) {
+ if (i > 0 && i < 8) continue; // skip irrelevant parameters that we don't use (brightness etc.);
+
if (i % 8 == 0 && i > 0)
s = s.concat("\n");
s = s.concat(String.format("0x%02x, ", cache[i]));
diff --git a/src/aguegu/dotmatrix/DotMatrixTest.java b/src/aguegu/dotmatrix/DotMatrixTest.java
index fbddf2a..4a6a252 100644
--- a/src/aguegu/dotmatrix/DotMatrixTest.java
+++ b/src/aguegu/dotmatrix/DotMatrixTest.java
@@ -7,7 +7,6 @@
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
-
import java.util.Arrays;
import java.util.Locale;
import java.util.ResourceBundle;
@@ -26,7 +25,6 @@
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.ScrollPaneConstants;
-
import javax.swing.border.BevelBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
@@ -54,12 +52,14 @@ public class DotMatrixTest extends JFrame {
private static final String PROGRAME_NAME = new String(
"3D8 TF Animation Editor");
- private Locale locale = Locale.CHINESE;
+ private Locale locale = Locale.ENGLISH;
private ResourceBundle res;
private File fileRecord = null;
private String message;
private boolean isSaved = true;
+ private boolean firstInit = true;
+ private JFileChooser fs = new JFileChooser();
public static void main(String[] args) {
DotMatrixTest dmt = new DotMatrixTest();
@@ -74,10 +74,9 @@ public void init() {
this.getContentPane().removeAll();
- if (dmrf == null)
- dmrf = new DMRecordFrame(0);
+ dmrf = new DMRecordFrame(0);
- panelRecord = new DMRecordPanel(res);
+ panelRecord = new DMRecordPanel(this, panelRecord, res);
panelRecord.setFrame(dmrf);
panelToolbar = panelToolBar();
@@ -91,14 +90,10 @@ public void init() {
.createBevelBorder(BevelBorder.LOWERED));
this.getContentPane().add(BorderLayout.SOUTH, labelStatus);
- if (dmr == null)
- dmr = new DMRecord();
+ dmr = new DMRecord();
- if (listFrame == null) {
- listFrame = new DMRecordList(dmr);
- listFrame
- .addListSelectionListener(new ListSelectionListenerListFrame());
- }
+ listFrame = new DMRecordList(dmr);
+ listFrame.addListSelectionListener(new ListSelectionListenerListFrame());
JScrollPane listFramePane = new JScrollPane(listFrame,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
@@ -114,7 +109,10 @@ public void init() {
begin();
refreshFrame();
- this.setLocation(100, 100);
+ if (firstInit) {
+ firstInit = false;
+ this.setLocation(100, 100);
+ }
this.pack();
this.setResizable(false);
@@ -124,7 +122,6 @@ public void init() {
private class ActionListenerFileOperation implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
- JFileChooser fs = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter(
"3D8 animation record (*.dat)", "dat");
fs.setFileFilter(filter);
@@ -141,13 +138,16 @@ public void actionPerformed(ActionEvent e) {
if (result == JOptionPane.YES_OPTION)
save();
}
- begin();
+ init(); //begin
break;
case "open":
result = fs.showOpenDialog(null);
file = fs.getSelectedFile();
+
if (file == null || result != JFileChooser.APPROVE_OPTION)
break;
+
+ fs.setCurrentDirectory(file); // save old directory
fileRecord = file;
dmr.readRecord(fileRecord);
@@ -192,7 +192,7 @@ public void actionPerformed(ActionEvent e) {
}
}
- private class ActionListenerRecordOperation implements ActionListener {
+ private class ActionListenerRocordOperation implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
int index = listFrame.getSelectedIndex();
@@ -242,7 +242,8 @@ public void actionPerformed(ActionEvent e) {
String sSpan = JOptionPane.showInputDialog(
res.getString("span_prompt"), "0x0080");
- if (sSpan != null && sSpan.matches("0[x|X][\\p{XDigit}]{4}")) {
+ if (sSpan != null
+ && sSpan.matches("0[x|X][\\p{XDigit}]{4}")) {
int span = Integer.decode(sSpan);
dmr.setSpan(span);
}
@@ -266,12 +267,20 @@ public void valueChanged(ListSelectionEvent e) {
if (index == -1)
return;
- dmrf = dmr.getFrame(index);
- panelRecord.setFrame(dmrf);
- panelRecord.refresh(true);
- refreshFrame();
+ setActiveFrame(index);
}
}
+
+ public void setActiveFrame(int index) {
+ dmrf = dmr.getFrame(index);
+ panelRecord.setFrame(dmrf);
+ panelRecord.refresh(true);
+ refreshFrame();
+ }
+
+ public int getNumFrames() {
+ return listFrame.getNumFrames();
+ }
private class DotMatrixTestMenuBar extends JMenuBar implements
ActionListener {
@@ -302,7 +311,7 @@ public DotMatrixTestMenuBar() {
JMenuItem button = new JMenuItem(res.getString(s));
button.setActionCommand(s);
- button.addActionListener(new ActionListenerRecordOperation());
+ button.addActionListener(new ActionListenerRocordOperation());
mnRecord.add(button);
}
@@ -338,7 +347,7 @@ public void actionPerformed(ActionEvent e) {
switch (e.getActionCommand()) {
case "about":
JOptionPane.showMessageDialog(this,
- "For more info, check\nhttp://aguegu.net",
+ "For more info, check\nhttp://aguegu.net\nhttp://www.wzona.info",
res.getString("about"), JOptionPane.OK_OPTION
| JOptionPane.INFORMATION_MESSAGE);
break;
@@ -431,7 +440,7 @@ private JPanel panelToolBar() {
JButton button = new JButton(new ImageIcon(getClass().getResource(
"/image/" + s + ".png")));
button.setActionCommand(s);
- button.addActionListener(new ActionListenerRecordOperation());
+ button.addActionListener(new ActionListenerRocordOperation());
button.setToolTipText(res.getString(s));
toolbarReord.add(button);
}
diff --git a/src/aguegu/dotmatrix/DotMatrixTest.properties b/src/aguegu/dotmatrix/DotMatrixTest.properties
index cd5fbb4..4130e67 100644
--- a/src/aguegu/dotmatrix/DotMatrixTest.properties
+++ b/src/aguegu/dotmatrix/DotMatrixTest.properties
@@ -9,10 +9,10 @@ frame=Frame
record=Record
help=Help
-append=Append
-insert=Insert
-update=Update
-delete=Delete
+append=Append Frame to End
+insert=Insert Frame Above Current
+update=Update current frame
+delete=Delete Current Frame
language=Language
english=English
@@ -25,7 +25,7 @@ span=Span
mode_prompt=Mode\ for\ All\ Frames:\ (0-2)
brightness_prompt=Brightness\ for\ All\ Frames:\ (0x00-0xff)"
span_prompt=Span\ for\ All\ Frames:\ (0x0000-0xffff)
-message=Developed\ by\ http://aGuegu.net
+message=Developed\ by\ http://aGuegu.net | Updates by http://www.wzona.info
time_span=Time\ Span
upper_led=Upper\ Led
@@ -33,15 +33,15 @@ bottom_led=Bottom\ Led
about=About
-loop=Loop
-on=All\ On
-off=All\ Off
-x+=X\ +
-x-=X\ -
-y+=Y\ +
-y-=Y\ -
-z+=Z\ +
-z-=Z\ -
+loop=Wrap
+on=All LEDs On
+off=All LEDs Off
+x+=Shift X forward
+x-=Shift X backwards
+y+=Shift Y forward
+y-=Shift X backwards
+z+=Shift Z forward
+z-=Shift Z backwards
3c=R3\ Clockwise
3a=R3\ Anticlockwise
2c=R2\ Clockwise
@@ -50,7 +50,19 @@ z-=Z\ -
1a=R1\ Anticlockwise
0c=R0\ Clockwise
0a=R0\ Anticlockwise
-xf=flip\ X
-yf=flip\ Y
-zf=flip\ Z
-r=Reverse
+xf=Flip X
+yf=Flip Y
+zf=Flip Z
+r=Invert All LEDs
+
+baud=Baud
+play=Play
+stop=Stop
+comport=Serial port
+open_serial=Open
+close_serial=Close
+delay=Update delay(ms)
+delay_ms=20
+refresh=Refresh
+anim_loop=Loop animation
+