Commit 834b71f4 authored by Christian Hilden's avatar Christian Hilden
Browse files

FSM Layout: Fix layout with inherited states and substatemachines

* Also slightly reduce size of transition names
parent 21310ea1
......@@ -1301,6 +1301,19 @@ public class FSMHelpers extends BaseHelpers {
}
return null;
}
public Transition getInitTransitionForNodeList(Set<StateGraph> sgs, List<StateGraphNode> allowedTargetNodes) {
for (StateGraph sg : sgs) {
Transition initTrans = getInitTransition(sg);
if (initTrans != null) {
StateGraphNode target = getTerminalState(initTrans.getTo());
if (allowedTargetNodes.contains(target)) {
return initTrans;
}
}
}
return null;
}
/**
* @param sg a {@link StateGraph}
......@@ -1310,6 +1323,13 @@ public class FSMHelpers extends BaseHelpers {
return getInitTransition(sg)!=null;
}
public StateGraphNode getTerminalState(TransitionTerminal tt) {
if (tt instanceof SubStateTrPointTerminal) {
return ((SubStateTrPointTerminal) tt).getState();
}
return getTerminalNode(tt);
}
public StateGraphNode getTerminalNode(TransitionTerminal tt) {
if (tt instanceof ChoicepointTerminal)
{
......@@ -1327,6 +1347,14 @@ public class FSMHelpers extends BaseHelpers {
return null;
}
public List<Transition> getTransitionsFromGraphNode(Set<StateGraph> sgs, StateGraphNode node) {
Set<Transition> result = new HashSet<>();
for (StateGraph sg : sgs) {
result.addAll(getTransitionsFromGraphNode(sg, node));
}
return new ArrayList<>(result);
}
/**
* @return all Transitions originating from the passed graph node
*/
......@@ -1336,7 +1364,7 @@ public class FSMHelpers extends BaseHelpers {
for (Transition tr : sg.getTransitions()) {
if (tr instanceof NonInitialTransition) {
TransitionTerminal tt = ((NonInitialTransition) tr).getFrom();
if (getTerminalNode(tt) == node) {
if (getTerminalState(tt) == node) {
result.add(tr);
}
}
......@@ -1345,6 +1373,14 @@ public class FSMHelpers extends BaseHelpers {
return result;
}
public List<Transition> getTransitionsToGraphNode(Set<StateGraph> sgs, StateGraphNode node) {
Set<Transition> result = new HashSet<>();
for (StateGraph sg : sgs) {
result.addAll(getTransitionsToGraphNode(sg, node));
}
return new ArrayList<>(result);
}
/**
* @return all Transitions ending in the passed graph node
*/
......@@ -1353,7 +1389,7 @@ public class FSMHelpers extends BaseHelpers {
for (Transition tr : sg.getTransitions()) {
TransitionTerminal tt = tr.getTo();
if (getTerminalNode(tt) == node) {
if (getTerminalState(tt) == node) {
result.add(tr);
}
}
......@@ -1377,8 +1413,8 @@ public class FSMHelpers extends BaseHelpers {
* @return true if the passed transition has the same source and destination graph node
*/
public boolean isSelfTransition(Transition trans) {
StateGraphNode from = (trans instanceof InitialTransition)? null:getTerminalNode(((NonInitialTransition)trans).getFrom());
StateGraphNode to = getTerminalNode(trans.getTo());
StateGraphNode from = (trans instanceof InitialTransition)? null:getTerminalState(((NonInitialTransition)trans).getFrom());
StateGraphNode to = getTerminalState(trans.getTo());
return from == to;
}
......@@ -1388,15 +1424,15 @@ public class FSMHelpers extends BaseHelpers {
public int getSamePathTransitionCount(StateGraph sg, Transition trans) {
int count = 1;
StateGraphNode src = (trans instanceof InitialTransition) ? null : getTerminalNode(((NonInitialTransition)trans).getFrom());
StateGraphNode dest = getTerminalNode(trans.getTo());
StateGraphNode src = (trans instanceof InitialTransition) ? null : getTerminalState(((NonInitialTransition)trans).getFrom());
StateGraphNode dest = getTerminalState(trans.getTo());
for (Transition t : sg.getTransitions()) {
if (trans == t) {
continue;
}
StateGraphNode tSrc = (t instanceof InitialTransition) ? null : getTerminalNode(((NonInitialTransition)t).getFrom());
StateGraphNode tDest = getTerminalNode(t.getTo());
StateGraphNode tSrc = (t instanceof InitialTransition) ? null : getTerminalState(((NonInitialTransition)t).getFrom());
StateGraphNode tDest = getTerminalState(t.getTo());
if (tSrc == src && tDest == dest || tSrc == dest && tDest == src) {
count++;
}
......
......@@ -14,10 +14,20 @@
package org.eclipse.etrice.ui.behavior.fsm.support;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.etrice.core.fsm.fSM.CPBranchTransition;
import org.eclipse.etrice.core.fsm.fSM.ChoicepointTerminal;
......@@ -98,6 +108,7 @@ import org.eclipse.graphiti.mm.algorithms.Polygon;
import org.eclipse.graphiti.mm.algorithms.Polyline;
import org.eclipse.graphiti.mm.algorithms.Text;
import org.eclipse.graphiti.mm.algorithms.styles.Color;
import org.eclipse.graphiti.mm.algorithms.styles.Font;
import org.eclipse.graphiti.mm.algorithms.styles.Point;
import org.eclipse.graphiti.mm.pictograms.Anchor;
import org.eclipse.graphiti.mm.pictograms.Connection;
......@@ -120,6 +131,7 @@ import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.themes.ColorAndFontProviderImpl;
import org.eclipse.xtext.util.Strings;
import org.eclipse.xtext.xbase.lib.Pair;
......@@ -346,7 +358,8 @@ public class TransitionSupport {
ConnectionDecorator textDecorator =
peCreateService.createConnectionDecorator(connection, true,
0.5, true);
Text text = gaService.createDefaultText(getDiagram(), textDecorator, getLabel(trans));
Text text = gaService.createText(getDiagram(), textDecorator, getLabel(trans), "Arial", 6);
text.setForeground(lineColor);
gaService.setLocation(text, 10, 0);
......
......@@ -17,12 +17,14 @@ package org.eclipse.etrice.ui.behavior.fsm.support.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import org.eclipse.core.runtime.Assert;
import org.eclipse.emf.common.util.EList;
......@@ -302,8 +304,6 @@ public class DiagramEditingUtil {
addStateGraphNodes(allNodes, ctx.getPositionProvider(), sgShape, fp, node2anchor);
//addStateGraphNodes(ctx.getTrPoints(), ctx.getPositionProvider(), sgShape, fp, node2anchor);
//addStateGraphNodes(ctx.getStates(), ctx.getPositionProvider(), sgShape, fp, node2anchor);
//addStateGraphNodes(ctx.getChPoints(), ctx.getPositionProvider(), sgShape, fp, node2anchor);
......@@ -510,6 +510,7 @@ public class DiagramEditingUtil {
// self transitions
ILocation begin = Graphiti.getPeService().getLocationRelativeToDiagram(conn.getStart());
// TODO: Improve spacing. Y dist should be > text height
Point pt = Graphiti.getGaService().createPoint(
(int) (begin.getX() + StateGraphSupport.MARGIN*3 * Math.sin(angleDiff_rad * i)),
(int) (begin.getY() + StateGraphSupport.MARGIN*3 * Math.cos(angleDiff_rad * i))
......@@ -605,21 +606,48 @@ public class DiagramEditingUtil {
}
}
private boolean hasUnpositionedSuccessors(StateGraphNode node, Set<StateGraph> sgs, Map<StateGraphNode, PosAndSize> positions, Set<StateGraphNode> visitedNodes) {
for (Transition t: fsmHelpers.getTransitionsFromGraphNode(sgs, node)) {
StateGraphNode target = fsmHelpers.getTerminalNode(t.getTo());
if (visitedNodes.contains(target)) {
continue;
}
visitedNodes.add(target);
if (!positions.containsKey(target) || hasUnpositionedSuccessors(node, sgs, positions, visitedNodes)) {
return true;
}
}
return false;
}
// Called to produce initial StateGraphNode positions
private <T extends StateGraphNode> List<PosAndSize> getPositions(List<T> nodes, IPositionProvider positionProvider, double scaleX) {
private <T extends StateGraphNode> List<PosAndSize> getPositions(List<T> nodes, IPositionProvider positionProvider, double scaleX, Diagram diagram) {
ArrayList<PosAndSize> result = new ArrayList<PosAndSize>(nodes.size());
if (nodes.isEmpty())
return result;
Map<StateGraphNode, PosAndSize> positions = new HashMap<>();
Map<StateGraphNode, PosAndSize> positions = new HashMap<>();
//StateGraph sg = ContextSwitcher.getCurrentStateGraph(diagram);
int n = 0;
for (T node : nodes) {
//StateGraph sg = (StateGraph)nodes.get(0).eContainer();
Set<StateGraph> sgSet = new HashSet<>();
for (StateGraphNode node: nodes) {
sgSet.add((StateGraph) node.eContainer());
}
// Ensure we know the current position of all existing nodes
Set<StateGraphNode> allGraphNodes = new HashSet<>(nodes);
for (StateGraph sg: sgSet) {
allGraphNodes.addAll(sg.getStates());
allGraphNodes.addAll(sg.getTrPoints());
}
for (StateGraphNode node : allGraphNodes) {
PosAndSize pt = positionProvider.getPosition(node);
result.add(pt);
//result.add(pt);
if (pt==null) {
n++;
} else {
positions.put(node, pt);
}
......@@ -627,44 +655,67 @@ public class DiagramEditingUtil {
int h = StateGraphSupport.MARGIN * 5;
StateGraph sg = (StateGraph)nodes.get(0).eContainer();
// Place nodes starting with the initial transition.
// Place all nodes directly reachable from the current node on a 90 deg circle slice (bottom right) from the current node
Transition initTransition = fsmHelpers.getInitTransition(sg);
// Place nodes starting with the initial transition.
// Place all nodes directly reachable from the current node on a 90 deg circle slice (bottom right) from the current node
@SuppressWarnings("unchecked")
Transition initTransition = fsmHelpers.getInitTransitionForNodeList(sgSet, (List<StateGraphNode>) nodes);
//fsmHelpers.getInitTransition(sg);
// used to place unconnected StateGraphNodes
int unconnectedItemOffset = StateGraphSupport.MARGIN * 2;
int unconnectedStatesCount = 0;
int unconnectedTransitionPointsCount = 0;
if (initTransition == null) {
// if we do not have an init transition try to find an entry point as start for the layout
for (StateGraphNode node : nodes) {
List<Transition> transitions = fsmHelpers.getTransitionsFromGraphNode(sgSet, node);
if ((node instanceof TrPoint) && transitions.size() > 0) {
// Transition point with outgoing transitions
initTransition = transitions.get(0);
positions.put(node, new PosAndSize(h + unconnectedItemOffset * unconnectedTransitionPointsCount, 0, 0, 0));
unconnectedTransitionPointsCount++;
break;
}
}
}
if (initTransition != null) {
// skip graph based node placement if we do not have a initial transition
Queue<Transition> taskList = new LinkedList<>();
List<StateGraphNode> nodesToPlace = new LinkedList<>();
taskList.add(initTransition);
PosAndSize currentPos = new PosAndSize(h, h, 0, 0);
// place initial node to fixed start position
positions.put(fsmHelpers.getTerminalNode(initTransition.getTo()), currentPos);
positions.put(fsmHelpers.getTerminalState(initTransition.getTo()), currentPos);
while (!taskList.isEmpty()) {
nodesToPlace.clear();
Transition task = taskList.remove();
StateGraphNode currentTargetNode = fsmHelpers.getTerminalNode(task.getTo());
StateGraphNode currentTargetNode = fsmHelpers.getTerminalState(task.getTo());
currentPos = positions.getOrDefault(currentTargetNode, currentPos);
// collect all next nodes which have not been placed, yet
for (Transition t : fsmHelpers.getTransitionsFromGraphNode(sg, currentTargetNode)) {
StateGraphNode tTarget = fsmHelpers.getTerminalNode(t.getTo());
for (Transition t : fsmHelpers.getTransitionsFromGraphNode(sgSet, currentTargetNode)) {
StateGraphNode tTarget = fsmHelpers.getTerminalState(t.getTo());
if (!positions.containsKey(tTarget)) {
nodesToPlace.add(tTarget);
taskList.add(t);
} else if (hasUnpositionedSuccessors(tTarget, sgSet, positions, new HashSet<>())) {
taskList.add(t);
}
}
// collect all previous nodes which do not have incoming transitions (except for self transitions) and are not placed, yet
// This will most likely be entry points. It might be an option to always place them as first nodes and fix them to the right boarder.
for (Transition t : fsmHelpers.getTransitionsToGraphNode(sg, currentTargetNode)) {
// collect all previous nodes which are not placed, yet
// This keeps the graph more compact and it also accounts for entry points
for (Transition t : fsmHelpers.getTransitionsToGraphNode(sgSet, currentTargetNode)) {
if (t instanceof NonInitialTransition) {
StateGraphNode tPrev = fsmHelpers.getTerminalNode(((NonInitialTransition)t).getFrom());
StateGraphNode tPrev = fsmHelpers.getTerminalState(((NonInitialTransition)t).getFrom());
if (!positions.containsKey(tPrev) && !nodesToPlace.contains(tPrev)) {
// add only if not positions and if not already added by forward transition
nodesToPlace.add(tPrev);
......@@ -696,7 +747,7 @@ public class DiagramEditingUtil {
for (StateGraphNode node: nodesToPlace) {
// level of successors determines required height
int childNodeSuccessorLevel = findSuccessorLevel(node, sg, 0, listOfAlreadyHandledNodes);
int childNodeSuccessorLevel = findSuccessorLevel(node, sgSet, 0, listOfAlreadyHandledNodes);
System.out.println(node.getName() + " ancestor level " + childNodeSuccessorLevel);
int requiredHeight = (int) (offsetY * childNodeSuccessorLevel); // TODO: Useful pruning based on placement order/responsibility
// required height is added to offset after current nodes position has been determined
......@@ -748,16 +799,23 @@ public class DiagramEditingUtil {
// place unconnected states below initial state (the default layout grows to the right)
// place unconnected transition points on the upper frame border from left to right
int unconnectedStatesCount = 0;
int unconnectedTransitionPointsCount = 0;
int offset = StateGraphSupport.MARGIN * 2;
for (StateGraphNode node: nodes) {
if (!positions.containsKey(node)) {
if (node instanceof State) {
unconnectedStatesCount++;
positions.put(node, new PosAndSize(StateGraphSupport.MARGIN * 3, h + offset * unconnectedStatesCount, 0, 0));
PosAndSize newPos = new PosAndSize(StateGraphSupport.MARGIN * 3, h + unconnectedItemOffset * unconnectedStatesCount, 0, 0);
while (positions.containsValue(newPos) && unconnectedStatesCount < 10) {
unconnectedStatesCount++;
newPos = new PosAndSize(StateGraphSupport.MARGIN * 3, h + unconnectedItemOffset * unconnectedStatesCount, 0, 0);
}
positions.put(node, newPos);
} else {
positions.put(node, new PosAndSize(h + offset * unconnectedTransitionPointsCount, 0, 0, 0));
PosAndSize newPos = new PosAndSize(h + unconnectedItemOffset * unconnectedTransitionPointsCount, 0, 0, 0);
while (positions.containsValue(newPos) && unconnectedTransitionPointsCount < 10) {
unconnectedTransitionPointsCount++;
newPos = new PosAndSize(h + unconnectedItemOffset * unconnectedTransitionPointsCount, 0, 0, 0);
}
positions.put(node, newPos);
unconnectedTransitionPointsCount++;
}
}
......@@ -765,9 +823,11 @@ public class DiagramEditingUtil {
for (int i=0; i<nodes.size(); ++i) {
if (result.get(i)==null) {
result.set(i, positions.get(nodes.get(i)));
}
//if (result.get(i)==null) {
PosAndSize pos = positions.get(nodes.get(i));
result.add(pos);
//result.set(i, pos);
//}
}
return result;
......@@ -778,14 +838,14 @@ public class DiagramEditingUtil {
* -> A node with only one successor does not increase the level
* @return recursive calculated "successor level"
*/
private int findSuccessorLevel(StateGraphNode node, StateGraph sg, int currentLevel, List<StateGraphNode> visitedNodes) {
private int findSuccessorLevel(StateGraphNode node, Set<StateGraph> sgs, int currentLevel, List<StateGraphNode> visitedNodes) {
visitedNodes.add(node);
int result = 0;
for (Transition trans : fsmHelpers.getTransitionsFromGraphNode(sg, node)) {
for (Transition trans : fsmHelpers.getTransitionsFromGraphNode(sgs, node)) {
StateGraphNode nextNode = fsmHelpers.getTerminalNode(trans.getTo());
if (!visitedNodes.contains(nextNode)) {
result++;
result += findSuccessorLevel(nextNode, sg, result, visitedNodes);
result += findSuccessorLevel(nextNode, sgs, result, visitedNodes);
}
}
// level is always one level less than we have child transitions, but can never be < 0
......@@ -794,8 +854,8 @@ public class DiagramEditingUtil {
private void addStateGraphNodes(List<? extends StateGraphNode> nodes, IPositionProvider positionProvider, ContainerShape sgShape,
IFeatureProvider fp, HashMap<String, Anchor> node2anchor) {
List<PosAndSize> positions = getPositions(nodes, positionProvider, sgShape.getGraphicsAlgorithm().getWidth());
Diagram diagram = FSMSupportUtil.getInstance().getDiagram(sgShape);
List<PosAndSize> positions = getPositions(nodes, positionProvider, sgShape.getGraphicsAlgorithm().getWidth(), diagram);
int idx = 0;
for (StateGraphNode node : nodes) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment