use of org.osate.ge.internal.diagram.runtime.DiagramModification 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);
}
}
}
}
}
}
use of org.osate.ge.internal.diagram.runtime.DiagramModification in project osate2 by osate.
the class DiagramElementLayoutUtil method applyConnectionLayout.
private static void applyConnectionLayout(final LayoutMapping mapping, final DiagramModification m) {
// Modify Connections
for (Entry<ElkGraphElement, Object> e : mapping.getGraphMap().entrySet()) {
final ElkGraphElement elkElement = e.getKey();
final Object mappedValue = e.getValue();
if (!(elkElement instanceof ElkEdge)) {
continue;
}
final ElkEdge edge = (ElkEdge) elkElement;
// Ignore edges which do not have exactly one section. This is usually the case where it is a long hierarchical connection that has 0 sections
if (edge.getSections().size() != 1) {
continue;
}
final ElkEdgeSection edgeSection = edge.getSections().get(0);
if (!(mappedValue instanceof DiagramElement)) {
continue;
}
final DiagramElement de = (DiagramElement) mappedValue;
if (!(de.getGraphic() instanceof AgeConnection)) {
continue;
}
final AgeConnection connection = (AgeConnection) de.getGraphic();
// Flow indicators have a position representing where the indicator ends.
if (connection.isFlowIndicator && edge.getTargets().size() == 1) {
final ElkPort flowIndicatorEndPort = (ElkPort) edge.getTargets().get(0);
final ElkShape flowIndicatorEndPortShape = (ElkShape) flowIndicatorEndPort.eContainer();
m.setPosition(de, new Point(flowIndicatorEndPortShape.getX(), flowIndicatorEndPortShape.getY()));
}
// Don't update connections if it wasn't updated. This prevents updating bendpoints to invalid values if an edge is not layed out.
if (edgeSection.eIsSet(ElkGraphPackage.eINSTANCE.getElkEdgeSection_StartX()) && edgeSection.eIsSet(ElkGraphPackage.eINSTANCE.getElkEdgeSection_EndX())) {
final List<Point> bendpointsInParentCoordinateSystem = edgeSection.getBendPoints().stream().map(bp -> new Point(bp.getX(), bp.getY())).collect(Collectors.toCollection(LinkedList::new));
//
// Set bendpoints
//
// Add the start and end points to the bendpoints list if the the start/end element is not a port.
// For ports the start and end points are unnecessary and will actually be located inside the port graphic.
final boolean srcIsPort = edge.getSources().size() == 1 ? edge.getSources().get(0) instanceof ElkPort : false;
final boolean dstIsPort = edge.getTargets().size() == 1 ? edge.getTargets().get(0) instanceof ElkPort : false;
if (!srcIsPort) {
bendpointsInParentCoordinateSystem.add(0, new Point(edgeSection.getStartX(), edgeSection.getStartY()));
}
if (!dstIsPort) {
bendpointsInParentCoordinateSystem.add(new Point(edgeSection.getEndX(), edgeSection.getEndY()));
}
// Adjust newly added bendpoints so that the connection arrows will face the appropriate direction
if (!srcIsPort && bendpointsInParentCoordinateSystem.size() >= 2) {
bendpointsInParentCoordinateSystem.set(0, getAdjacentPoint(bendpointsInParentCoordinateSystem.get(0), bendpointsInParentCoordinateSystem.get(1), START_AND_END_BENDPOINT_DISTANCE));
}
if (!dstIsPort && bendpointsInParentCoordinateSystem.size() >= 2) {
bendpointsInParentCoordinateSystem.set(bendpointsInParentCoordinateSystem.size() - 1, getAdjacentPoint(bendpointsInParentCoordinateSystem.get(bendpointsInParentCoordinateSystem.size() - 1), bendpointsInParentCoordinateSystem.get(bendpointsInParentCoordinateSystem.size() - 2), START_AND_END_BENDPOINT_DISTANCE));
}
// Get the absolute coordinate in the diagram of the edge's ELK container.
final Point elkContainerPosition;
if (edge.getContainingNode() == mapping.getLayoutGraph()) {
// Node available. Use the first and only child of the top level ELK node.
if (mapping.getLayoutGraph().getChildren().size() == 1) {
final ElkNode topLayoutElkNode = mapping.getLayoutGraph().getChildren().get(0);
final Point topLayoutElkNodePosition = getAbsolutePosition((DiagramNode) mapping.getGraphMap().get(topLayoutElkNode));
elkContainerPosition = new Point(topLayoutElkNodePosition.x - topLayoutElkNode.getX(), topLayoutElkNodePosition.y - topLayoutElkNode.getY());
} else {
elkContainerPosition = new Point(0, 0);
}
} else {
elkContainerPosition = getAbsolutePosition((DiagramNode) mapping.getGraphMap().get(edge.getContainingNode()));
}
final List<Point> bendpointsInAbsoluteCoordinateSystem = bendpointsInParentCoordinateSystem.stream().map(p -> new Point(p.x + elkContainerPosition.x, p.y + elkContainerPosition.y)).collect(Collectors.toList());
m.setBendpoints(de, bendpointsInAbsoluteCoordinateSystem);
// For the midpoint calculation, the start and end points are needed. Add them if they have not already been added.
if (srcIsPort) {
bendpointsInParentCoordinateSystem.add(0, new Point(edgeSection.getStartX(), edgeSection.getStartY()));
}
if (dstIsPort) {
bendpointsInParentCoordinateSystem.add(new Point(edgeSection.getEndX(), edgeSection.getEndY()));
}
// Set Label Positions
setLabelPositionsForEdge(mapping, m, edge, findMidpoint(bendpointsInParentCoordinateSystem));
}
}
}
use of org.osate.ge.internal.diagram.runtime.DiagramModification in project osate2 by osate.
the class DiagramElementLayoutUtil method layoutFlowIndicators.
/**
* Sets the position and bendpoints of all specified flow indicators. Intended to provide a default position for flow indicators in cases
* where only contents are being layed out. This layout will be replaced by the ELK produced layout when the flow indicator's container
* is layed out.
*
* This function simply positions flow indicators such that they are in a after existing indicators.
* @param m is the modification to use to modify the diagram.
* @param flowIndicatorsToLayout is the stream of flow indicators to layout.
* @param layoutInfoProvider is the source for layout info needed to determine source anchor points.
*/
public static void layoutFlowIndicators(final DiagramModification m, final Stream<DiagramElement> flowIndicatorsToLayout, final LayoutInfoProvider layoutInfoProvider) {
Objects.requireNonNull(flowIndicatorsToLayout, "flowIndicators must not be null");
// Create set of a start elements in which we are interested.
final Set<DiagramNode> startElements = flowIndicatorsToLayout.map(n -> n.getStartElement()).collect(Collectors.toSet());
if (startElements.isEmpty()) {
return;
}
// Search diagram and build a multimap mapping start elements to the flow indicators which reference them.
final ArrayListMultimap<DiagramElement, DiagramElement> startElementToFlowIndicators = ArrayListMultimap.create();
m.getDiagram().getAllDescendants().filter(q -> q instanceof DiagramElement && DiagramElementPredicates.isFlowIndicator((DiagramElement) q)).forEachOrdered(q -> {
final DiagramElement e = (DiagramElement) q;
final DiagramElement start = e.getStartElement();
if (startElements.contains(start)) {
startElementToFlowIndicators.put(start, e);
}
});
// Process each start element
for (DiagramElement startElement : startElementToFlowIndicators.keySet()) {
// Skip start elements that haven't been positioned
if (!startElement.hasPosition()) {
continue;
}
// Skip if unable to determine what side the start element is on. Flow indicators are only supported when there is a source element which is docked.
final DockArea dockArea = getNonGroupDockArea(startElement);
if (dockArea == null) {
continue;
}
// Sort by X or Y based on dock area. Flow indicators without a position are sorted at the end.
final List<DiagramElement> flowIndicatorsForStartElement = startElementToFlowIndicators.get(startElement);
flowIndicatorsForStartElement.sort((e1, e2) -> {
if (e1.hasPosition() && e2.hasPosition()) {
if (dockArea.isLeftOrRight()) {
return Double.compare(e1.getY(), e2.getY());
} else {
return Double.compare(e1.getX(), e2.getX());
}
} else if (e1.hasPosition()) {
return -1;
} else if (e2.hasPosition()) {
return 1;
} else {
return 0;
}
});
// Flow indicators are positions based on the first undocked container. Need to find that container.
final DiagramElement indicatorContainer = DiagramElementUtil.getUndockedDiagramElement(flowIndicatorsForStartElement.get(0).getParent());
// Skip if we are unable to determine the container or if the container doesn't have a size.
if (indicatorContainer == null || !indicatorContainer.hasSize()) {
continue;
}
final Point containerAbsPosition = getAbsolutePosition(indicatorContainer);
//
// Determine how to adjust position of bendpoints and indicator positions based on the dock area
//
final Point startAnchorPosition = getPortAnchorOffset(startElement, dockArea, containerAbsPosition, layoutInfoProvider);
final double initialPositionOffsetX;
final double initialPositionOffsetY;
final double positionIncrementX;
final double positionIncrementY;
switch(dockArea) {
case LEFT:
initialPositionOffsetX = INCREMENTAL_FLOW_INDICATOR_PRIMARY_OFFSET;
initialPositionOffsetY = 0;
positionIncrementX = 0.0;
positionIncrementY = INCREMENTAL_FLOW_INDICATOR_SECONDARY_INCREMENT;
break;
case RIGHT:
initialPositionOffsetX = -INCREMENTAL_FLOW_INDICATOR_PRIMARY_OFFSET;
initialPositionOffsetY = 0;
positionIncrementX = 0;
positionIncrementY = INCREMENTAL_FLOW_INDICATOR_SECONDARY_INCREMENT;
break;
case TOP:
initialPositionOffsetX = 0;
initialPositionOffsetY = INCREMENTAL_FLOW_INDICATOR_PRIMARY_OFFSET;
positionIncrementX = INCREMENTAL_FLOW_INDICATOR_SECONDARY_INCREMENT;
positionIncrementY = 0;
break;
case BOTTOM:
initialPositionOffsetX = 0;
initialPositionOffsetY = -INCREMENTAL_FLOW_INDICATOR_PRIMARY_OFFSET;
positionIncrementX = INCREMENTAL_FLOW_INDICATOR_SECONDARY_INCREMENT;
positionIncrementY = 0;
break;
case GROUP:
default:
// Our dock area should never have the group value and all other values should be handled
throw new GraphicalEditorException("Unexpected case: " + dockArea);
}
// Calculate absolute position for the start anchor. Used for bendpoints
final Point startAnchorAbsPosition = new Point(containerAbsPosition.x + startAnchorPosition.x, containerAbsPosition.y + startAnchorPosition.y);
// Determine initial of the first flow indicator relative to it's container.
// This is only used when there all flow indicators do not have a position
final Point firstPosition = new Point(startAnchorPosition.x + initialPositionOffsetX, startAnchorPosition.y + initialPositionOffsetY);
Point nextPosition = firstPosition;
for (DiagramElement indicator : flowIndicatorsForStartElement) {
final Point currentPosition;
if (indicator.hasPosition()) {
currentPosition = indicator.getPosition();
} else {
// Set the position
currentPosition = nextPosition;
m.setPosition(indicator, nextPosition);
// The first flow indicator should not need bendpoints, to reset them
if (nextPosition.equals(firstPosition)) {
m.setBendpoints(indicator, ImmutableList.of());
} else {
// Set bendpoints
final Point bp1 = new Point(startAnchorAbsPosition.x + (initialPositionOffsetX * INCREMENTAL_FLOW_INDICATOR_BENDPOINT_OFFSET_SCALING), startAnchorAbsPosition.y + (initialPositionOffsetY * INCREMENTAL_FLOW_INDICATOR_BENDPOINT_OFFSET_SCALING));
final Point positionAbs = new Point(containerAbsPosition.x + nextPosition.x, +containerAbsPosition.y + nextPosition.y);
final Point bp2 = new Point(positionAbs.x - (initialPositionOffsetX * (1.0 - INCREMENTAL_FLOW_INDICATOR_BENDPOINT_OFFSET_SCALING)), positionAbs.y - (initialPositionOffsetY * (1.0 - INCREMENTAL_FLOW_INDICATOR_BENDPOINT_OFFSET_SCALING)));
m.setBendpoints(indicator, ImmutableList.of(bp1, bp2));
}
}
// Determine the next position
nextPosition = new Point(currentPosition.x + positionIncrementX, currentPosition.y + positionIncrementY);
}
}
}
use of org.osate.ge.internal.diagram.runtime.DiagramModification in project osate2 by osate.
the class DiagramElementLayoutUtil method shiftRelatedConnections.
/**
* Shifts the bendpoints of all connections for which both endpoints are contained within the specified elements.
* Shifts bendpoints of flow indicators if start elements are contained within the specified elements.
* Shifts position of flow indicators if start elements are contained within the specified elements and the flow indicator container is not
* is not in movedElements.
*
* @param movedElements are the element which have been moved.
* @param delta the amount to shift the bendpoints
* @param m the modification that will be used to update the bendpoints
* @param shiftBendpoints whether to shift bendpoints
* @param shiftFlowIndicatorPositions whether to shift flow indicator positions.
* @param checkDescendants whether to check descendants of the specified elements when looking for connections
*/
public static void shiftRelatedConnections(final Stream<DiagramElement> movedElements, final org.osate.ge.graphics.Point delta, final DiagramModification m, boolean shiftBendpoints, boolean shiftFlowIndicatorPositions, final boolean checkDescendants) {
final Set<BusinessObjectContext> movedElementsSet = movedElements.collect(Collectors.toSet());
// Build a set containing the moved elements and all of their descendant which are represented as shapes
final Set<BusinessObjectContext> diagramElements = checkDescendants ? movedElementsSet.stream().flatMap(de -> Stream.concat(Stream.of(de), de.getAllDescendants())).collect(Collectors.toSet()) : movedElementsSet;
final Stream<DiagramElement> connections = m.getDiagram().getAllDiagramNodes().filter(q -> q instanceof DiagramElement && DiagramElementPredicates.isConnection((DiagramElement) q)).map(DiagramElement.class::cast);
// Iterate over all the connections in the diagram and update their bendpoints if their ends are in the set above.
connections.forEachOrdered(connection -> {
final DiagramElement startElement = connection.getStartElement();
final DiagramElement endElement = connection.getEndElement();
final boolean isFlowIndicator = ((AgeConnection) connection.getGraphic()).isFlowIndicator;
if (diagramElements.contains(startElement) && (diagramElements.contains(endElement) || isFlowIndicator)) {
if (shiftBendpoints) {
shiftBendpoints(connection, delta, m);
}
// Shift flow indicator positions
if (shiftFlowIndicatorPositions && isFlowIndicator && connection.hasPosition()) {
// Flow indicator positions are relative to the container of the flow indicator.
// If the flow indicator's ancestor has moved, then do not shift the flow indicator's position
boolean ancestorHasMoved = false;
for (DiagramNode tmp = connection.getParent(); tmp != null; tmp = tmp.getParent()) {
if (movedElementsSet.contains(tmp)) {
ancestorHasMoved = true;
}
}
if (!ancestorHasMoved) {
final DockArea startDockArea = getNonGroupDockArea(startElement);
m.setPosition(connection, new org.osate.ge.graphics.Point(connection.getX() + (startDockArea == null || !startDockArea.isLeftOrRight() ? delta.x : 0), connection.getY() + (startDockArea == null || startDockArea.isLeftOrRight() ? delta.y : 0)));
}
}
}
});
}
use of org.osate.ge.internal.diagram.runtime.DiagramModification in project osate2 by osate.
the class DiagramUpdater method updateStructure.
/**
* Updates the structure of the diagram based on the business object tree.
* Creates/Unghosts elements to match the business object tree. Ghosts diagram elements which are not in the diagram element tree.
* @param m
* @param container
* @param bos
*/
private void updateStructure(final DiagramModification m, final DiagramNode container, final Collection<BusinessObjectNode> bos) {
for (final BusinessObjectNode n : bos) {
// Get existing element if it exists.
DiagramElement element = container.getChildByRelativeReference(n.getRelativeReference());
// Create the element if it does not exist
if (element == null) {
final DiagramElement removedGhost = removeGhost(container, n.getRelativeReference());
if (removedGhost == null) {
final BusinessObjectHandler boh = infoProvider.getApplicableBusinessObjectHandler(n.getBusinessObject());
if (boh == null) {
// Ignore the object
continue;
}
element = new DiagramElement(container, n.getBusinessObject(), boh, n.getRelativeReference(), n.getId());
} else {
element = removedGhost;
m.updateBusinessObject(element, n.getBusinessObject(), n.getRelativeReference());
}
m.addElement(element);
} else {
// Update the business object and relative reference. Although the reference matches. The business object may be new and the
// relative reference may have case differences.
m.updateBusinessObject(element, n.getBusinessObject(), n.getRelativeReference());
}
// Set the business object handler if it is null
if (element.getBusinessObjectHandler() == null) {
final BusinessObjectHandler boh = infoProvider.getApplicableBusinessObjectHandler(n.getBusinessObject());
if (boh == null) {
ghostAndRemove(m, element);
continue;
} else {
m.setBusinessObjectHandler(element, boh);
}
}
// Update the element's children
updateStructure(m, element, n.getChildren());
}
// If the collections are the same size, there is nothing to remove
if (bos.size() != container.getChildren().size()) {
// Build Set of Relative References of All the Objects in the Business Object Tree
final Set<RelativeBusinessObjectReference> boTreeRelativeReferenceSet = bos.stream().map((n) -> n.getRelativeReference()).collect(Collectors.toCollection(HashSet::new));
Iterator<DiagramElement> childrenIt = container.getChildren().iterator();
while (childrenIt.hasNext()) {
final DiagramElement child = childrenIt.next();
if (!boTreeRelativeReferenceSet.contains(child.getRelativeReference())) {
ghostAndRemove(m, child);
}
}
}
}
Aggregations