Skip to content
Snippets Groups Projects
Commit 98586ece authored by Simon Barner's avatar Simon Barner
Browse files

- Initial Import of KIELER-based auto-layout initially implemented by Philip...

- Initial Import of KIELER-based auto-layout initially implemented by Philip Offtermatt (offtermatt@)
- TODO
 - Code cleanup
 - UI integration
parent ac713054
No related branches found
No related tags found
No related merge requests found
Showing
with 365 additions and 2 deletions
......@@ -5,4 +5,11 @@
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="build"/>
<classpathentry kind="lib" path="lib/opal-1.0.4.jar" sourcepath="lib/opal-1.0.4-src.zip"/>
<classpathentry exported="true" kind="lib" path="lib/com.google.guava-15.0.0.v201403281430.jar"/>
<classpathentry exported="true" kind="lib" path="lib/de.cau.cs.kieler.core-0.15.0.201602160301.jar"/>
<classpathentry exported="true" kind="lib" path="lib/de.cau.cs.kieler.core.kgraph-0.8.0.201602160301.jar"/>
<classpathentry exported="true" kind="lib" path="lib/de.cau.cs.kieler.kiml-0.19.0.201602160301.jar"/>
<classpathentry exported="true" kind="lib" path="lib/de.cau.cs.kieler.klay.force-0.4.0.201602160301.jar"/>
<classpathentry exported="true" kind="lib" path="lib/de.cau.cs.kieler.klay.layered-0.13.0.201602160301.jar"/>
<classpathentry exported="true" kind="lib" path="lib/de.cau.cs.kieler.klay.tree-0.4.0.201602160301.jar"/>
</classpath>
......@@ -35,6 +35,7 @@ Export-Package: org.fortiss.tooling.base.ui,
org.fortiss.tooling.base.ui.extension.base,
org.fortiss.tooling.base.ui.fieldassist,
org.fortiss.tooling.base.ui.layout,
org.fortiss.tooling.base.ui.layout.auto,
org.fortiss.tooling.base.ui.library,
org.fortiss.tooling.base.ui.preferences,
org.fortiss.tooling.base.ui.properties.view,
......@@ -42,4 +43,11 @@ Export-Package: org.fortiss.tooling.base.ui,
org.fortiss.tooling.base.ui.utils,
org.fortiss.tooling.base.ui.widget
Bundle-ClassPath: .,
lib/opal-1.0.4.jar
lib/opal-1.0.4.jar,
lib/com.google.guava-15.0.0.v201403281430.jar,
lib/de.cau.cs.kieler.core-0.15.0.201602160301.jar,
lib/de.cau.cs.kieler.core.kgraph-0.8.0.201602160301.jar,
lib/de.cau.cs.kieler.kiml-0.19.0.201602160301.jar,
lib/de.cau.cs.kieler.klay.force-0.4.0.201602160301.jar,
lib/de.cau.cs.kieler.klay.layered-0.13.0.201602160301.jar,
lib/de.cau.cs.kieler.klay.tree-0.4.0.201602160301.jar
......@@ -4,5 +4,12 @@ bin.includes = META-INF/,\
.,\
plugin.xml,\
icons/,\
lib/opal-1.0.4.jar
lib/opal-1.0.4.jar, \
lib/com.google.guava-15.0.0.v201403281430.jar,\
lib/de.cau.cs.kieler.core-0.15.0.201602160301.jar,\
lib/de.cau.cs.kieler.core.kgraph-0.8.0.201602160301.jar,\
lib/de.cau.cs.kieler.kiml-0.19.0.201602160301.jar,\
lib/de.cau.cs.kieler.klay.force-0.4.0.201602160301.jar,\
lib/de.cau.cs.kieler.klay.layered-0.13.0.201602160301.jar,\
lib/de.cau.cs.kieler.klay.tree-0.4.0.201602160301.jar
src.includes = icons/
File added
File added
File added
File added
File added
File added
File added
/*--------------------------------------------------------------------------+
$Id$
| |
| Copyright 2016 fortiss GmbH |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.base.ui.layout.auto;
import java.util.Map.Entry;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.BasicEMap;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.EMap;
import org.fortiss.tooling.base.model.base.EntryConnectorBase;
import org.fortiss.tooling.base.model.base.ExitConnectorBase;
import org.fortiss.tooling.base.model.element.IConnection;
import org.fortiss.tooling.base.model.element.IConnector;
import org.fortiss.tooling.base.model.element.IHierarchicElement;
import org.fortiss.tooling.base.model.layout.EOrientation;
import org.fortiss.tooling.base.model.layout.ILayoutData;
import org.fortiss.tooling.base.model.layout.ILayoutedModelElement;
import org.fortiss.tooling.base.model.layout.Points;
import org.fortiss.tooling.base.model.layout.impl.PointsImpl;
import org.fortiss.tooling.base.ui.utils.LayoutDataUIUtils;
import org.fortiss.tooling.base.utils.LayoutDataUtils;
import org.fortiss.tooling.kernel.extension.data.ITopLevelElement;
import org.fortiss.tooling.kernel.service.IPersistencyService;
import de.cau.cs.kieler.core.alg.BasicProgressMonitor;
import de.cau.cs.kieler.core.alg.IKielerProgressMonitor;
import de.cau.cs.kieler.core.kgraph.KEdge;
import de.cau.cs.kieler.core.kgraph.KNode;
import de.cau.cs.kieler.core.kgraph.KPort;
import de.cau.cs.kieler.kiml.AbstractLayoutProvider;
import de.cau.cs.kieler.kiml.klayoutdata.KEdgeLayout;
import de.cau.cs.kieler.kiml.klayoutdata.KPoint;
import de.cau.cs.kieler.kiml.klayoutdata.KShapeLayout;
import de.cau.cs.kieler.kiml.options.Direction;
import de.cau.cs.kieler.kiml.options.LayoutOptions;
import de.cau.cs.kieler.kiml.options.PortSide;
import de.cau.cs.kieler.kiml.util.KimlUtil;
import de.cau.cs.kieler.klay.layered.LayeredLayoutProvider;
/**
* This class provides a way to generate a layout for AF3 components using the KIELER framework.
* It does so by generating a KIELER graph from the AF3 model, generate a layout using the KIELER
* layered layout algorithm, and then applies the generated layout to the AF3 model.
*
* @author offtermatt
* @author $Author$
* @version $Rev$
* @ConQAT.Rating YELLOW Hash: BD5764CC45AD86F0D08B4EEED87A00B3
*/
public class KAutoLayout {
/**
* IHierarchicElements for which the layout will be generated, i.e. the children of the selected
* component.
*/
private static EList<IHierarchicElement> containedElements;
/** Node width for af3 layout. */
private static final int nodeWidth = 150;
/** Mapping from components to KNodes. */
private static EMap<IHierarchicElement, KNode> nodesToKNodes;
/** Mapping from output ports to KPorts. */
private static EMap<IConnector, KPort> outputPortsToKPorts;
/** Mapping from input ports to KPorts. */
private static EMap<IConnector, KPort> inputPortsToKPorts;
/** Used to map ports that are neither input nor output ports to KPorts */
private static EMap<IConnector, KPort> genericPortsToKPorts;
/** Mapping from IConnections to KEdges. */
private static EMap<IConnection, KEdge> edgesToKEdges;
/** Mapping from global input ports to KPorts. */
private static EMap<IConnector, KPort> globalInputPortsToKPorts;
/** Mapping from global output ports to KPorts. */
private static EMap<IConnector, KPort> globalOutputPortsToKPorts;
/**
* @param component
* The component that the layout will be generated for. Note that this means that the
* contained elements of this component will be arranged according to the layout, not
* the component itself.
*/
public static void runAutoLayout(IHierarchicElement component) {
KNode parentNode = createKIELERGraph(component);
KShapeLayout par = parentNode.getData(KShapeLayout.class);
par.setProperty(LayoutOptions.DIRECTION, Direction.RIGHT);
par.setProperty(LayoutOptions.INTERACTIVE, true);
AbstractLayoutProvider layoutProvider = new LayeredLayoutProvider();
IKielerProgressMonitor progressMonitor = new BasicProgressMonitor();
layoutProvider.doLayout(parentNode, progressMonitor);
applyLayout(parentNode, component);
}
/**
* Applies the layout that the KNode follows to the IHierarchicElement.
*
* @param template
* The KNode that stores the layout.
* @param layouted
* The component to which the layout should be applied.
*/
private static void applyLayout(KNode template, IHierarchicElement layouted) {
ITopLevelElement topLevelElement =
IPersistencyService.getInstance().getTopLevelElementFor(layouted);
topLevelElement.runAsCommand(() -> {
filterBendPoints(layouted);
for(IHierarchicElement curElement : containedElements) {
KNode curNode = nodesToKNodes.get(curElement);
KShapeLayout curLayout = curNode.getData(KShapeLayout.class);
LayoutDataUtils.setNodeLayoutData((ILayoutedModelElement)curElement,
(int)curLayout.getXpos(), (int)curLayout.getYpos(),
(int)curLayout.getWidth(), (int)curLayout.getHeight());
}
for(Entry<IConnection, KEdge> curEntry : edgesToKEdges) {
KEdge curEdge = curEntry.getValue();
IConnection curConnection = curEntry.getKey();
KEdgeLayout curLayout = curEdge.getData(KEdgeLayout.class);
for(KPoint k : curLayout.getBendPoints()) {
LayoutDataUIUtils.addBendPointToConnection(
(ILayoutedModelElement)curConnection, (int)k.getX(), (int)k.getY());
}
}
setPositionsForPorts(outputPortsToKPorts);
setPositionsForPorts(inputPortsToKPorts);
setPositionsForGlobalPorts(globalInputPortsToKPorts);
setPositionsForGlobalPorts(globalOutputPortsToKPorts);
});
}
/**
* Applies the layout of the specified KPorts to their matching (global) IConnectors. Global
* means in this case that the IConnectors do not have a corresponding node, i.e. because they
* contain connections to a higher-level model.
*
* @param globalPorts
* A bijection from IConnectors to KPorts
*/
private static void setPositionsForGlobalPorts(EMap<IConnector, KPort> globalPorts) {
for(Entry<IConnector, KPort> i : globalPorts) {
IConnector port = i.getKey();
KPort p = i.getValue();
KNode temp = p.getNode();
KShapeLayout lay = temp.getData(KShapeLayout.class);
LayoutDataUtils.setNodePosition((ILayoutedModelElement)port, (int)lay.getXpos(),
(int)lay.getYpos());
}
}
/**
* Applies the layout of the specified KPorts to their matching IConnectors. The IConnectors
* must have corresponding nodes, i.e. they can not contain an IConnection to a higher-level
* model.
*
* @param ports
* A bijection from IConnectors to KPorts.
*/
private static void setPositionsForPorts(EMap<IConnector, KPort> ports) {
for(Entry<IConnector, KPort> ic : ports) {
KPort kp = ic.getValue();
IConnector port = ic.getKey();
KShapeLayout portLayout = kp.getData(KShapeLayout.class);
PortSide side = portLayout.getProperty(LayoutOptions.PORT_SIDE);
EOrientation orientation = null;
switch(side) {
case NORTH:
orientation = EOrientation.NORTH;
break;
case WEST:
orientation = EOrientation.WEST;
break;
case EAST:
orientation = EOrientation.EAST;
break;
case SOUTH:
orientation = EOrientation.SOUTH;
break;
}
LayoutDataUtils.setStickyConnectorLayoutData((ILayoutedModelElement)port, orientation,
(int)portLayout.getYpos());
}
}
/**
* Removes all bendpoints in connections contained in the specified component.
*
* @param component
*/
private static void filterBendPoints(IHierarchicElement component) {
EList<IConnection> clears = component.getConnections();
for(IConnection c : clears) {
EList<ILayoutData> l = ((ILayoutedModelElement)c).getLayoutData();
for(ILayoutData i : l) {
if(i instanceof Points || i instanceof PointsImpl) {
((Points)i).getPoints().clear();
}
}
}
}
/**
* @param component
* The af3 component for which the KNode graph should be created
* @return A KNode graph with the same structure as the graph contained in the component.
*/
private static KNode createKIELERGraph(IHierarchicElement component) {
KNode parentNode = KimlUtil.createInitializedNode();
containedElements = component.getContainedElements();
nodesToKNodes = new BasicEMap<IHierarchicElement, KNode>();
outputPortsToKPorts = new BasicEMap<IConnector, KPort>();
inputPortsToKPorts = new BasicEMap<IConnector, KPort>();
edgesToKEdges = new BasicEMap<IConnection, KEdge>();
genericPortsToKPorts = new BasicEMap<IConnector, KPort>();
// create nodes
for(IHierarchicElement curElement : containedElements) {
KNode curKNode = KimlUtil.createInitializedNode();
KShapeLayout curLayout = curKNode.getData(KShapeLayout.class);
EList<IConnector> inputPorts = new BasicEList<IConnector>();
EList<IConnector> outputPorts = new BasicEList<IConnector>();
EList<IConnector> genericPorts = new BasicEList<IConnector>();
EList<IConnector> ports = curElement.getConnectors();
for(IConnector curConnector : ports) {
if(curConnector instanceof EntryConnectorBase) {
inputPorts.add(curConnector);
} else if(curConnector instanceof ExitConnectorBase) {
outputPorts.add(curConnector);
} else {
outputPorts.add(curConnector);
genericPorts.add(curConnector);
}
}
int portNmbr = Math.max(inputPorts.size(), outputPorts.size());
curLayout.setHeight(Math.max(portNmbr * 15, 100));
curLayout.setWidth(nodeWidth);
curKNode.setParent(parentNode);
nodesToKNodes.put(curElement, curKNode);
for(IConnector curPort : outputPorts) {
KPort k = KimlUtil.createInitializedPort();
k.setNode(curKNode);
outputPortsToKPorts.put(curPort, k);
if(genericPorts.contains(curPort)) {
genericPortsToKPorts.put(curPort, k);
}
}
for(IConnector curPort : inputPorts) {
KPort k = KimlUtil.createInitializedPort();
k.setNode(curKNode);
inputPortsToKPorts.put(curPort, k);
}
}
// create virtual nodes for global ports
globalInputPortsToKPorts = new BasicEMap<IConnector, KPort>();
globalOutputPortsToKPorts = new BasicEMap<IConnector, KPort>();
for(IConnector curPort : component.getConnectors()) {
KNode virtual = KimlUtil.createInitializedNode();
KShapeLayout virtualLayout = virtual.getData(KShapeLayout.class);
virtualLayout.setHeight(50);
virtualLayout.setWidth(50);
virtual.setParent(parentNode);
KPort k = KimlUtil.createInitializedPort();
k.setNode(virtual);
if(curPort instanceof ExitConnectorBase) {
globalOutputPortsToKPorts.put(curPort, k);
} else {
globalInputPortsToKPorts.put(curPort, k);
outputPortsToKPorts.put(curPort, k);
}
}
// create edges
for(Entry<IConnector, KPort> out : outputPortsToKPorts) {
for(IConnection ic : out.getKey().getOutgoing()) {
KEdge curEdge = KimlUtil.createInitializedEdge();
KPort sourceport = out.getValue();
curEdge.setSource(sourceport.getNode());
curEdge.setSourcePort(sourceport);
sourceport.getEdges().add(curEdge);
KPort targetport = inputPortsToKPorts.get(ic.getTarget());
if(targetport == null) {
targetport = globalOutputPortsToKPorts.get(ic.getTarget());
}
if(targetport == null) {
targetport = genericPortsToKPorts.get(ic.getTarget());
}
curEdge.setTargetPort(targetport);
curEdge.setTarget(targetport.getNode());
targetport.getEdges().add(curEdge);
edgesToKEdges.put(ic, curEdge);
}
}
return parentNode;
}
}
<!--
$Id$
@version $Rev$
@ConQAT.Rating YELLOW Hash: 15C0BF8074343CAA2911742A4DEA6B96
-->
<body>
This package provides automatic layouting of diagrams.
The current implementation is based on the KIELER Project (https://rtsys.informatik.uni-kiel.de/confluence/display/KIELER/Home).
</body>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment