From 45e0d6816837e85d957c78d6332e9cec8440e70a Mon Sep 17 00:00:00 2001 From: hneemann Date: Sun, 31 May 2020 20:29:05 +0200 Subject: [PATCH] Allows to select the angle at which the init state marker is drawn with the mouse. --- src/main/java/de/neemann/digital/fsm/FSM.java | 11 ++-- .../de/neemann/digital/fsm/MouseMovable.java | 35 +++++++++++ .../java/de/neemann/digital/fsm/Movable.java | 4 +- .../java/de/neemann/digital/fsm/State.java | 60 ++++++++++++++----- .../neemann/digital/fsm/gui/FSMComponent.java | 27 +++------ src/main/resources/lang/lang_de.xml | 4 -- src/main/resources/lang/lang_en.xml | 4 -- 7 files changed, 97 insertions(+), 48 deletions(-) create mode 100644 src/main/java/de/neemann/digital/fsm/MouseMovable.java diff --git a/src/main/java/de/neemann/digital/fsm/FSM.java b/src/main/java/de/neemann/digital/fsm/FSM.java index fc14459b1..6aee6a03e 100644 --- a/src/main/java/de/neemann/digital/fsm/FSM.java +++ b/src/main/java/de/neemann/digital/fsm/FSM.java @@ -301,7 +301,7 @@ public class FSM { * @param dt the time step * @param except element which is fixed */ - public void move(int dt, Movable except) { + public void move(int dt, MouseMovable except) { if (state != MovingState.STOP) { calculateForces(); if (state == MovingState.BOTH) @@ -377,8 +377,8 @@ public class FSM { * @param pos the position * @return the element or null */ - public Movable getMovable(Vector pos) { - Movable found = null; + public MouseMovable getMovable(Vector pos) { + Movable found = null; float dist = Float.MAX_VALUE; for (Transition t : transitions) if (t.matches(pos)) { @@ -400,7 +400,8 @@ public class FSM { dist = d; found = s; } - } + } else if (s.matchesInitial(pos)) + return s.getInitialMarkerMovable(); return found; } @@ -439,7 +440,7 @@ public class FSM { * @param movable the element changed * @param prop the property which has changed */ - void wasModified(Movable movable, Movable.Property prop) { + void wasModified(Movable movable, Movable.Property prop) { modified = true; if (movable instanceof State) { diff --git a/src/main/java/de/neemann/digital/fsm/MouseMovable.java b/src/main/java/de/neemann/digital/fsm/MouseMovable.java new file mode 100644 index 000000000..64d94c457 --- /dev/null +++ b/src/main/java/de/neemann/digital/fsm/MouseMovable.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 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.draw.graphics.VectorFloat; +import de.neemann.digital.draw.graphics.VectorInterface; + +/** + * Element which can be moved by the mouse + */ +public interface MouseMovable { + /** + * @return the position + */ + VectorInterface getPos(); + + /** + * Sets the position by the mouse. + * Is called while dragging. + * + * @param pos the position + */ + void setPosByMouse(VectorFloat pos); + + /** + * Sets the position by the mouse. + * Is called if mouse button is released. + * + * @param pos the position + */ + void setPos(VectorFloat pos); +} diff --git a/src/main/java/de/neemann/digital/fsm/Movable.java b/src/main/java/de/neemann/digital/fsm/Movable.java index a3fa6a1e5..0a6e1f059 100644 --- a/src/main/java/de/neemann/digital/fsm/Movable.java +++ b/src/main/java/de/neemann/digital/fsm/Movable.java @@ -12,9 +12,9 @@ import de.neemann.digital.draw.graphics.VectorFloat; * * @param the type of the implementing class */ -public class Movable { +public class Movable> implements MouseMovable { - enum Property {POS, REMOVED, CONDITION, NAME, NUMBER, MOUSEPOS, VALUES, INITIAL, ADDED} + enum Property {POS, REMOVED, CONDITION, NAME, NUMBER, MOUSEPOS, VALUES, INITIAL, ADDED, INITIAL_ANGLE} private static final float MASS = 50f; private static final float FRICTION = 0.8f; diff --git a/src/main/java/de/neemann/digital/fsm/State.java b/src/main/java/de/neemann/digital/fsm/State.java index 890886bed..21bfe37c4 100644 --- a/src/main/java/de/neemann/digital/fsm/State.java +++ b/src/main/java/de/neemann/digital/fsm/State.java @@ -99,9 +99,7 @@ public class State extends Movable { if (isInitial) { Vector initRad = new Vector(INIT_RAD, INIT_RAD); - int r = radius + INIT_RAD * 6; - double angle = 2 * Math.PI / 32 * initialAngle; - VectorInterface pos = getPos().add(new VectorFloat((float) (Math.cos(angle) * r), -(float) (Math.sin(angle) * r))); + VectorInterface pos = getInitialMarkerPos(); gr.drawCircle(pos.sub(initRad), pos.add(initRad), Style.FILLED); VectorInterface delta = getPos().sub(pos).norm(); VectorInterface a0 = pos.add(delta.mul(INIT_RAD + Style.FILLED.getThickness())); @@ -122,6 +120,15 @@ public class State extends Movable { } } + /** + * @return the initial marker position + */ + VectorInterface getInitialMarkerPos() { + int r = radius + INIT_RAD * 6; + double angle = 2 * Math.PI / 32 * initialAngle; + return getPos().add(new VectorFloat((float) (Math.cos(angle) * r), -(float) (Math.sin(angle) * r))); + } + /** * @return the radius of the state */ @@ -176,6 +183,18 @@ public class State extends Movable { return pos.sub(getPos()).len() <= radius; } + /** + * Returns true if the position matches the states initial marker + * + * @param pos the position + * @return true if pos inside of the states initial marker + */ + public boolean matchesInitial(Vector pos) { + if (!isInitial) + return false; + return pos.sub(getInitialMarkerPos()).len() <= INIT_RAD; + } + @Override public String toString() { if (name != null && name.length() > 0) @@ -221,18 +240,31 @@ public class State extends Movable { } /** - * @return the angle of the initial marker + * @return a movable that represents the initial marker. */ - public int getInitialAngle() { - return initialAngle; - } + public MouseMovable getInitialMarkerMovable() { + return new MouseMovable() { + @Override + public VectorInterface getPos() { + return getInitialMarkerPos(); + } - /** - * Sets the angle of the initial marker - * - * @param angle the angle - */ - public void setInitialAngle(Integer angle) { - initialAngle = angle; + @Override + public void setPosByMouse(VectorFloat pos) { + VectorInterface delta = pos.sub(State.this.getPos()); + double angle = Math.atan2(-delta.getYFloat(), delta.getXFloat()) / Math.PI * 16; + if (angle < 0) + angle += 32; + int ia = (int) Math.round(angle); + if (initialAngle != ia) { + initialAngle = ia; + wasModified(Property.INITIAL_ANGLE); + } + } + + @Override + public void setPos(VectorFloat pos) { + } + }; } } 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 35fc153f2..768e87930 100644 --- a/src/main/java/de/neemann/digital/fsm/gui/FSMComponent.java +++ b/src/main/java/de/neemann/digital/fsm/gui/FSMComponent.java @@ -10,10 +10,7 @@ import de.neemann.digital.core.element.Key; import de.neemann.digital.core.element.Keys; import de.neemann.digital.draw.graphics.*; import de.neemann.digital.draw.graphics.Polygon; -import de.neemann.digital.fsm.FSM; -import de.neemann.digital.fsm.Movable; -import de.neemann.digital.fsm.State; -import de.neemann.digital.fsm.Transition; +import de.neemann.digital.fsm.*; import de.neemann.digital.gui.components.AttributeDialog; import de.neemann.digital.lang.Lang; import de.neemann.gui.Mouse; @@ -34,12 +31,6 @@ import static de.neemann.digital.gui.components.CircuitComponent.ICON_DELETE; public class FSMComponent extends JComponent { private static final Key KEY_NUMBER = new Key.KeyInteger("stateNum", 0); private static final Key KEY_INITIAL = new Key<>("isInitialState", false); - private static final Key KEY_INITIAL_ANGLE = new Key.KeyInteger("initialAngle", 12) - .setComboBoxValues(4, 12, 20, 28) - .setMin(0) - .setMax(31) - .setDependsOn(KEY_INITIAL) - .setSecondary(); private static final Key KEY_VALUES = new Key<>("stateValues", ""); private static final Key KEY_CONDITION = new Key<>("transCond", ""); private static final Key KEY_RADIUS = new Key.KeyInteger("transRad", 70) @@ -51,7 +42,7 @@ public class FSMComponent extends JComponent { private boolean isManualScale; private AffineTransform transform = new AffineTransform(); - private Movable elementMoved; + private MouseMovable elementMoved; private FSM fsm; private Vector lastMousePos; private State newTransitionFromState; @@ -87,7 +78,7 @@ public class FSMComponent extends JComponent { if (elementMoved != null) delta = posVector.sub(elementMoved.getPos()); } else if (mouse.isSecondaryClick(e)) { - Movable st = fsm.getMovable(posVector); + MouseMovable st = fsm.getMovable(posVector); if (st instanceof State) { newTransitionStartPos = posVector; newTransitionFromState = (State) st; @@ -107,7 +98,7 @@ public class FSMComponent extends JComponent { if (newTransitionFromState != null) { final Vector posVector = getPosVector(mouseEvent); if (newTransitionStartPos.sub(posVector).len() > MIN_NEW_TRANS_DIST) { - Movable target = fsm.getMovable(posVector); + MouseMovable target = fsm.getMovable(posVector); if (target instanceof State) fsm.add(new Transition(newTransitionFromState, (State) target, lastCondition)); } @@ -119,7 +110,7 @@ public class FSMComponent extends JComponent { @Override public void mouseClicked(MouseEvent mouseEvent) { final Vector posVector = getPosVector(mouseEvent); - Movable elementClicked = fsm.getMovable(posVector); + MouseMovable elementClicked = fsm.getMovable(posVector); if (mouse.isSecondaryClick(mouseEvent)) { if (elementClicked == null) createNewState(posVector, new Point(mouseEvent.getX(), mouseEvent.getY())); @@ -161,7 +152,7 @@ public class FSMComponent extends JComponent { ToolTipAction deleteAction = new ToolTipAction(Lang.get("menu_delete"), ICON_DELETE) { @Override public void actionPerformed(ActionEvent actionEvent) { - Movable element = fsm.getMovable(lastMousePos); + MouseMovable element = fsm.getMovable(lastMousePos); if (element instanceof State) { fsm.remove((State) element); repaint(); @@ -188,7 +179,7 @@ public class FSMComponent extends JComponent { setPreferredSize(new Dimension(600, 600)); } - private static final Key[] STATE_EDIT_KEYS = {Keys.LABEL, KEY_NUMBER, KEY_INITIAL, KEY_INITIAL_ANGLE, KEY_VALUES, KEY_RADIUS}; + private static final Key[] STATE_EDIT_KEYS = {Keys.LABEL, KEY_NUMBER, KEY_INITIAL, KEY_VALUES, KEY_RADIUS}; private void createNewState(Vector posVector, Point point) { ElementAttributes attr = new ElementAttributes(); @@ -216,7 +207,6 @@ public class FSMComponent extends JComponent { ElementAttributes attr = new ElementAttributes() .set(KEY_NUMBER, state.getNumber()) .set(KEY_INITIAL, state.isInitial()) - .set(KEY_INITIAL_ANGLE, state.getInitialAngle()) .set(KEY_VALUES, state.getValues()) .set(KEY_RADIUS, state.getVisualRadius()) .set(Keys.LABEL, state.getName()); @@ -228,7 +218,6 @@ public class FSMComponent extends JComponent { if (newAttr != null) { state.setNumber(newAttr.get(KEY_NUMBER)); state.setInitial(newAttr.get(KEY_INITIAL)); - state.setInitialAngle(newAttr.get(KEY_INITIAL_ANGLE)); state.setValues(newAttr.get(KEY_VALUES)); state.setRadius(newAttr.get(KEY_RADIUS)); state.setName(newAttr.get(Keys.LABEL)); @@ -318,7 +307,7 @@ public class FSMComponent extends JComponent { /** * @return the element picked by the mouse */ - Movable getElementMoved() { + MouseMovable getElementMoved() { return elementMoved; } diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index 0dfed8806..5fff5baf1 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -2080,10 +2080,6 @@ Daher steht auch das Signal 'D_out' zur Verfügung, um in diesem Fall den Wert z Wenn gesetzt, ist dies der Initialzustand, in welchem der Automat gestartet wird. - Winkel Initial-Marker - Winkel bei dem der Marker des Initialzustandes gezeichnet werden soll. - Ist ein Wert zwischen 0 und 31. - Ausgänge Legt Ausgangswerte fest. Mit einfachen Zuweisungen wie "A=1, B=0" können Ausgänge gesetzt werden. diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index eb1cd545c..199eba9e4 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -2035,10 +2035,6 @@ Therefore, the signal 'D_out' is also available to check the value in this case. The number which represents this state. Initial State If set, this state is the initial state. - Angle Initial Marker - Angle at which the marker of the initial state should be drawn. - Is a value between 0 and 31. - Outputs Defines the output values. With simple assignments like "A=1, B=0" outputs can be set.