From 6fec5c702384e65c9a7595b62aedab29f0db9964 Mon Sep 17 00:00:00 2001 From: hneemann Date: Sat, 8 Dec 2018 13:37:04 +0100 Subject: [PATCH] more consistent moving of fsm elements --- src/main/java/de/neemann/digital/fsm/FSM.java | 123 ++++++++++++++---- .../java/de/neemann/digital/fsm/Movable.java | 28 +++- .../java/de/neemann/digital/fsm/State.java | 4 +- .../de/neemann/digital/fsm/Transition.java | 17 ++- .../neemann/digital/fsm/gui/FSMComponent.java | 6 +- .../de/neemann/digital/fsm/gui/FSMFrame.java | 20 +-- 6 files changed, 146 insertions(+), 52 deletions(-) diff --git a/src/main/java/de/neemann/digital/fsm/FSM.java b/src/main/java/de/neemann/digital/fsm/FSM.java index 56488aa6d..43cbf5e12 100644 --- a/src/main/java/de/neemann/digital/fsm/FSM.java +++ b/src/main/java/de/neemann/digital/fsm/FSM.java @@ -25,6 +25,24 @@ import java.util.List; */ public class FSM { + /** + * The moving state of the fsm. + */ + public enum MovingState { + /** + * no elements are moving + */ + STOP, + /** + * only transitions are moving + */ + TRANSITIONS, + /** + * transitions and states are moving + */ + BOTH + } + private ArrayList states; private ArrayList transitions; private transient boolean modified; @@ -33,6 +51,7 @@ public class FSM { private transient Transition initialTransition; private transient int activeState = -1; private transient File file; + private transient MovingState state = MovingState.STOP; /** * Creates a proper configured XStream instance @@ -131,6 +150,30 @@ public class FSM { add(s); } + /** + * Sets the moving state of this FSM + * + * @param state the state + */ + public void setMovingState(MovingState state) { + if (this.state != state) { + this.state = state; + if (state != MovingState.BOTH) + for (State s : states) + s.toRaster(); + } + } + + /** + * @return the moving state of this FSM + */ + public MovingState getMovingState() { + if (state == null) + state = MovingState.STOP; + return state; + } + + /** * Adds a state to the FSM * @@ -293,19 +336,20 @@ public class FSM { /** * Moved the elements * - * @param dt the time step - * @param moveStates if true also states are moved - * @param except element which is fixed + * @param dt the time step + * @param except element which is fixed */ - 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) - if (t != except) - t.move(dt); + public void move(int dt, Movable except) { + if (state != MovingState.STOP) { + calculateForces(); + if (state == MovingState.BOTH) + for (State s : states) + if (s != except) + s.move(dt); + for (Transition t : transitions) + if (t != except) + t.move(dt); + } } /** @@ -353,23 +397,31 @@ public class FSM { * @return the element or null */ public Movable getMovable(Vector pos) { + Movable found = null; + float dist = Float.MAX_VALUE; for (Transition t : transitions) - if (t.matches(pos)) - return t; + if (t.matches(pos)) { + float d = pos.sub(t.getPos()).len(); + if (d < dist) { + dist = d; + found = t; + } + } + if (found != null) + return found; + + dist = Float.MAX_VALUE; for (State s : states) - if (s.matches(pos)) - return s; + if (s.matches(pos)) { + float d = pos.sub(s.getPos()).len(); + if (d < dist) { + dist = d; + found = s; + } + } - return null; - } - - /** - * Move states to raster - */ - public void toRaster() { - for (State s : states) - s.toRaster(); + return found; } /** @@ -386,7 +438,7 @@ public class FSM { */ public void remove(Transition transition) { transitions.remove(transition); - wasModified(); + wasModified(transition, Movable.Property.REMOVED); resetInitInitialization(); } @@ -398,15 +450,30 @@ public class FSM { public void remove(State state) { states.remove(state); transitions.removeIf(t -> t.getStartState() == state || t.getTargetState() == state); - wasModified(); + wasModified(state, Movable.Property.REMOVED); resetInitInitialization(); } /** * Marks the fsm as modified + * + * @param movable the element changed + * @param prop the property which has changed */ - void wasModified() { + void wasModified(Movable movable, Movable.Property prop) { modified = true; + + if (movable instanceof State) { + State st = (State) movable; + if (prop == Movable.Property.POS && getMovingState() != MovingState.BOTH) + st.toRaster(); + + if ((prop == Movable.Property.POS || prop == Movable.Property.MOUSEPOS) && getMovingState() == MovingState.STOP) + for (Transition t : transitions) + if (t.getTargetState() == st || t.getStartState() == st) + t.setPos(t.getPos()); + } + if (modifiedListener != null) modifiedListener.modifiedChanged(modified); } diff --git a/src/main/java/de/neemann/digital/fsm/Movable.java b/src/main/java/de/neemann/digital/fsm/Movable.java index e707747e3..652ccbb40 100644 --- a/src/main/java/de/neemann/digital/fsm/Movable.java +++ b/src/main/java/de/neemann/digital/fsm/Movable.java @@ -13,6 +13,9 @@ import de.neemann.digital.draw.graphics.VectorFloat; * @param the type of the implementing class */ public class Movable { + + enum Property {POS, REMOVED, CONDITION, NAME, NUMBER, MOUSEPOS, VALUES} + private static final float MASS = 50f; private static final float FRICTION = 0.8f; private static final float MAX_FORCE = 100000f; @@ -23,6 +26,7 @@ public class Movable { private transient VectorFloat speed; private transient VectorFloat force; private transient FSM fsm; + private transient Property lastPosProp; /** * Creates a new instance @@ -33,21 +37,35 @@ public class Movable { position = new VectorFloat(0, 0); } + /** + * Sets the position by mouse movement + * + * @param position the position + */ + public void setPosByMouse(VectorFloat position) { + setPos(position, Property.MOUSEPOS); + } + /** * Sets the position * * @param position the position */ public void setPos(VectorFloat position) { - if (!this.position.equals(position)) { + setPos(position, Property.POS); + } + + private void setPos(VectorFloat position, Property prop) { + if (!this.position.equals(position) || lastPosProp != prop) { this.position = position; - wasModified(); + lastPosProp = prop; + wasModified(prop); } } - void wasModified() { + void wasModified(Property prop) { if (fsm != null) - fsm.wasModified(); + fsm.wasModified(this, prop); } /** @@ -167,7 +185,7 @@ public class Movable { public A setValues(String values) { if (!this.values.equals(values)) { this.values = values; - wasModified(); + wasModified(Property.VALUES); } return (A) this; } diff --git a/src/main/java/de/neemann/digital/fsm/State.java b/src/main/java/de/neemann/digital/fsm/State.java index cf94de9ba..ef4ac4eb8 100644 --- a/src/main/java/de/neemann/digital/fsm/State.java +++ b/src/main/java/de/neemann/digital/fsm/State.java @@ -52,7 +52,7 @@ public class State extends Movable { public void setName(String name) { if (!this.name.equals(name)) { this.name = name; - wasModified(); + wasModified(Property.NAME); } } @@ -158,7 +158,7 @@ public class State extends Movable { public State setNumber(int number) { if (this.number != number) { this.number = number; - wasModified(); + wasModified(Property.NUMBER); if (getFsm() != null) getFsm().resetInitInitialization(); } diff --git a/src/main/java/de/neemann/digital/fsm/Transition.java b/src/main/java/de/neemann/digital/fsm/Transition.java index cae1c9c33..b4c5277c1 100644 --- a/src/main/java/de/neemann/digital/fsm/Transition.java +++ b/src/main/java/de/neemann/digital/fsm/Transition.java @@ -92,8 +92,18 @@ public class Transition extends Movable { return getFsm() != null && getFsm().isInitial(this); } + + @Override + public void setPosByMouse(VectorFloat position) { + super.setPosByMouse(posConstrain(position)); + } + @Override public void setPos(VectorFloat position) { + super.setPos(posConstrain(position)); + } + + private VectorFloat posConstrain(VectorFloat position) { if (fromState != toState) { VectorFloat dist = toState.getPos().sub(fromState.getPos()); if (dist.getXFloat() != 0 || dist.getYFloat() != 0) { @@ -104,11 +114,10 @@ public class Transition extends Movable { VectorFloat p = position.sub(start); VectorFloat n = dist.getOrthogonal(); float l = p.mul(n); - super.setPos(start.add(end).div(2).add(n.mul(l))); - return; + return start.add(end).div(2).add(n.mul(l)); } } - super.setPos(position); + return position; } /** @@ -198,7 +207,7 @@ public class Transition extends Movable { public void setCondition(String condition) { if (!this.condition.equals(condition)) { this.condition = condition; - wasModified(); + wasModified(Property.CONDITION); conditionExpression = null; if (getFsm() != null) getFsm().resetInitInitialization(); 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 0967df669..6575e4b99 100644 --- a/src/main/java/de/neemann/digital/fsm/gui/FSMComponent.java +++ b/src/main/java/de/neemann/digital/fsm/gui/FSMComponent.java @@ -91,8 +91,8 @@ public class FSMComponent extends JComponent { @Override public void mouseReleased(MouseEvent mouseEvent) { - if (elementMoved instanceof State) { - ((State) elementMoved).toRaster(); + if (elementMoved != null) { + elementMoved.setPos(getPosVector(mouseEvent).sub(delta).toFloat()); repaint(); } elementMoved = null; @@ -140,7 +140,7 @@ public class FSMComponent extends JComponent { repaint(); } if (elementMoved != null) { - elementMoved.setPos(getPosVector(e).sub(delta).toFloat()); + elementMoved.setPosByMouse(getPosVector(e).sub(delta).toFloat()); repaint(); } if (newTransitionFromState != null) diff --git a/src/main/java/de/neemann/digital/fsm/gui/FSMFrame.java b/src/main/java/de/neemann/digital/fsm/gui/FSMFrame.java index bd127fd65..ed255aae6 100644 --- a/src/main/java/de/neemann/digital/fsm/gui/FSMFrame.java +++ b/src/main/java/de/neemann/digital/fsm/gui/FSMFrame.java @@ -54,7 +54,6 @@ public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSav private final Timer timer; private final JComboBox moveControl; private FSM fsm; - private boolean moveStates = false; private ToolTipAction save; private File filename; private File baseFilename; @@ -78,9 +77,13 @@ public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSav timer = new Timer(100, new AbstractAction() { @Override public void actionPerformed(ActionEvent actionEvent) { - for (int i = 0; i < 100; i++) - fsm.move(10, moveStates, fsmComponent.getElementMoved()); - repaint(); + if (fsm.getMovingState() == FSM.MovingState.STOP) + timer.stop(); + else { + for (int i = 0; i < 100; i++) + fsm.move(10, fsmComponent.getElementMoved()); + repaint(); + } } }); @@ -111,18 +114,15 @@ public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSav public void actionPerformed(ActionEvent actionEvent) { switch (moveControl.getSelectedIndex()) { case 0: - timer.stop(); - fsm.toRaster(); + fsm.setMovingState(FSM.MovingState.STOP); fsmComponent.repaint(); break; case 1: - if (moveStates) - fsm.toRaster(); - moveStates = false; + fsm.setMovingState(FSM.MovingState.TRANSITIONS); timer.start(); break; case 2: - moveStates = true; + fsm.setMovingState(FSM.MovingState.BOTH); timer.start(); break; }