mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-18 09:24:42 -04:00
added some editing function
This commit is contained in:
parent
ec1511258d
commit
e83dd45d71
@ -252,4 +252,8 @@ public class Vector implements VectorInterface {
|
|||||||
return (float) Math.sqrt(x * x + y * y);
|
return (float) Math.sqrt(x * x + y * y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VectorFloat toFloat() {
|
||||||
|
return new VectorFloat(x, y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,16 @@ public class VectorFloat implements VectorInterface {
|
|||||||
return new VectorFloat(x * a, y * a);
|
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
|
* Creates a new vector which has the value this/d
|
||||||
*
|
*
|
||||||
@ -131,4 +141,9 @@ public class VectorFloat implements VectorInterface {
|
|||||||
public float len() {
|
public float len() {
|
||||||
return (float) Math.sqrt(x * x + y * y);
|
return (float) Math.sqrt(x * x + y * y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VectorFloat toFloat() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,11 @@ public interface VectorInterface {
|
|||||||
*/
|
*/
|
||||||
Vector round();
|
Vector round();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return returns a float vector
|
||||||
|
*/
|
||||||
|
VectorFloat toFloat();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the length of the vector
|
* @return the length of the vector
|
||||||
*/
|
*/
|
||||||
|
@ -8,11 +8,13 @@ package de.neemann.digital.fsm;
|
|||||||
import de.neemann.digital.analyse.TruthTable;
|
import de.neemann.digital.analyse.TruthTable;
|
||||||
import de.neemann.digital.analyse.expression.*;
|
import de.neemann.digital.analyse.expression.*;
|
||||||
import de.neemann.digital.draw.graphics.Graphic;
|
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.draw.graphics.VectorFloat;
|
||||||
import de.neemann.digital.lang.Lang;
|
import de.neemann.digital.lang.Lang;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,7 +44,8 @@ public class FSM {
|
|||||||
* @return this for chained calls
|
* @return this for chained calls
|
||||||
*/
|
*/
|
||||||
public FSM add(State state) {
|
public FSM add(State state) {
|
||||||
state.setNumber(states.size());
|
if (state.getNumber() < 0)
|
||||||
|
state.setNumber(states.size());
|
||||||
states.add(state);
|
states.add(state);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -101,14 +104,14 @@ public class FSM {
|
|||||||
for (State s : states)
|
for (State s : states)
|
||||||
if (s.getName().equals(name))
|
if (s.getName().equals(name))
|
||||||
return s;
|
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 {
|
private State findState(int number) throws FinitStateMachineException {
|
||||||
for (State s : states)
|
for (State s : states)
|
||||||
if (s.getNumber() == number)
|
if (s.getNumber() == number)
|
||||||
return s;
|
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 this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the states
|
||||||
|
*/
|
||||||
|
public List<State> getStates() {
|
||||||
|
return states;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the FSM
|
* Draws the FSM
|
||||||
*
|
*
|
||||||
@ -139,14 +149,19 @@ public class FSM {
|
|||||||
/**
|
/**
|
||||||
* Moved the elements
|
* 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) {
|
public void move(int dt, boolean moveStates, Movable except) {
|
||||||
for (State s : states)
|
calculateForces();
|
||||||
s.move(dt);
|
if (moveStates)
|
||||||
|
for (State s : states)
|
||||||
|
if (s != except)
|
||||||
|
s.move(dt);
|
||||||
for (Transition t : transitions)
|
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++;
|
while ((1 << n) <= maxNumber) n++;
|
||||||
return 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
144
src/main/java/de/neemann/digital/fsm/FSMDemos.java
Normal file
144
src/main/java/de/neemann/digital/fsm/FSMDemos.java
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -117,8 +117,8 @@ public class Movable {
|
|||||||
* @param dt the time step
|
* @param dt the time step
|
||||||
*/
|
*/
|
||||||
public void move(int dt) {
|
public void move(int dt) {
|
||||||
speed = speed.add(force.mul(0.2f));
|
speed = speed.add(force.mul(dt / 200f));
|
||||||
position = position.add(speed);
|
setPos(position.add(speed.mul(dt / 1000f)));
|
||||||
speed = speed.mul(0.7f);
|
speed = speed.mul(0.7f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,9 +16,9 @@ import java.util.TreeMap;
|
|||||||
*/
|
*/
|
||||||
public class State extends Movable {
|
public class State extends Movable {
|
||||||
private static final int RAD = 70;
|
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 String name;
|
||||||
private int radius;
|
private int radius;
|
||||||
private TreeMap<String, Long> values;
|
private TreeMap<String, Long> values;
|
||||||
@ -86,6 +86,10 @@ public class State extends Movable {
|
|||||||
public void drawTo(Graphic gr) {
|
public void drawTo(Graphic gr) {
|
||||||
VectorInterface rad = new Vector(RAD, RAD);
|
VectorInterface rad = new Vector(RAD, RAD);
|
||||||
gr.drawCircle(getPos().sub(rad), getPos().add(rad), Style.NORMAL);
|
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());
|
Vector delta = new Vector(0, Style.NORMAL.getFontSize());
|
||||||
VectorFloat pos = getPos().add(delta.mul(-1));
|
VectorFloat pos = getPos().add(delta.mul(-1));
|
||||||
@ -113,9 +117,11 @@ public class State extends Movable {
|
|||||||
* Sets the number of the state
|
* Sets the number of the state
|
||||||
*
|
*
|
||||||
* @param number the number
|
* @param number the number
|
||||||
|
* @return this for chained calls
|
||||||
*/
|
*/
|
||||||
public void setNumber(int number) {
|
public State setNumber(int number) {
|
||||||
this.number = number;
|
this.number = number;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,4 +137,14 @@ public class State extends Movable {
|
|||||||
public TreeMap<String, Long> getValues() {
|
public TreeMap<String, Long> getValues() {
|
||||||
return values;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,12 +58,14 @@ public class Transition extends Movable {
|
|||||||
*/
|
*/
|
||||||
public void calcForce(float preferredDist, List<State> states, List<Transition> transitions) {
|
public void calcForce(float preferredDist, List<State> states, List<Transition> transitions) {
|
||||||
|
|
||||||
VectorFloat dir = fromState.getPos().sub(toState.getPos());
|
if (fromState != toState) {
|
||||||
float len = dir.len();
|
VectorFloat dir = fromState.getPos().sub(toState.getPos());
|
||||||
float d = len - preferredDist;
|
float len = dir.len();
|
||||||
dir = dir.mul(EXPANSION_TRANS * d);
|
float d = len - preferredDist;
|
||||||
toState.addToForce(dir);
|
dir = dir.mul(EXPANSION_TRANS * d);
|
||||||
fromState.addToForce(dir.mul(-1));
|
toState.addToForce(dir);
|
||||||
|
fromState.addToForce(dir.mul(-1));
|
||||||
|
}
|
||||||
|
|
||||||
resetForce();
|
resetForce();
|
||||||
VectorFloat center = fromState.getPos().add(toState.getPos()).mul(0.5f);
|
VectorFloat center = fromState.getPos().add(toState.getPos()).mul(0.5f);
|
||||||
@ -74,8 +76,19 @@ public class Transition extends Movable {
|
|||||||
|
|
||||||
for (Transition t : transitions)
|
for (Transition t : transitions)
|
||||||
if (t != this)
|
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
|
* @param gr the Graphic instance to draw to
|
||||||
*/
|
*/
|
||||||
public void drawTo(Graphic gr) {
|
public void drawTo(Graphic gr) {
|
||||||
VectorFloat difFrom = getPos().sub(fromState.getPos()).norm().mul(fromState.getRadius());
|
VectorFloat difFrom = getPos().sub(fromState.getPos()).norm().mul(fromState.getRadius() + Style.MAXLINETHICK);
|
||||||
VectorFloat difTo = getPos().sub(toState.getPos()).norm().mul(toState.getRadius());
|
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 start = fromState.getPos().add(difFrom);
|
||||||
final VectorFloat end = toState.getPos().add(difTo);
|
final VectorFloat end = toState.getPos().add(difTo);
|
||||||
|
final VectorFloat arrowTip = toState.getPos().add(difToTip);
|
||||||
|
|
||||||
Polygon p = new Polygon(false)
|
Polygon p = new Polygon(false)
|
||||||
.add(start)
|
.add(start)
|
||||||
@ -96,13 +111,12 @@ public class Transition extends Movable {
|
|||||||
final Style arrowStyle = Style.SHAPE_PIN;
|
final Style arrowStyle = Style.SHAPE_PIN;
|
||||||
gr.drawPolygon(p, arrowStyle);
|
gr.drawPolygon(p, arrowStyle);
|
||||||
|
|
||||||
// gr.drawLine(start, getPos(), Style.THIN);
|
|
||||||
// gr.drawLine(getPos(), end, Style.THIN);
|
|
||||||
|
|
||||||
// arrow
|
// arrow
|
||||||
VectorFloat lot = new VectorFloat(difTo.getYFloat(), -difTo.getXFloat()).mul(0.5f);
|
VectorFloat lot = new VectorFloat(difTo.getYFloat(), -difTo.getXFloat()).mul(0.5f);
|
||||||
gr.drawLine(end, end.add(difTo.add(lot).mul(0.2f)), arrowStyle);
|
gr.drawPolygon(new Polygon(false)
|
||||||
gr.drawLine(end, end.add(difTo.sub(lot).mul(0.2f)), arrowStyle);
|
.add(end.add(difTo.add(lot).mul(0.2f)))
|
||||||
|
.add(arrowTip)
|
||||||
|
.add(end.add(difTo.sub(lot).mul(0.2f))), arrowStyle);
|
||||||
if (condition != null) {
|
if (condition != null) {
|
||||||
String format;
|
String format;
|
||||||
try {
|
try {
|
||||||
@ -142,4 +156,14 @@ public class Transition extends Movable {
|
|||||||
public State getTargetState() {
|
public State getTargetState() {
|
||||||
return toState;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,28 +5,40 @@
|
|||||||
*/
|
*/
|
||||||
package de.neemann.digital.fsm.gui;
|
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.GraphicMinMax;
|
||||||
import de.neemann.digital.draw.graphics.GraphicSwing;
|
import de.neemann.digital.draw.graphics.GraphicSwing;
|
||||||
import de.neemann.digital.draw.graphics.Style;
|
import de.neemann.digital.draw.graphics.Style;
|
||||||
import de.neemann.digital.draw.graphics.Vector;
|
import de.neemann.digital.draw.graphics.Vector;
|
||||||
import de.neemann.digital.fsm.FSM;
|
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 javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ComponentAdapter;
|
import java.awt.event.ComponentAdapter;
|
||||||
import java.awt.event.ComponentEvent;
|
import java.awt.event.ComponentEvent;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.NoninvertibleTransformException;
|
import java.awt.geom.NoninvertibleTransformException;
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The component to show the fsm
|
* The component to show the fsm
|
||||||
*/
|
*/
|
||||||
public class FSMComponent extends JComponent {
|
public class FSMComponent extends JComponent {
|
||||||
|
|
||||||
|
private Mouse mouse = Mouse.getMouse();
|
||||||
|
|
||||||
private boolean isManualScale;
|
private boolean isManualScale;
|
||||||
private AffineTransform transform = new AffineTransform();
|
private AffineTransform transform = new AffineTransform();
|
||||||
|
private Movable elementMoved;
|
||||||
private FSM fsm;
|
private FSM fsm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,6 +61,55 @@ public class FSMComponent extends JComponent {
|
|||||||
repaint();
|
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() {
|
addComponentListener(new ComponentAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void componentResized(ComponentEvent componentEvent) {
|
public void componentResized(ComponentEvent componentEvent) {
|
||||||
@ -60,6 +121,26 @@ public class FSMComponent extends JComponent {
|
|||||||
setPreferredSize(new Dimension(600, 600));
|
setPreferredSize(new Dimension(600, 600));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createNewState(Vector posVector, Point point) {
|
||||||
|
Key<Integer> number = new Key.KeyInteger("stateNum", 0);
|
||||||
|
Key values = new Key<>("stateValues", "");
|
||||||
|
ArrayList<Key> list = new ArrayList<Key>();
|
||||||
|
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) {
|
private Vector getPosVector(MouseEvent e) {
|
||||||
return getPosVector(e.getX(), e.getY());
|
return getPosVector(e.getX(), e.getY());
|
||||||
}
|
}
|
||||||
@ -104,7 +185,13 @@ public class FSMComponent extends JComponent {
|
|||||||
transform = newTrans;
|
transform = newTrans;
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the element picked by the mouse
|
||||||
|
*/
|
||||||
|
public Movable getElementMoved() {
|
||||||
|
return elementMoved;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -115,6 +202,10 @@ public class FSMComponent extends JComponent {
|
|||||||
graphics.fillRect(0, 0, getWidth(), getHeight());
|
graphics.fillRect(0, 0, getWidth(), getHeight());
|
||||||
|
|
||||||
Graphics2D gr2 = (Graphics2D) graphics;
|
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);
|
gr2.transform(transform);
|
||||||
GraphicSwing gr = new GraphicSwing(gr2, 1);
|
GraphicSwing gr = new GraphicSwing(gr2, 1);
|
||||||
fsm.drawTo(gr);
|
fsm.drawTo(gr);
|
||||||
|
@ -5,21 +5,20 @@
|
|||||||
*/
|
*/
|
||||||
package de.neemann.digital.fsm.gui;
|
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.library.ElementLibrary;
|
||||||
import de.neemann.digital.draw.shapes.ShapeFactory;
|
import de.neemann.digital.draw.shapes.ShapeFactory;
|
||||||
import de.neemann.digital.fsm.FSM;
|
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.gui.components.table.TableDialog;
|
||||||
|
import de.neemann.digital.lang.Lang;
|
||||||
|
import de.neemann.gui.ErrorMessage;
|
||||||
|
import de.neemann.gui.ToolTipAction;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.WindowAdapter;
|
import java.awt.event.WindowAdapter;
|
||||||
import java.awt.event.WindowEvent;
|
import java.awt.event.WindowEvent;
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The dialog to show the FSM
|
* The dialog to show the FSM
|
||||||
@ -29,20 +28,27 @@ public class FSMDialog extends JDialog {
|
|||||||
private final FSM fsm;
|
private final FSM fsm;
|
||||||
private final FSMComponent fsmComponent;
|
private final FSMComponent fsmComponent;
|
||||||
private final Timer timer;
|
private final Timer timer;
|
||||||
|
private final ElementLibrary library;
|
||||||
|
private boolean moveStates = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance
|
* Creates a new instance
|
||||||
*
|
*
|
||||||
* @param frame the parents frame
|
* @param frame the parents frame
|
||||||
* @param fsm the fsm to visualize
|
* @param givenFsm the fsm to visualize
|
||||||
|
* @param library the library used to show the table
|
||||||
*/
|
*/
|
||||||
public FSMDialog(Frame frame, FSM fsm) {
|
public FSMDialog(Frame frame, FSM givenFsm, ElementLibrary library) {
|
||||||
super(frame, "FSM");
|
super(frame, Lang.get("fsm_title"));
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
this.fsm = fsm;
|
this.library = library;
|
||||||
|
if (givenFsm == null)
|
||||||
|
givenFsm = FSMDemos.createRotDecoder();
|
||||||
|
|
||||||
|
this.fsm = givenFsm;
|
||||||
|
|
||||||
fsmComponent = new FSMComponent(fsm);
|
fsmComponent = new FSMComponent(fsm);
|
||||||
getContentPane().add(fsmComponent);
|
getContentPane().add(fsmComponent, BorderLayout.CENTER);
|
||||||
pack();
|
pack();
|
||||||
setLocationRelativeTo(frame);
|
setLocationRelativeTo(frame);
|
||||||
|
|
||||||
@ -51,8 +57,8 @@ public class FSMDialog extends JDialog {
|
|||||||
timer = new Timer(100, new AbstractAction() {
|
timer = new Timer(100, new AbstractAction() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
fsm.calculateForces();
|
for (int i = 0; i < 100; i++)
|
||||||
fsm.move(100);
|
fsm.move(10, moveStates, fsmComponent.getElementMoved());
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -65,56 +71,53 @@ public class FSMDialog extends JDialog {
|
|||||||
timer.stop();
|
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
|
* A simple test method
|
||||||
*
|
*
|
||||||
* @param args the programs arguments
|
* @param args the programs arguments
|
||||||
* @throws Exception Exception
|
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) {
|
||||||
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)
|
|
||||||
|
|
||||||
.transition(rightA, top, e("!A & !B"))
|
FSM fsm = FSMDemos.selCounter();
|
||||||
.transition(rightB, topSetRight, e("!A & !B"))
|
|
||||||
.transition(leftA, top, e("!A & !B"))
|
|
||||||
.transition(leftB, topSetLeft, e("!A & !B"))
|
|
||||||
|
|
||||||
.transition(bottom, leftB, e("A & !B"))
|
ElementLibrary library = new ElementLibrary();
|
||||||
.transition(bottom, rightB, e("!A & B"))
|
new ShapeFactory(library);
|
||||||
.transition(bottomSetLeft, bottom, null)
|
|
||||||
.transition(bottomSetRight, bottom, null)
|
|
||||||
|
|
||||||
.transition(rightB, bottom, e("A & B"))
|
new FSMDialog(null, fsm, library).setVisible(true);
|
||||||
.transition(rightA, bottomSetRight, e("A & B"))
|
|
||||||
.transition(leftB, bottom, e("A & B"))
|
|
||||||
.transition(leftA, bottomSetLeft, e("A & B"));
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import de.neemann.digital.draw.model.ModelCreator;
|
|||||||
import de.neemann.digital.draw.model.RealTimeClock;
|
import de.neemann.digital.draw.model.RealTimeClock;
|
||||||
import de.neemann.digital.draw.shapes.Drawable;
|
import de.neemann.digital.draw.shapes.Drawable;
|
||||||
import de.neemann.digital.draw.shapes.ShapeFactory;
|
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.*;
|
||||||
import de.neemann.digital.gui.components.data.GraphDialog;
|
import de.neemann.digital.gui.components.data.GraphDialog;
|
||||||
import de.neemann.digital.gui.components.expression.ExpressionDialog;
|
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 TableDialog(Main.this,
|
||||||
new ModelAnalyser(model).analyse(),
|
new ModelAnalyser(model).analyse(),
|
||||||
library,
|
library,
|
||||||
shapeFactory,
|
|
||||||
getBaseFileName())
|
getBaseFileName())
|
||||||
.setVisible(true);
|
.setVisible(true);
|
||||||
ensureModelIsStopped();
|
ensureModelIsStopped();
|
||||||
@ -1114,7 +1114,7 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
|
|||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
TruthTable tt = new TruthTable(3).addResult();
|
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();
|
ensureModelIsStopped();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1130,6 +1130,15 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
|
|||||||
.setToolTip(Lang.get("menu_expression_tt"))
|
.setToolTip(Lang.get("menu_expression_tt"))
|
||||||
.createJMenuItem());
|
.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() {
|
private void orderMeasurements() {
|
||||||
|
@ -96,13 +96,12 @@ public class TableDialog extends JDialog {
|
|||||||
* @param parent the parent frame
|
* @param parent the parent frame
|
||||||
* @param truthTable the table to show
|
* @param truthTable the table to show
|
||||||
* @param library the library to use
|
* @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
|
* @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"));
|
super(parent, Lang.get("win_table"));
|
||||||
this.library = library;
|
this.library = library;
|
||||||
this.shapeFactory = shapeFactory;
|
this.shapeFactory = library.getShapeFactory();
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
kvMap = new KarnaughMapDialog(this, (boolTable, row) -> model.incValue(boolTable, row));
|
kvMap = new KarnaughMapDialog(this, (boolTable, row) -> model.incValue(boolTable, row));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user