use of org.osate.ge.internal.diagram.runtime.DiagramNode 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.DiagramNode 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.DiagramNode 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.DiagramNode in project osate2 by osate.
the class DiagramElementLayoutUtil method applyProperties.
/**
* Sets the ELK properties of elements in the specified layout mapping based on the layout options.
* @param layoutMapping
*/
private static void applyProperties(final DiagramNode rootDiagramNode, final LayoutMapping layoutMapping) {
// Set the minimum node size based on the ports and their assigned sides.
final IGraphElementVisitor minNodeSizeVisitor = element -> {
if (element instanceof ElkNode) {
final ElkNode n = (ElkNode) element;
final double maxLabelWidth = n.getLabels().stream().mapToDouble(l -> l.getWidth()).max().orElse(0.0);
final double labelHeightSum = n.getLabels().stream().mapToDouble(l -> l.getHeight()).sum();
// Determine max width for ports on the left and right sides
final double maxLeftPortWidth = n.getPorts().stream().filter(p -> p.getProperty(CoreOptions.PORT_SIDE) == PortSide.WEST).mapToDouble(p -> p.getWidth()).max().orElse(0.0);
final double maxRightPortWidth = n.getPorts().stream().filter(p -> p.getProperty(CoreOptions.PORT_SIDE) == PortSide.EAST).mapToDouble(p -> p.getWidth()).max().orElse(0.0);
final DiagramNode dn = (DiagramNode) layoutMapping.getGraphMap().get(n);
double minWidth = 0;
if (n.getProperty(CoreOptions.NODE_LABELS_PLACEMENT).contains(NodeLabelPlacement.H_CENTER)) {
// Ensure the minimum width is such that the label can be centered without overlapping with ports.
// This happens because ports are inside the node due to the PORT_BORDER_OFFSET and ELK centers the labels in the remaining space.
final double widthForPorts = 2 * Math.max(maxLeftPortWidth, maxRightPortWidth);
minWidth = Math.max(40, maxLabelWidth + widthForPorts + PORT_WIDTH_PADDING);
} else {
final double widthForPorts = maxLeftPortWidth + maxRightPortWidth + PORT_WIDTH_PADDING;
minWidth = Math.max(40, Math.max(maxLabelWidth, widthForPorts));
}
double minHeight = Math.max(35, labelHeightSum);
if (dn instanceof DiagramElement) {
final DiagramElement de = ((DiagramElement) dn);
// Special min height handling for initial modes
final Graphic graphic = de.getGraphic();
if (graphic instanceof AgeShape && !((AgeShape) graphic).isResizeable() && de.hasSize()) {
final Dimension dim = de.getSize();
minHeight = dim.height;
minWidth = dim.width;
// Adjust size constraints for fixed sized shapes which do not have contents.
if (n.getChildren().size() == 0 || n.getLabels().size() == 0 && n.getPorts().size() == 0) {
final EnumSet<SizeConstraint> nodeSizeConstraints = EnumSet.of(SizeConstraint.MINIMUM_SIZE);
n.setProperty(CoreOptions.NODE_SIZE_CONSTRAINTS, nodeSizeConstraints);
}
}
if (graphic instanceof ModeGraphic && ((ModeGraphic) graphic).isInitialMode) {
minHeight += ModeGraphic.INITIAL_MODE_AREA_HEIGHT;
}
// Special min size handling for elements shown as image
final Style style = de.getStyle();
if (style != null && Boolean.TRUE.equals(style.getShowAsImage())) {
final Dimension dim = ((DiagramElement) dn).getSize();
minHeight = dim.height;
minWidth = dim.width;
}
}
// Increase min width and min height for top level nodes.
if (dn != null && dn.getParent() instanceof AgeDiagram) {
minWidth = Math.max(minWidth, 200);
minHeight = Math.max(minHeight, 100);
}
n.setProperty(CoreOptions.NODE_SIZE_MINIMUM, new KVector(minWidth, minHeight));
}
};
ElkUtil.applyVisitors(layoutMapping.getLayoutGraph(), minNodeSizeVisitor);
// If the top level element has a size set, don't shrink it.
if (rootDiagramNode instanceof DiagramElement) {
final DiagramElement rootDiagramElement = (DiagramElement) rootDiagramNode;
final ElkGraphElement rootGraphElement = layoutMapping.getGraphMap().inverse().get(rootDiagramNode);
if (rootGraphElement != null && rootDiagramElement.hasSize() && DiagramElementPredicates.isResizeable(rootDiagramElement)) {
final KVector minSize = rootGraphElement.getProperty(CoreOptions.NODE_SIZE_MINIMUM);
final double newMinWidth = Math.max(rootDiagramElement.getWidth(), minSize == null ? 0.0 : minSize.x);
final double newMinHeight = Math.max(rootDiagramElement.getHeight(), minSize == null ? 0.0 : minSize.y);
rootGraphElement.setProperty(CoreOptions.NODE_SIZE_MINIMUM, new KVector(newMinWidth, newMinHeight));
}
}
}
use of org.osate.ge.internal.diagram.runtime.DiagramNode in project osate2 by osate.
the class DiagramElementLayoutUtil method moveElement.
/**
* Sets the position of a diagram element
* @param modification the diagram modification to use to modify the diagram
* @param e the element for which to set the position
* @param value the new position of the element
* @param updateDockArea whether the dock area should be updated based on the set position.
* @param updateBendpoints whether to shift bendpoints of related connections
* @param updateFlowIndicators if related flow indicators should be moved. If dock area has changed the position of the dock areas will be reset to allow for a new layout.
*/
public static void moveElement(final DiagramModification modification, final DiagramElement e, final Point value, final boolean updateDockArea, final boolean updateBendpoints, final boolean updateFlowIndicators) {
if (!Objects.equals(e.getPosition(), value)) {
// Determine the different between X and Y
final Point delta = value == null ? null : new Point(value.x - e.getX(), value.y - e.getY());
modification.setPosition(e, value);
// Only update dock area and bendpoints if position is being set to an actual value
if (delta != null) {
final DockArea originalDockArea = e.getDockArea();
if (updateDockArea) {
// Update the dock area based on the position
if (originalDockArea != null) {
if (originalDockArea != DockArea.GROUP) {
modification.setDockArea(e, calculateDockArea(e));
}
}
}
if (updateBendpoints || updateFlowIndicators) {
DiagramElementLayoutUtil.shiftRelatedConnections(Stream.of(e), new Point(delta.x, delta.y), modification, updateBendpoints, updateFlowIndicators, true);
}
// Reset flow indicators entirely if dock area has changed
if (updateFlowIndicators && originalDockArea != e.getDockArea()) {
final Stream<DiagramNode> affectedStartElements = e.getAllDiagramNodes();
DiagramElementLayoutUtil.resetFlowIndicatorsWithStartElementsPositions(modification, affectedStartElements);
}
}
}
}
Aggregations