diff --git a/src/main/java/de/neemann/digital/fsm/FSM.java b/src/main/java/de/neemann/digital/fsm/FSM.java index 99f61950d..7ade8c257 100644 --- a/src/main/java/de/neemann/digital/fsm/FSM.java +++ b/src/main/java/de/neemann/digital/fsm/FSM.java @@ -26,9 +26,10 @@ public class FSM { private ArrayList states; private ArrayList transitions; - private transient boolean initChecked; private transient boolean modified; private transient ModifiedListener modifiedListener; + private transient boolean isInitialChecked; + private transient Transition initialTransition; /** * Creates a proper configured XStream instance @@ -134,6 +135,7 @@ public class FSM { state.setNumber(states.size()); state.setFSM(this); states.add(state); + resetInitInitialization(); return this; } @@ -146,6 +148,7 @@ public class FSM { public FSM add(Transition transition) { transitions.add(transition); transition.setFSM(this); + resetInitInitialization(); return this; } @@ -230,10 +233,6 @@ public class FSM { * @param gr the Graphic instance to draw to */ public void drawTo(Graphic gr) { - if (!initChecked) { - checkInitState(); - initChecked = true; - } for (State s : states) s.drawTo(gr); for (Transition t : transitions) @@ -241,6 +240,9 @@ public class FSM { } private void checkInitState() { + initialTransition = null; + isInitialChecked = true; + int count = 0; Transition found = null; for (Transition t : transitions) { @@ -248,17 +250,33 @@ public class FSM { count++; found = t; } + if (t.getTargetState().getNumber() == 0) + return; } try { - if (count == 1 && !found.hasCondition()) { - found.getStartState().setInitial(); - found.setInitial(); - } + if (count == 1 && !found.hasCondition()) + initialTransition = found; } catch (FiniteStateMachineException e) { // ignore } } + void resetInitInitialization() { + isInitialChecked = false; + } + + boolean isInitial(State state) { + if (!isInitialChecked) + checkInitState(); + return initialTransition != null && state.getNumber() == 0; + } + + boolean isInitial(Transition transition) { + if (!isInitialChecked) + checkInitState(); + return transition == initialTransition; + } + /** * Moved the elements * @@ -286,8 +304,8 @@ public class FSM { double delta = 2 * Math.PI / states.size(); double rad = 0; for (State s : states) - if (s.getRadius() > rad) - rad = s.getRadius(); + if (s.getVisualRadius() > rad) + rad = s.getVisualRadius(); rad *= 4; double phi = 0; @@ -354,6 +372,7 @@ public class FSM { public void remove(Transition transition) { transitions.remove(transition); wasModified(); + resetInitInitialization(); } /** @@ -365,6 +384,7 @@ public class FSM { states.remove(state); transitions.removeIf(t -> t.getStartState() == state || t.getTargetState() == state); wasModified(); + resetInitInitialization(); } /** diff --git a/src/main/java/de/neemann/digital/fsm/State.java b/src/main/java/de/neemann/digital/fsm/State.java index ddc6ae3e6..7d614f67e 100644 --- a/src/main/java/de/neemann/digital/fsm/State.java +++ b/src/main/java/de/neemann/digital/fsm/State.java @@ -18,6 +18,7 @@ public class State extends Movable { private static final int RAD = 70; private static final float REACH = 2000; + private static final int INIT_RADIUS = 20; private int number = -1; private String name; @@ -25,7 +26,6 @@ public class State extends Movable { private String values = ""; private transient TreeMap valueMap; - private transient boolean isInitial; /** * Creates a new state @@ -119,8 +119,8 @@ public class State extends Movable { * @param gr the Graphic instance to draw to */ public void drawTo(Graphic gr) { - if (isInitial) { - VectorInterface rad = new Vector(radius, radius); + if (isInitialState()) { + VectorInterface rad = new Vector(INIT_RADIUS, INIT_RADIUS); gr.drawCircle(getPos().sub(rad), getPos().add(rad), Style.FILLED); } else { VectorInterface rad = new Vector(radius, radius); @@ -144,6 +144,20 @@ public class State extends Movable { } } + private boolean isInitialState() { + return getFsm() != null && getFsm().isInitial(this); + } + + /** + * @return the radius of the state + */ + float getVisualRadius() { + if (isInitialState()) + return INIT_RADIUS; + else + return radius; + } + /** * @return the radius of the state */ @@ -161,6 +175,8 @@ public class State extends Movable { if (this.number != number) { this.number = number; wasModified(); + if (getFsm() != null) + getFsm().resetInitInitialization(); } return this; } @@ -202,11 +218,4 @@ public class State extends Movable { return this; } - /** - * Makes this state the initial state - */ - public void setInitial() { - isInitial = true; - radius = 20; - } } diff --git a/src/main/java/de/neemann/digital/fsm/Transition.java b/src/main/java/de/neemann/digital/fsm/Transition.java index 2900b3c13..051fb71ea 100644 --- a/src/main/java/de/neemann/digital/fsm/Transition.java +++ b/src/main/java/de/neemann/digital/fsm/Transition.java @@ -27,7 +27,6 @@ public class Transition extends Movable { private String condition = ""; private String values = ""; private transient Expression conditionExpression; - private transient boolean isInitial; private transient TreeMap valuesMap; @@ -53,9 +52,7 @@ public class Transition extends Movable { * @param transitions the transitions */ public void calcForce(List states, List transitions) { - float preferredDist = 20; - if (!isInitial) - preferredDist = Math.max(fromState.getRadius(), toState.getRadius()) * 5; + float preferredDist = Math.max(fromState.getVisualRadius(), toState.getVisualRadius()) * 5; calcForce(preferredDist, states, transitions); } @@ -81,7 +78,7 @@ public class Transition extends Movable { VectorFloat center = fromState.getPos().add(toState.getPos()).mul(0.5f); addAttractiveTo(center, 1); - if (!isInitial) { + if (!isInitialTransition()) { for (State s : states) addRepulsive(s.getPos(), 2000); @@ -91,6 +88,10 @@ public class Transition extends Movable { } } + private boolean isInitialTransition() { + return getFsm() != null && getFsm().isInitial(this); + } + @Override public void setPos(VectorFloat position) { if (fromState != toState) { @@ -120,9 +121,9 @@ public class Transition extends Movable { anchorTo = anchorTo.sub(dif); } - VectorFloat difFrom = anchorFrom.sub(fromState.getPos()).norm().mul(fromState.getRadius() + Style.MAXLINETHICK); - VectorFloat difTo = anchorTo.sub(toState.getPos()).norm().mul(toState.getRadius() + Style.MAXLINETHICK + 2); - VectorFloat difToTip = anchorTo.sub(toState.getPos()).norm().mul(toState.getRadius() + Style.MAXLINETHICK); + VectorFloat difFrom = anchorFrom.sub(fromState.getPos()).norm().mul(fromState.getVisualRadius() + Style.MAXLINETHICK); + VectorFloat difTo = anchorTo.sub(toState.getPos()).norm().mul(toState.getVisualRadius() + Style.MAXLINETHICK + 2); + VectorFloat difToTip = anchorTo.sub(toState.getPos()).norm().mul(toState.getVisualRadius() + Style.MAXLINETHICK); final VectorFloat start = fromState.getPos().add(difFrom); final VectorFloat end = toState.getPos().add(difTo); @@ -167,6 +168,8 @@ public class Transition extends Movable { this.condition = condition; wasModified(); conditionExpression = null; + if (getFsm()!=null) + getFsm().resetInitInitialization(); } } @@ -268,10 +271,4 @@ public class Transition extends Movable { return fromState + " --[" + condition + "]-> " + toState; } - /** - * Mark this transition as initial transition - */ - public void setInitial() { - isInitial = true; - } } 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 12b2956f9..127e041c7 100644 --- a/src/main/java/de/neemann/digital/fsm/gui/FSMFrame.java +++ b/src/main/java/de/neemann/digital/fsm/gui/FSMFrame.java @@ -263,6 +263,7 @@ public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSav moveControl.setSelectedIndex(0); setFSM(FSM.loadFSM(file)); setFilename(file); + lastModified=fsm.isModified(); } catch (IOException e) { new ErrorMessage(Lang.get("msg_fsm_errorLoadingFile")).addCause(e).show(this); } @@ -273,6 +274,7 @@ public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSav fsm.save(file); setFilename(file); save.setEnabled(false); + lastModified=fsm.isModified(); } catch (IOException e) { new ErrorMessage(Lang.get("msg_fsm_errorStoringFile")).addCause(e).show(this); }