From e83dd45d71f01ab1a8b29693c75612d9606b0e51 Mon Sep 17 00:00:00 2001 From: hneemann Date: Thu, 22 Nov 2018 18:21:52 +0100 Subject: [PATCH] added some editing function --- .../neemann/digital/draw/graphics/Vector.java | 4 + .../digital/draw/graphics/VectorFloat.java | 15 ++ .../draw/graphics/VectorInterface.java | 5 + src/main/java/de/neemann/digital/fsm/FSM.java | 50 ++++-- .../java/de/neemann/digital/fsm/FSMDemos.java | 144 ++++++++++++++++++ .../java/de/neemann/digital/fsm/Movable.java | 4 +- .../java/de/neemann/digital/fsm/State.java | 22 ++- .../de/neemann/digital/fsm/Transition.java | 52 +++++-- .../neemann/digital/fsm/gui/FSMComponent.java | 91 +++++++++++ .../de/neemann/digital/fsm/gui/FSMDialog.java | 109 ++++++------- .../java/de/neemann/digital/gui/Main.java | 13 +- .../gui/components/table/TableDialog.java | 5 +- 12 files changed, 428 insertions(+), 86 deletions(-) create mode 100644 src/main/java/de/neemann/digital/fsm/FSMDemos.java diff --git a/src/main/java/de/neemann/digital/draw/graphics/Vector.java b/src/main/java/de/neemann/digital/draw/graphics/Vector.java index 11c71c7de..98fcbe89d 100644 --- a/src/main/java/de/neemann/digital/draw/graphics/Vector.java +++ b/src/main/java/de/neemann/digital/draw/graphics/Vector.java @@ -252,4 +252,8 @@ public class Vector implements VectorInterface { return (float) Math.sqrt(x * x + y * y); } + @Override + public VectorFloat toFloat() { + return new VectorFloat(x, y); + } } diff --git a/src/main/java/de/neemann/digital/draw/graphics/VectorFloat.java b/src/main/java/de/neemann/digital/draw/graphics/VectorFloat.java index 6d90a9b6e..39dedb714 100644 --- a/src/main/java/de/neemann/digital/draw/graphics/VectorFloat.java +++ b/src/main/java/de/neemann/digital/draw/graphics/VectorFloat.java @@ -97,6 +97,16 @@ public class VectorFloat implements VectorInterface { return new VectorFloat(x * a, y * a); } + /** + * Returns the sclar product + * + * @param p the other vector + * @return the scalar procuct + */ + public float mul(VectorFloat p) { + return x * p.x + y * p.y; + } + /** * Creates a new vector which has the value this/d * @@ -131,4 +141,9 @@ public class VectorFloat implements VectorInterface { public float len() { return (float) Math.sqrt(x * x + y * y); } + + @Override + public VectorFloat toFloat() { + return this; + } } diff --git a/src/main/java/de/neemann/digital/draw/graphics/VectorInterface.java b/src/main/java/de/neemann/digital/draw/graphics/VectorInterface.java index fc8c5efdf..99b366d7b 100644 --- a/src/main/java/de/neemann/digital/draw/graphics/VectorInterface.java +++ b/src/main/java/de/neemann/digital/draw/graphics/VectorInterface.java @@ -74,6 +74,11 @@ public interface VectorInterface { */ Vector round(); + /** + * @return returns a float vector + */ + VectorFloat toFloat(); + /** * @return the length of the vector */ diff --git a/src/main/java/de/neemann/digital/fsm/FSM.java b/src/main/java/de/neemann/digital/fsm/FSM.java index 00203b144..a35c3b76c 100644 --- a/src/main/java/de/neemann/digital/fsm/FSM.java +++ b/src/main/java/de/neemann/digital/fsm/FSM.java @@ -8,11 +8,13 @@ package de.neemann.digital.fsm; import de.neemann.digital.analyse.TruthTable; import de.neemann.digital.analyse.expression.*; import de.neemann.digital.draw.graphics.Graphic; +import de.neemann.digital.draw.graphics.Vector; import de.neemann.digital.draw.graphics.VectorFloat; import de.neemann.digital.lang.Lang; import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.TreeSet; /** @@ -42,7 +44,8 @@ public class FSM { * @return this for chained calls */ public FSM add(State state) { - state.setNumber(states.size()); + if (state.getNumber() < 0) + state.setNumber(states.size()); states.add(state); return this; } @@ -101,14 +104,14 @@ public class FSM { for (State s : states) if (s.getName().equals(name)) return s; - throw new FinitStateMachineException("State " + name + " not found!"); + throw new FinitStateMachineException(Lang.get("err_fsmState_N_notFound!", name)); } private State findState(int number) throws FinitStateMachineException { for (State s : states) if (s.getNumber() == number) return s; - throw new FinitStateMachineException("State " + number + " not found!"); + throw new FinitStateMachineException(Lang.get("err_fsmState_N_notFound!", number)); } /** @@ -124,6 +127,13 @@ public class FSM { return this; } + /** + * @return the states + */ + public List getStates() { + return states; + } + /** * Draws the FSM * @@ -139,14 +149,19 @@ public class FSM { /** * Moved the elements * - * @param dt the time step + * @param dt the time step + * @param moveStates if true also states are moved + * @param except element which is fixed */ - public void move(int dt) { - for (State s : states) - s.move(dt); + public void move(int dt, boolean moveStates, Movable except) { + calculateForces(); + if (moveStates) + for (State s : states) + if (s != except) + s.move(dt); for (Transition t : transitions) - t.move(dt); - + if (t != except) + t.move(dt); } /** @@ -287,4 +302,21 @@ public class FSM { while ((1 << n) <= maxNumber) n++; return n; } + + /** + * Returns the element at the given position + * @param pos the position + * @return the element or null + */ + public Movable getMovable(Vector pos) { + for (State s : states) + if (s.matches(pos)) + return s; + + for (Transition t : transitions) + if (t.matches(pos)) + return t; + + return null; + } } diff --git a/src/main/java/de/neemann/digital/fsm/FSMDemos.java b/src/main/java/de/neemann/digital/fsm/FSMDemos.java new file mode 100644 index 000000000..a451a2c36 --- /dev/null +++ b/src/main/java/de/neemann/digital/fsm/FSMDemos.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2018 Helmut Neemann. + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ +package de.neemann.digital.fsm; + +import de.neemann.digital.analyse.expression.Expression; +import de.neemann.digital.analyse.parser.Parser; + +import static de.neemann.digital.analyse.expression.Not.not; +import static de.neemann.digital.analyse.expression.Variable.v; + +/** + * Provides some demo fsm's + */ +public final class FSMDemos { + + private FSMDemos() { + } + + private static Expression e(String s) { + try { + return new Parser(s).parse().get(0); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Creates a debounced rotary switch decoder + * + * @return the fsm + */ + public static FSM createRotDecoder() { + State top = new State("top"); + State topSetLeft = new State("topSetLeft").val("L", 1); + State topSetRight = new State("topSetRight").val("R", 1); + State leftA = new State("leftA"); + State leftB = new State("leftB"); + State bottom = new State("bottom"); + State bottomSetLeft = new State("bottomSetRight").val("R", 1); + State bottomSetRight = new State("bottomSetLeft").val("L", 1); + State rightA = new State("rightA"); + State rightB = new State("rightB"); + return new FSM(top, topSetLeft, leftA, leftB, bottomSetLeft, bottom, bottomSetRight, rightB, rightA, topSetRight) + .transition(top, leftA, e("A & !B")) + .transition(top, rightA, e("!A & B")) + .transition(topSetLeft, top, null) + .transition(topSetRight, top, null) + + .transition(rightA, top, e("!A & !B")) + .transition(rightB, topSetRight, e("!A & !B")) + .transition(leftA, top, e("!A & !B")) + .transition(leftB, topSetLeft, e("!A & !B")) + + .transition(bottom, leftB, e("A & !B")) + .transition(bottom, rightB, e("!A & B")) + .transition(bottomSetLeft, bottom, null) + .transition(bottomSetRight, bottom, null) + + .transition(rightB, bottom, e("A & B")) + .transition(rightA, bottomSetRight, e("A & B")) + .transition(leftB, bottom, e("A & B")) + .transition(leftA, bottomSetLeft, e("A & B")); + } + + /** + * Creates a counter + * + * @param n the number of states + * @return the fsm + */ + public static FSM counter(int n) { + FSM fsm = new FSM(); + State last = null; + for (int i = 0; i < n; i++) { + State s = new State(Integer.toString(i)).setNumber(i); + fsm.add(s); + if (last != null) + fsm.transition(last, s, null); + last = s; + } + fsm.transition(last, fsm.getStates().get(0), null); + + return fsm; + } + + /** + * Creates a traffic light fsm + * + * @return the fsm + */ + public static FSM trafficLight() { + State red = new State("red").setNumber(0).val("R", 1); + State redYellow = new State("red/yellow").setNumber(1).val("R", 1).val("Y", 1); + State green = new State("green").setNumber(2).val("G", 1); + State yellow = new State("yellow").setNumber(3).val("Y", 1); + return new FSM(red, redYellow, green, yellow) + .transition(red, redYellow, not(v("Stop"))) + .transition(redYellow, green, null) + .transition(green, yellow, null) + .transition(yellow, red, null); + } + + /** + * Creates a traffic light fsm + * + * @return the fsm + */ + public static FSM trafficLightMedwedev() { + State init = new State("init").setNumber(0); + State red = new State("red").setNumber(1).val("R", 1); + State redYellow = new State("red/yellow").setNumber(3).val("R", 1).val("Y", 1); + State green = new State("green").setNumber(4).val("G", 1); + State yellow = new State("yellow").setNumber(2).val("Y", 1); + return new FSM(init, red, redYellow, green, yellow) + .transition(init, red, null) + .transition(red, redYellow, not(v("Stop"))) + .transition(redYellow, green, null) + .transition(green, yellow, null) + .transition(yellow, red, null); + } + + /** + * Creates a traffic light fsm + * + * @return the fsm + */ + public static FSM selCounter() { + State s0 = new State("s0").setNumber(0); + State s1 = new State("s1").setNumber(1); + State s2 = new State("s2").setNumber(2); + State s3 = new State("s3").setNumber(3); + return new FSM(s0, s1, s2, s3) + .transition(s0, s1, null) + .transition(s0, s0, e("!T0 !T1")) + .transition(s1, s2, null) + .transition(s1, s0, e("T0 !T1")) + .transition(s2, s3, null) + .transition(s2, s0, e("!T0 T1")) + .transition(s3, s0, null); + } +} diff --git a/src/main/java/de/neemann/digital/fsm/Movable.java b/src/main/java/de/neemann/digital/fsm/Movable.java index c6ca85b97..561f7556b 100644 --- a/src/main/java/de/neemann/digital/fsm/Movable.java +++ b/src/main/java/de/neemann/digital/fsm/Movable.java @@ -117,8 +117,8 @@ public class Movable { * @param dt the time step */ public void move(int dt) { - speed = speed.add(force.mul(0.2f)); - position = position.add(speed); + speed = speed.add(force.mul(dt / 200f)); + setPos(position.add(speed.mul(dt / 1000f))); speed = speed.mul(0.7f); } diff --git a/src/main/java/de/neemann/digital/fsm/State.java b/src/main/java/de/neemann/digital/fsm/State.java index 15228dfe7..2b7b5dfb6 100644 --- a/src/main/java/de/neemann/digital/fsm/State.java +++ b/src/main/java/de/neemann/digital/fsm/State.java @@ -16,9 +16,9 @@ import java.util.TreeMap; */ public class State extends Movable { private static final int RAD = 70; - private static final float REACH = 500; + private static final float REACH = 2000; - private int number; + private int number = -1; private String name; private int radius; private TreeMap values; @@ -86,6 +86,10 @@ public class State extends Movable { public void drawTo(Graphic gr) { VectorInterface rad = new Vector(RAD, RAD); gr.drawCircle(getPos().sub(rad), getPos().add(rad), Style.NORMAL); + if (number == 0) { + VectorInterface rad2 = new Vector(RAD - Style.MAXLINETHICK * 2, RAD - Style.MAXLINETHICK * 2); + gr.drawCircle(getPos().sub(rad2), getPos().add(rad2), Style.THIN); + } Vector delta = new Vector(0, Style.NORMAL.getFontSize()); VectorFloat pos = getPos().add(delta.mul(-1)); @@ -113,9 +117,11 @@ public class State extends Movable { * Sets the number of the state * * @param number the number + * @return this for chained calls */ - public void setNumber(int number) { + public State setNumber(int number) { this.number = number; + return this; } /** @@ -131,4 +137,14 @@ public class State extends Movable { public TreeMap getValues() { return values; } + + /** + * Returns true if the position matches the state + * + * @param pos the position + * @return true if pos inside of the state + */ + public boolean matches(Vector pos) { + return pos.sub(getPos()).len() <= radius; + } } diff --git a/src/main/java/de/neemann/digital/fsm/Transition.java b/src/main/java/de/neemann/digital/fsm/Transition.java index 3e7408f51..81067f848 100644 --- a/src/main/java/de/neemann/digital/fsm/Transition.java +++ b/src/main/java/de/neemann/digital/fsm/Transition.java @@ -58,12 +58,14 @@ public class Transition extends Movable { */ public void calcForce(float preferredDist, List states, List transitions) { - VectorFloat dir = fromState.getPos().sub(toState.getPos()); - float len = dir.len(); - float d = len - preferredDist; - dir = dir.mul(EXPANSION_TRANS * d); - toState.addToForce(dir); - fromState.addToForce(dir.mul(-1)); + if (fromState != toState) { + VectorFloat dir = fromState.getPos().sub(toState.getPos()); + float len = dir.len(); + float d = len - preferredDist; + dir = dir.mul(EXPANSION_TRANS * d); + toState.addToForce(dir); + fromState.addToForce(dir.mul(-1)); + } resetForce(); VectorFloat center = fromState.getPos().add(toState.getPos()).mul(0.5f); @@ -74,8 +76,19 @@ public class Transition extends Movable { for (Transition t : transitions) if (t != this) - addRepulsiveInv(t.getPos(), 1000); + addRepulsive(t.getPos(), 400); + } + @Override + public void setPos(VectorFloat position) { + if (fromState != toState) { + VectorFloat dist = fromState.getPos().sub(toState.getPos()); + VectorFloat p = position.sub(fromState.getPos()); + VectorFloat n = new VectorFloat(dist.getYFloat(), -dist.getXFloat()).norm(); + float l = p.mul(n); + super.setPos(fromState.getPos().sub(dist.mul(0.5f)).add(n.mul(l))); + } else + super.setPos(position); } /** @@ -84,11 +97,13 @@ public class Transition extends Movable { * @param gr the Graphic instance to draw to */ public void drawTo(Graphic gr) { - VectorFloat difFrom = getPos().sub(fromState.getPos()).norm().mul(fromState.getRadius()); - VectorFloat difTo = getPos().sub(toState.getPos()).norm().mul(toState.getRadius()); + VectorFloat difFrom = getPos().sub(fromState.getPos()).norm().mul(fromState.getRadius() + Style.MAXLINETHICK); + VectorFloat difTo = getPos().sub(toState.getPos()).norm().mul(toState.getRadius() + Style.MAXLINETHICK + 2); + VectorFloat difToTip = getPos().sub(toState.getPos()).norm().mul(toState.getRadius() + Style.MAXLINETHICK); final VectorFloat start = fromState.getPos().add(difFrom); final VectorFloat end = toState.getPos().add(difTo); + final VectorFloat arrowTip = toState.getPos().add(difToTip); Polygon p = new Polygon(false) .add(start) @@ -96,13 +111,12 @@ public class Transition extends Movable { final Style arrowStyle = Style.SHAPE_PIN; gr.drawPolygon(p, arrowStyle); -// gr.drawLine(start, getPos(), Style.THIN); -// gr.drawLine(getPos(), end, Style.THIN); - // arrow VectorFloat lot = new VectorFloat(difTo.getYFloat(), -difTo.getXFloat()).mul(0.5f); - gr.drawLine(end, end.add(difTo.add(lot).mul(0.2f)), arrowStyle); - gr.drawLine(end, end.add(difTo.sub(lot).mul(0.2f)), arrowStyle); + gr.drawPolygon(new Polygon(false) + .add(end.add(difTo.add(lot).mul(0.2f))) + .add(arrowTip) + .add(end.add(difTo.sub(lot).mul(0.2f))), arrowStyle); if (condition != null) { String format; try { @@ -142,4 +156,14 @@ public class Transition extends Movable { public State getTargetState() { return toState; } + + /** + * Gives true if the position matches the transition. + * + * @param pos the position + * @return true if pos matches the transition + */ + public boolean matches(Vector pos) { + return pos.sub(getPos()).len() < 50; + } } diff --git a/src/main/java/de/neemann/digital/fsm/gui/FSMComponent.java b/src/main/java/de/neemann/digital/fsm/gui/FSMComponent.java index ef6d2ec07..e41607ead 100644 --- a/src/main/java/de/neemann/digital/fsm/gui/FSMComponent.java +++ b/src/main/java/de/neemann/digital/fsm/gui/FSMComponent.java @@ -5,28 +5,40 @@ */ package de.neemann.digital.fsm.gui; +import de.neemann.digital.core.element.ElementAttributes; +import de.neemann.digital.core.element.Key; +import de.neemann.digital.core.element.Keys; import de.neemann.digital.draw.graphics.GraphicMinMax; import de.neemann.digital.draw.graphics.GraphicSwing; import de.neemann.digital.draw.graphics.Style; import de.neemann.digital.draw.graphics.Vector; import de.neemann.digital.fsm.FSM; +import de.neemann.digital.fsm.Movable; +import de.neemann.digital.fsm.State; +import de.neemann.digital.gui.components.AttributeDialog; +import de.neemann.gui.Mouse; import javax.swing.*; import java.awt.*; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; +import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; +import java.util.ArrayList; /** * The component to show the fsm */ public class FSMComponent extends JComponent { + private Mouse mouse = Mouse.getMouse(); + private boolean isManualScale; private AffineTransform transform = new AffineTransform(); + private Movable elementMoved; private FSM fsm; /** @@ -49,6 +61,55 @@ public class FSMComponent extends JComponent { repaint(); }); + MouseAdapter mouseListener = new MouseAdapter() { + private Vector delta; + private Vector pos; + + @Override + public void mousePressed(MouseEvent e) { + pos = new Vector(e.getX(), e.getY()); + if (mouse.isPrimaryClick(e)) { + final Vector posVector = getPosVector(e); + elementMoved = fsm.getMovable(posVector); + if (elementMoved != null) + delta = posVector.sub(elementMoved.getPos()); + } + } + + @Override + public void mouseReleased(MouseEvent mouseEvent) { + elementMoved = null; + } + + @Override + public void mouseClicked(MouseEvent mouseEvent) { + if (mouse.isSecondaryClick(mouseEvent)) { + final Vector posVector = getPosVector(mouseEvent); + Movable elementClicked = fsm.getMovable(posVector); + if (elementClicked == null) + createNewState(posVector, new Point(mouseEvent.getX(), mouseEvent.getY())); + } + } + + @Override + public void mouseDragged(MouseEvent e) { + if (elementMoved == null) { + Vector newPos = new Vector(e.getX(), e.getY()); + Vector delta = newPos.sub(pos); + double s = transform.getScaleX(); + transform.translate(delta.x / s, delta.y / s); + pos = newPos; + isManualScale = true; + repaint(); + } else { + elementMoved.setPos(getPosVector(e).sub(delta).toFloat()); + } + } + }; + addMouseMotionListener(mouseListener); + addMouseListener(mouseListener); + + addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent componentEvent) { @@ -60,6 +121,26 @@ public class FSMComponent extends JComponent { setPreferredSize(new Dimension(600, 600)); } + private void createNewState(Vector posVector, Point point) { + Key number = new Key.KeyInteger("stateNum", 0); + Key values = new Key<>("stateValues", ""); + ArrayList list = new ArrayList(); + list.add(Keys.LABEL); + list.add(number); + list.add(values); + ElementAttributes attr = new ElementAttributes(); + SwingUtilities.convertPointToScreen(point, this); + AttributeDialog ad = new AttributeDialog(SwingUtilities.getWindowAncestor(this), point, list, attr); + ElementAttributes newAttr = ad.showDialog(); + if (newAttr!=null) { + State s = new State(newAttr.get(Keys.LABEL)) + .setPosition(posVector.toFloat()) + .setNumber(newAttr.get(number)); + fsm.add(s); + repaint(); + } + } + private Vector getPosVector(MouseEvent e) { return getPosVector(e.getX(), e.getY()); } @@ -104,7 +185,13 @@ public class FSMComponent extends JComponent { transform = newTrans; repaint(); } + } + /** + * @return the element picked by the mouse + */ + public Movable getElementMoved() { + return elementMoved; } @Override @@ -115,6 +202,10 @@ public class FSMComponent extends JComponent { graphics.fillRect(0, 0, getWidth(), getHeight()); Graphics2D gr2 = (Graphics2D) graphics; + gr2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + gr2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + gr2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); + gr2.transform(transform); GraphicSwing gr = new GraphicSwing(gr2, 1); fsm.drawTo(gr); diff --git a/src/main/java/de/neemann/digital/fsm/gui/FSMDialog.java b/src/main/java/de/neemann/digital/fsm/gui/FSMDialog.java index 7c81f8cc7..616426c3e 100644 --- a/src/main/java/de/neemann/digital/fsm/gui/FSMDialog.java +++ b/src/main/java/de/neemann/digital/fsm/gui/FSMDialog.java @@ -5,21 +5,20 @@ */ package de.neemann.digital.fsm.gui; -import de.neemann.digital.analyse.expression.Expression; -import de.neemann.digital.analyse.parser.ParseException; -import de.neemann.digital.analyse.parser.Parser; import de.neemann.digital.draw.library.ElementLibrary; import de.neemann.digital.draw.shapes.ShapeFactory; import de.neemann.digital.fsm.FSM; -import de.neemann.digital.fsm.State; +import de.neemann.digital.fsm.FSMDemos; import de.neemann.digital.gui.components.table.TableDialog; +import de.neemann.digital.lang.Lang; +import de.neemann.gui.ErrorMessage; +import de.neemann.gui.ToolTipAction; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import java.io.IOException; /** * The dialog to show the FSM @@ -29,20 +28,27 @@ public class FSMDialog extends JDialog { private final FSM fsm; private final FSMComponent fsmComponent; private final Timer timer; + private final ElementLibrary library; + private boolean moveStates = false; /** * Creates a new instance * - * @param frame the parents frame - * @param fsm the fsm to visualize + * @param frame the parents frame + * @param givenFsm the fsm to visualize + * @param library the library used to show the table */ - public FSMDialog(Frame frame, FSM fsm) { - super(frame, "FSM"); + public FSMDialog(Frame frame, FSM givenFsm, ElementLibrary library) { + super(frame, Lang.get("fsm_title")); setDefaultCloseOperation(DISPOSE_ON_CLOSE); - this.fsm = fsm; + this.library = library; + if (givenFsm == null) + givenFsm = FSMDemos.createRotDecoder(); + + this.fsm = givenFsm; fsmComponent = new FSMComponent(fsm); - getContentPane().add(fsmComponent); + getContentPane().add(fsmComponent, BorderLayout.CENTER); pack(); setLocationRelativeTo(frame); @@ -51,8 +57,8 @@ public class FSMDialog extends JDialog { timer = new Timer(100, new AbstractAction() { @Override public void actionPerformed(ActionEvent actionEvent) { - fsm.calculateForces(); - fsm.move(100); + for (int i = 0; i < 100; i++) + fsm.move(10, moveStates, fsmComponent.getElementMoved()); repaint(); } }); @@ -65,56 +71,53 @@ public class FSMDialog extends JDialog { timer.stop(); } }); + + JMenuBar bar = new JMenuBar(); + + JMenu create = new JMenu(Lang.get("menu_fsm_create")); + bar.add(create); + create.add(new ToolTipAction(Lang.get("menu_fsm_create_table")) { + @Override + public void actionPerformed(ActionEvent actionEvent) { + try { + new TableDialog(FSMDialog.this, fsm.createTruthTable(), library, null).setVisible(true); + } catch (Exception e) { + new ErrorMessage(Lang.get("msg_fsmCantCreateTable")).addCause(e).show(FSMDialog.this); + } + } + }.createJMenuItem()); + + JToolBar toolBar = new JToolBar(); + + final JCheckBox moveCheck = new JCheckBox(Lang.get("fsm_move")); + moveCheck.addActionListener(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + moveStates = moveCheck.isSelected(); + } + }); + moveCheck.setSelected(moveStates); + toolBar.add(moveCheck); + getContentPane().add(toolBar, BorderLayout.PAGE_START); + + + setJMenuBar(bar); + } /** * A simple test method * * @param args the programs arguments - * @throws Exception Exception */ - public static void main(String[] args) throws Exception { - State top = new State("top"); - State topSetLeft = new State("topSetLeft").val("L", 1); - State topSetRight = new State("topSetRight").val("R", 1); - State leftA = new State("leftA"); - State leftB = new State("leftB"); - State bottom = new State("bottom"); - State bottomSetLeft = new State("bottomSetRight").val("R", 1); - State bottomSetRight = new State("bottomSetLeft").val("L", 1); - State rightA = new State("rightA"); - State rightB = new State("rightB"); - FSM fsm = new FSM(top, topSetLeft, leftA, leftB, bottomSetLeft, bottom, bottomSetRight, rightB, rightA, topSetRight) - .transition(top, leftA, e("A & !B")) - .transition(top, rightA, e("!A & B")) - .transition(topSetLeft, top, null) - .transition(topSetRight, top, null) + public static void main(String[] args) { - .transition(rightA, top, e("!A & !B")) - .transition(rightB, topSetRight, e("!A & !B")) - .transition(leftA, top, e("!A & !B")) - .transition(leftB, topSetLeft, e("!A & !B")) + FSM fsm = FSMDemos.selCounter(); - .transition(bottom, leftB, e("A & !B")) - .transition(bottom, rightB, e("!A & B")) - .transition(bottomSetLeft, bottom, null) - .transition(bottomSetRight, bottom, null) + ElementLibrary library = new ElementLibrary(); + new ShapeFactory(library); - .transition(rightB, bottom, e("A & B")) - .transition(rightA, bottomSetRight, e("A & B")) - .transition(leftB, bottom, e("A & B")) - .transition(leftA, bottomSetLeft, e("A & B")); + new FSMDialog(null, fsm, library).setVisible(true); - - ElementLibrary lib = new ElementLibrary(); - ShapeFactory shapeFactory = new ShapeFactory(lib); - new TableDialog(null, fsm.createTruthTable(), lib, shapeFactory, null).setVisible(true); - - //new FSMDialog(null, fsm).setVisible(true); - - } - - private static Expression e(String s) throws IOException, ParseException { - return new Parser(s).parse().get(0); } } diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java index 51e3bc0b0..298bf547b 100644 --- a/src/main/java/de/neemann/digital/gui/Main.java +++ b/src/main/java/de/neemann/digital/gui/Main.java @@ -28,6 +28,7 @@ import de.neemann.digital.draw.model.ModelCreator; import de.neemann.digital.draw.model.RealTimeClock; import de.neemann.digital.draw.shapes.Drawable; import de.neemann.digital.draw.shapes.ShapeFactory; +import de.neemann.digital.fsm.gui.FSMDialog; import de.neemann.digital.gui.components.*; import de.neemann.digital.gui.components.data.GraphDialog; import de.neemann.digital.gui.components.expression.ExpressionDialog; @@ -1094,7 +1095,6 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS new TableDialog(Main.this, new ModelAnalyser(model).analyse(), library, - shapeFactory, getBaseFileName()) .setVisible(true); ensureModelIsStopped(); @@ -1114,7 +1114,7 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS @Override public void actionPerformed(ActionEvent e) { TruthTable tt = new TruthTable(3).addResult(); - new TableDialog(Main.this, tt, library, shapeFactory, getBaseFileName()).setVisible(true); + new TableDialog(Main.this, tt, library, getBaseFileName()).setVisible(true); ensureModelIsStopped(); } } @@ -1130,6 +1130,15 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS .setToolTip(Lang.get("menu_expression_tt")) .createJMenuItem()); + analyse.add(new ToolTipAction(Lang.get("menu_fsm")) { + @Override + public void actionPerformed(ActionEvent e) { + new FSMDialog(Main.this, null, library).setVisible(true); + } + } + .setToolTip(Lang.get("menu_fsm_tt")) + .createJMenuItem()); + } private void orderMeasurements() { diff --git a/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java b/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java index 7c0babd4c..89c420946 100644 --- a/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java +++ b/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java @@ -96,13 +96,12 @@ public class TableDialog extends JDialog { * @param parent the parent frame * @param truthTable the table to show * @param library the library to use - * @param shapeFactory the shape factory * @param filename the file name used to create the names of the created files */ - public TableDialog(JFrame parent, TruthTable truthTable, ElementLibrary library, ShapeFactory shapeFactory, File filename) { + public TableDialog(Window parent, TruthTable truthTable, ElementLibrary library, File filename) { super(parent, Lang.get("win_table")); this.library = library; - this.shapeFactory = shapeFactory; + this.shapeFactory = library.getShapeFactory(); this.filename = filename; setDefaultCloseOperation(DISPOSE_ON_CLOSE); kvMap = new KarnaughMapDialog(this, (boolTable, row) -> model.incValue(boolTable, row));