From 74206ee98e42a6228aed448f45c49cbdab98253a Mon Sep 17 00:00:00 2001 From: Christian Femers Date: Tue, 14 May 2019 14:19:28 +0200 Subject: [PATCH] Added GUITest, ManualSwitch and one method to Timeline --- .../era/mi/logic/components/ManualSwitch.java | 72 +++++ era.mi/src/era/mi/logic/tests/GUITest.java | 261 ++++++++++++++++++ .../src/era/mi/logic/timeline/Timeline.java | 40 +++ 3 files changed, 373 insertions(+) create mode 100644 era.mi/src/era/mi/logic/components/ManualSwitch.java create mode 100644 era.mi/src/era/mi/logic/tests/GUITest.java diff --git a/era.mi/src/era/mi/logic/components/ManualSwitch.java b/era.mi/src/era/mi/logic/components/ManualSwitch.java new file mode 100644 index 00000000..5e6a00c1 --- /dev/null +++ b/era.mi/src/era/mi/logic/components/ManualSwitch.java @@ -0,0 +1,72 @@ +package era.mi.logic.components; + +import java.util.List; + +import era.mi.logic.Bit; +import era.mi.logic.wires.WireArray; +import era.mi.logic.wires.WireArray.WireArrayInput; + +/** + * This class models a simple on/off (ONE/ZERO) switch for user interaction. + * + * @author Christian Femers + * + */ +public final class ManualSwitch implements Component +{ + private WireArray output; + private WireArrayInput outputI; + private boolean isOn; + + public ManualSwitch(WireArray output) + { + if(output.length != 1) + throw new IllegalArgumentException("Switch output can be only a single wire"); + this.output = output; + this.outputI = output.createInput(); + } + + public void switchOn() + { + setState(true); + } + + public void switchOff() + { + setState(false); + } + + public void toggle() + { + setState(!isOn); + } + + public void setState(boolean isOn) + { + if(this.isOn == isOn) + return; + this.isOn = isOn; + outputI.feedSignals(getValue()); + } + + public boolean isOn() + { + return isOn; + } + + public Bit getValue() + { + return isOn ? Bit.ONE : Bit.ZERO; + } + + @Override + public List getAllInputs() { + return List.of(); + } + + @Override + public List getAllOutputs() { + return List.of(output); + } + +} diff --git a/era.mi/src/era/mi/logic/tests/GUITest.java b/era.mi/src/era/mi/logic/tests/GUITest.java new file mode 100644 index 00000000..adc1b21c --- /dev/null +++ b/era.mi/src/era/mi/logic/tests/GUITest.java @@ -0,0 +1,261 @@ +package era.mi.logic.tests; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.WindowConstants; + +import era.mi.logic.Simulation; +import era.mi.logic.components.ManualSwitch; +import era.mi.logic.components.gates.NotGate; +import era.mi.logic.components.gates.OrGate; +import era.mi.logic.timeline.Timeline.ExecutionResult; +import era.mi.logic.wires.WireArray; + +public class GUITest extends JPanel { + + private static final long serialVersionUID = 1L; + + private static final int WIRE_DELAY = 40; + private static final int OR_DELAY = 100; + private static final int NOT_DELAY = 100; + + WireArray r = new WireArray(1, WIRE_DELAY); + WireArray s = new WireArray(1, WIRE_DELAY); + WireArray t1 = new WireArray(1, WIRE_DELAY); + WireArray t2 = new WireArray(1, WIRE_DELAY); + WireArray q = new WireArray(1, WIRE_DELAY); + WireArray nq = new WireArray(1, WIRE_DELAY); + + ManualSwitch rIn = new ManualSwitch(r); + ManualSwitch sIn = new ManualSwitch(s); + + OrGate or1 = new OrGate(OR_DELAY, t2, r, nq); + OrGate or2 = new OrGate(OR_DELAY, t1, s, q); + NotGate not1 = new NotGate(NOT_DELAY, t2, q); + NotGate not2 = new NotGate(NOT_DELAY, t1, nq); + + Map switchMap = new HashMap<>(); + + int height; + int width; + boolean sizeChanged; + + public GUITest() { + addMouseListener(new MouseListener() { + + @Override + public void mouseReleased(MouseEvent e) { + for (Entry dim : switchMap.entrySet()) { + if (dim.getValue().contains(e.getPoint())) { + dim.getKey().switchOff(); + repaint(); + } + } + } + + @Override + public void mousePressed(MouseEvent e) { + for (Entry dim : switchMap.entrySet()) { + if (dim.getValue().contains(e.getPoint())) { + dim.getKey().switchOn(); + repaint(); + } + } + } + + @Override + public void mouseExited(MouseEvent e) { + // none + } + + @Override + public void mouseEntered(MouseEvent e) { + // none + } + + @Override + public void mouseClicked(MouseEvent e) { + // If you want toggle buttons, use this code instead +// for (Entry dim : switchMap.entrySet()) { +// if (dim.getValue().contains(e.getPoint())) { +// dim.getKey().toggle(); +// repaint(); +// } +// } + } + }); + } + + @Override + public void paint(Graphics some_g) { + super.paint(some_g); + Graphics2D g = ((Graphics2D) some_g); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); + + checkSizeChange(); + adaptFont(g); + + drawWire(g, r, "r", 2, 9, 4, 9); + + drawWire(g, s, "s", 2, 3, 4, 3); + + drawWire(g, t2, "t2", 5, 8.5, 6, 8.5); + + drawWire(g, t1, "t1", 5, 3.5, 6, 3.5); + + drawWire(g, q, "q", 7, 8.5, 9, 8.5); + + drawWire(g, nq, "nq", 7, 3.5, 9, 3.5); + + drawWire(g, q, "", 7.5, 8.5, 7.5, 7.5); + drawWire(g, q, "", 7.5, 7.5, 3, 4.5); + drawWire(g, q, "", 3, 4.5, 3, 4); + drawWire(g, q, "q", 3, 4, 4, 4); + + drawWire(g, nq, "", 7.5, 3.5, 7.5, 4.5); + drawWire(g, nq, "", 7.5, 4.5, 3, 7.5); + drawWire(g, nq, "", 3, 7.5, 3, 8); + drawWire(g, nq, "nq", 3, 8, 4, 8); + + drawSquare(g, 4, 8, "OR"); + drawSquare(g, 4, 3, "OR"); + + drawSquare(g, 6, 8, "NOT"); + drawSquare(g, 6, 3, "NOT"); + + drawSwitch(g, rIn, "Switch R", 0.5, 8.25, 2, 9.75); + drawSwitch(g, sIn, "Switch S", 0.5, 2.25, 2, 3.75); + + drawString(g, "Hint: drag the cursor out of the pressed switch to keep it's state", 5, 0, 0.0, 1.0); + } + + private void checkSizeChange() { + sizeChanged = height != getHeight() || width != getWidth(); + if (sizeChanged) { + height = getHeight(); + width = getWidth(); + } + } + + private void adaptFont(Graphics g) { + g.setFont(g.getFont().deriveFont(Math.min(height, width) / 40f)); + } + + private void drawString(Graphics g, String s, int x, int y, double anchorX, double anchorY) { + int h = g.getFontMetrics().getAscent(); + int w = g.getFontMetrics().stringWidth(s); + g.drawString(s, x - (int) (w * anchorX), y + (int) (h * anchorY)); + } + + private void drawWire(Graphics g, WireArray wa, String name, double x1, double y1, double x2, double y2) { + setTo(g, wa); + g.drawLine(gX(x1), gY(y1), gX(x2), gY(y2)); + drawString(g, name, (gX(x1) + gX(x2)) / 2, (gY(y1) + gY(y2)) / 2 - 5, 0, 0); + } + + private void drawSquare(Graphics g, int posX, int posY, String text) { + int x1 = gX(posX) - 5; + int x2 = gX(posX + 1) + 5; + int y1 = gY(posY) - 5; + int y2 = gY(posY + 1) + 5; + + g.setColor(Color.WHITE); + g.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1); + setBlack(g); + g.drawRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1); + drawString(g, text, (x1 + x2) / 2, (y1 + y2) / 2, 0.5, 0.5); + + } + + private void drawSwitch(Graphics g, ManualSwitch ms, String text, double posX1, double posY1, double posX2, + double posY2) { + int x1 = gX(posX1) - 5; + int x2 = gX(posX2) + 5; + int y1 = gY(posY1) - 5; + int y2 = gY(posY2) + 5; + + if (sizeChanged) { + Rectangle r = new Rectangle(x1, y1, x2 - x1, y2 - y1); + switchMap.put(ms, r); + } + + g.setColor(ms.isOn() ? Color.getHSBColor(.3f, .5f, 1f) : Color.WHITE); + g.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1); + setBlack(g); + g.drawRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1); + drawString(g, text, (x1 + x2) / 2, (y1 + y2) / 2, 0.5, 0.5); + } + + private static void setBlack(Graphics g) { + g.setColor(Color.BLACK); + } + + private static void setTo(Graphics g, WireArray wa) { + switch (wa.getValue()) { + case ONE: + g.setColor(Color.GREEN); + break; + case X: + g.setColor(Color.RED); + break; + case Z: + g.setColor(Color.DARK_GRAY); + break; + case ZERO: + g.setColor(Color.BLACK); + break; + default: + throw new IllegalArgumentException(); + } + } + + private int gY(double pos) { + return (int) (pos * height / 11); + } + + private int gX(double pos) { + return (int) (pos * width / 11) + 50; + } + + public static void main(String[] args) { + JFrame f = new JFrame("Test circuit 1.0.0"); + GUITest gt = new GUITest(); + f.add(gt); + f.setSize(800, 600); + f.setLocation(500, 400); + f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + f.setVisible(true); + + long begin = System.currentTimeMillis(); + + long lastFrame = begin; + long updateT = 16; + + while (f.isVisible()) { + ExecutionResult er = Simulation.TIMELINE.executeUpTo((lastFrame - begin) * 3, lastFrame + 14); +// if (Simulation.TIMELINE.hasNext()) +// Simulation.TIMELINE.executeNext(); + if (er != ExecutionResult.NOTHING_DONE) + gt.repaint(12); + try { + Thread.sleep(Math.max(16 - System.currentTimeMillis() + lastFrame, 0)); + } catch (Exception e) { + e.printStackTrace(); + } + lastFrame = System.currentTimeMillis(); + } + } +} diff --git a/era.mi/src/era/mi/logic/timeline/Timeline.java b/era.mi/src/era/mi/logic/timeline/Timeline.java index 20bb4009..8b3c3255 100644 --- a/era.mi/src/era/mi/logic/timeline/Timeline.java +++ b/era.mi/src/era/mi/logic/timeline/Timeline.java @@ -39,6 +39,41 @@ public class Timeline while (hasNext()) executeNext(); } + + /** + * Executes all events up to a given simulation timestamp. The simulation + * process can be constrained by a real world timestamp. + * + * @param timestamp the simulation timestamp up to which the events will be + * processed + * @param stopMillis the System.currentTimeMillis() when simulation definitely + * needs to stop. + * @return if it was possible to fulfil the goal in the given real world time. + * @author Christian Femers + */ + public ExecutionResult executeUpTo(long timestamp, long stopMillis) + { + if (events.isEmpty()) + { + currentTime = timestamp; + return ExecutionResult.NOTHING_DONE; + } + int checkStop = 0; + InnerEvent first = events.peek(); + while (first != null && first.getTiming() <= timestamp) + { + events.remove(); + currentTime = first.getTiming(); + first.run(); + // Don't check after every run + checkStop = (checkStop + 1) % 10; + if (checkStop == 0 && System.currentTimeMillis() >= stopMillis) + return ExecutionResult.RAN_OUT_OF_TIME; + first = events.peek(); + } + currentTime = timestamp; + return ExecutionResult.DONE_IN_TIME; + } public long getSimulationTime() { @@ -108,4 +143,9 @@ public class Timeline { return ticks; //TODO: Alter this when it has been determined how ticks should relate to real time. } + + public enum ExecutionResult + { + NOTHING_DONE, DONE_IN_TIME, RAN_OUT_OF_TIME + } } \ No newline at end of file -- 2.17.1