use of org.osate.ge.internal.diagram.runtime.DiagramElement in project osate2 by osate.
the class DiagramElementLayoutUtil method getNodesToLayoutIncrementally.
private static Set<DiagramNode> getNodesToLayoutIncrementally(final DiagramNode node, final IncrementalLayoutMode mode, final Set<DiagramNode> results) {
final boolean alwaysLayoutContainer = mode != IncrementalLayoutMode.LAYOUT_CONTENTS;
for (final DiagramElement child : node.getChildren()) {
if (DiagramElementPredicates.isShape(child)) {
final boolean positionIsSet = child.hasPosition() || !DiagramElementPredicates.isMoveableShape(child);
final boolean sizeIsSet = child.hasSize() || !DiagramElementPredicates.isResizeable(child);
// This occurs when a user has created an element using the palette
if (positionIsSet && !sizeIsSet) {
results.add(child);
} else {
if (sizeIsSet && positionIsSet) {
getNodesToLayoutIncrementally(child, mode, results);
} else {
// If always layout container is specified, layout container
// If container does not have any layed out shapes, layout container.
final boolean layoutContainer = alwaysLayoutContainer || !hasLayedOutShapes(node.getChildren());
if (layoutContainer) {
results.add(node);
break;
} else {
results.add(child);
}
}
}
} else if (DiagramElementPredicates.isConnection(child)) {
final AgeConnection c = (AgeConnection) child.getGraphic();
if (c.isFlowIndicator) {
// Layout the flow indicator's not docked container if its position has not been set
if (child.getStartElement() != null && (!child.isBendpointsSet() || !child.hasPosition())) {
// If we should lay out container than lay out the container of the flow indicator
if (alwaysLayoutContainer) {
DiagramElement undockedContainer = DiagramElementUtil.getUndockedDiagramElement(child.getStartElement().getParent());
if (undockedContainer != null) {
results.add(undockedContainer);
}
} else {
// Otherwise, layout the flow indicator
results.add(child);
}
}
} else if (alwaysLayoutContainer) {
// Only layout the connection if its bendpoints have not been set regardless of whether it has any bendpoints.
if (child.getStartElement() != null && child.getEndElement() != null && !child.isBendpointsSet()) {
final Optional<BusinessObjectContext> ancestor = BusinessObjectContext.getFirstCommonAncestor(child.getStartElement().getParent(), child.getEndElement().getParent());
if (ancestor.isPresent()) {
results.add((DiagramNode) ancestor.get());
}
}
} else if (isFeatureSelfLoopConnection(child) && !child.isBendpointsSet()) {
results.add(child);
}
}
}
return results;
}
use of org.osate.ge.internal.diagram.runtime.DiagramElement in project osate2 by osate.
the class DiagramElementLayoutUtil method getNonGroupDockArea.
/**
* Walks up the tree from the given node and returns the first dock area that isn't the group dock area.
* Checks the specified diagram node and then ancestors.
* @param diagramNode is the diagram for which to return the non group docker area.
* @return the first dock area that isn't the group dock area.
*/
public static DockArea getNonGroupDockArea(DiagramNode diagramNode) {
DockArea result = null;
do {
if (!(diagramNode instanceof DiagramElement)) {
result = null;
break;
}
result = ((DiagramElement) diagramNode).getDockArea();
diagramNode = diagramNode.getParent();
} while (result != null && result == DockArea.GROUP);
return result;
}
use of org.osate.ge.internal.diagram.runtime.DiagramElement in project osate2 by osate.
the class DiagramElementLayoutUtil method getAbsolutePosition.
/**
* Gets the absolute position of a node. This absolute position only considers the positions of shapes.
* Connections are ignored.
* @param dn the node for which to get the absolute position.
* @return the absolute position.
*/
public static Point getAbsolutePosition(final DiagramNode dn) {
int x = 0;
int y = 0;
for (DiagramNode tmp = dn; tmp instanceof DiagramElement; tmp = tmp.getParent()) {
final DiagramElement tmpDe = (DiagramElement) tmp;
if (tmpDe.getGraphic() instanceof AgeShape) {
// Ignore connections in case the X and Y values are not 0.
x += tmpDe.getX();
y += tmpDe.getY();
}
}
return new Point(x, y);
}
use of org.osate.ge.internal.diagram.runtime.DiagramElement in project osate2 by osate.
the class DiagramElementLayoutUtil method layout.
private static void layout(final DiagramModification m, final Collection<? extends DiagramNode> nodesToLayout, final StyleProvider styleProvider, final LayoutInfoProvider layoutInfoProvider, final LayoutOptions options) {
Objects.requireNonNull(nodesToLayout, "nodesToLayout must not be null");
try {
// Layout the nodes
final RecursiveGraphLayoutEngine layoutEngine = new RecursiveGraphLayoutEngine();
for (final DiagramNode dn : nodesToLayout) {
LayoutMapping mapping;
ElkNode layoutGraph;
// Perform the first layout. This layout will not include nested ports. This will allow ELK additional flexibility when determining port
// placement.
mapping = ElkGraphBuilder.buildLayoutGraph(dn, styleProvider, layoutInfoProvider, options, !options.layoutPortsOnDefaultSides, ElkGraphBuilder.FixedPortPositionProvider.NO_OP);
layoutGraph = mapping.getLayoutGraph();
layoutGraph.setProperty(CoreOptions.ALGORITHM, LAYOUT_ALGORITHM);
applyProperties(dn, mapping);
LayoutDebugUtil.saveElkGraphToDebugProject(layoutGraph, "pass1");
layoutEngine.layout(layoutGraph, new BasicProgressMonitor());
// nested ports and performing edge routing.
if (layoutGraph.getProperty(AgeLayoutOptions.NESTED_PORTS_WERE_OMITTED)) {
final LayoutMapping initialLayoutMapping = mapping;
mapping = ElkGraphBuilder.buildLayoutGraph(dn, styleProvider, layoutInfoProvider, options, false, new ElkGraphBuilder.FixedPortPositionProvider() {
@Override
public PortSide getPortSide(final DiagramElement de) {
final ElkGraphElement ge = initialLayoutMapping.getGraphMap().inverse().get(de);
if (ge instanceof ElkPort) {
return ge.getProperty(CoreOptions.PORT_SIDE);
}
return null;
}
@Override
public Double getPortPosition(final DiagramElement de) {
final ElkGraphElement ge = initialLayoutMapping.getGraphMap().inverse().get(de);
if (ge instanceof ElkPort) {
final ElkPort port = (ElkPort) ge;
final PortSide ps = port.getProperty(CoreOptions.PORT_SIDE);
if (PortSide.SIDES_EAST_WEST.contains(ps)) {
return port.getY();
} else {
return port.getX();
}
}
return null;
}
});
layoutGraph = mapping.getLayoutGraph();
layoutGraph.setProperty(CoreOptions.ALGORITHM, LAYOUT_ALGORITHM);
applyProperties(dn, mapping);
LayoutDebugUtil.saveElkGraphToDebugProject(layoutGraph, "pass2");
layoutEngine.layout(layoutGraph, new BasicProgressMonitor());
}
LayoutDebugUtil.saveElkGraphToDebugProject(layoutGraph, "final");
applyShapeLayout(mapping, m);
applyConnectionLayout(mapping, m);
// Layout feature self loop connections. These are omitted from the ELK based layout.
dn.getAllDiagramNodes().filter(DiagramElementLayoutUtil::isFeatureSelfLoopConnection).map(DiagramElement.class::cast).forEachOrdered(de -> layoutFeatureSelfLoopConnection(de, m, layoutInfoProvider));
}
} catch (final RuntimeException ex) {
// If a layout error occurs, display the exception but do not rethrow. This is so that the operation that attempted to do the layout will continue.
// This is important because otherwise simple operations such a adding elements to the diagram will completely fail. Suppressing the error will
// degrade performance but allow the user to keep working and should ensure things stay in a valid state.
// It would be best for other parts of the code to handle exceptions properly to avoid entering into an invalid state but this is the best
// workaround.
final Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, "A layout error occured.", ex);
StatusManager.getManager().handle(status, StatusManager.SHOW | StatusManager.LOG);
}
}
use of org.osate.ge.internal.diagram.runtime.DiagramElement in project osate2 by osate.
the class DiagramElementLayoutUtil method layoutIncrementally.
/**
* Performs layout on elements in the specified diagram which have not been laid out.
* @param diagram the diagram for which to perform the incremental layout
* @param mod the modification to use to modify the diagram
* @param layoutInfoProvider the layout info provider which provides additional information required for laying out the diagram
*/
public static void layoutIncrementally(final AgeDiagram diagram, final DiagramModification mod, final LayoutInfoProvider layoutInfoProvider) {
Objects.requireNonNull(diagram, "diagram must not be null");
Objects.requireNonNull(mod, "mod must not be null");
Objects.requireNonNull(layoutInfoProvider, "layoutInfoProvider must not be null");
final IncrementalLayoutMode currentLayoutMode = LayoutPreferences.getCurrentIncrementalLayoutMode();
// Get all the nodes that need to be layed out.
final Set<DiagramNode> unfilteredNodesToLayout = getNodesToLayoutIncrementally(diagram, currentLayoutMode, new HashSet<>());
if (unfilteredNodesToLayout.size() == 0) {
return;
}
// Lay our flow indicators. In the container is eventually layed out, this will be replaced but in cases where that is not the case,
// we provide a default layout. Flow indicators are connections and as such will be filtered in the next step.
layoutFlowIndicators(mod, unfilteredNodesToLayout.stream().filter(DiagramNodePredicates::isFlowIndicator).map(DiagramElement.class::cast), layoutInfoProvider);
final Collection<DiagramNode> nodesToLayout = DiagramElementLayoutUtil.filterUnnecessaryNodes(unfilteredNodesToLayout, currentLayoutMode == IncrementalLayoutMode.LAYOUT_DIAGRAM);
if (nodesToLayout.size() == 0) {
// If the filtered node list is empty then the unfiltered list still contain feature self loop connections that need to be layed out.
unfilteredNodesToLayout.stream().filter(DiagramElementLayoutUtil::isFeatureSelfLoopConnection).map(DiagramElement.class::cast).forEachOrdered(de -> layoutFeatureSelfLoopConnection(de, mod, layoutInfoProvider));
return;
}
final LayoutOptions layoutOptions = LayoutOptions.createFromPreferences();
if (currentLayoutMode == IncrementalLayoutMode.LAYOUT_DIAGRAM) {
layout(INCREMENTAL_LAYOUT_LABEL, diagram, layoutInfoProvider, layoutOptions);
} else {
layout(mod, nodesToLayout, new StyleCalculator(diagram.getConfiguration(), StyleProvider.EMPTY), layoutInfoProvider, layoutOptions);
// Set Positions of elements which do not have a position set.
for (final DiagramNode dn : nodesToLayout) {
if (dn instanceof DiagramElement) {
final DiagramElement de = (DiagramElement) dn;
if (!de.hasPosition()) {
if (de.getDockArea() == null) {
mod.setPosition(de, new Point(0.0, 0.0));
} else if (de.getDockArea() != DockArea.GROUP && de.getParent() instanceof DiagramElement) {
final DiagramElement parent = (DiagramElement) de.getParent();
final DockingPosition defaultDockingPosition = de.getGraphicalConfiguration().getDefaultDockingPosition();
final DockArea defaultDockArea = DockArea.fromDockingPosition(defaultDockingPosition);
if (parent.hasSize()) {
final Stream<DiagramElement> otherElementsAlongSide = parent.getChildren().stream().filter(c -> c.hasPosition() && c.hasSize() && c.getDockArea() == defaultDockArea);
// Determine the position of the new element along it's preferred docking position.
double locationAlongSide;
if (defaultDockingPosition == DockingPosition.TOP || defaultDockingPosition == DockingPosition.BOTTOM) {
locationAlongSide = otherElementsAlongSide.max(Comparator.comparingDouble(c -> c.getY())).map(c -> c.getX() + c.getWidth()).orElse(0.0);
} else {
locationAlongSide = otherElementsAlongSide.max(Comparator.comparingDouble(c -> c.getY())).map(c -> c.getY() + c.getHeight()).orElse(0.0);
}
// Set position based on the docking position
switch(defaultDockingPosition) {
case TOP:
mod.setPosition(de, new Point(locationAlongSide, 0));
break;
case BOTTOM:
mod.setPosition(de, new Point(locationAlongSide, parent.getHeight()));
break;
case LEFT:
mod.setPosition(de, new Point(0, locationAlongSide));
break;
case RIGHT:
mod.setPosition(de, new Point(parent.getWidth(), locationAlongSide));
break;
default:
break;
}
}
mod.setDockArea(de, defaultDockArea);
}
}
}
}
}
}
Aggregations