use of org.osate.ge.internal.diagram.runtime.DockArea 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.DockArea 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.DockArea in project osate2 by osate.
the class DiagramElementLayoutUtil method layoutFeatureSelfLoopConnection.
/**
* Sets the bendpoints for an element. The element should be a feature self loop connection element.
* @param connectionElement the element to update.
* @param mod the modification to use to set the bendpoints
* @param layoutInfoProvider the layout information provider to help calculate positions
*/
private static void layoutFeatureSelfLoopConnection(final DiagramElement connectionElement, final DiagramModification mod, final LayoutInfoProvider layoutInfoProvider) {
final DockArea dockArea = getNonGroupDockArea(connectionElement.getStartElement());
final Point featurePosition = getPortAnchorOffset(connectionElement.getStartElement(), dockArea, new Point(0, 0), layoutInfoProvider);
// Calculate new bendpoints
final List<Point> newBendpoints;
// Offset towards the inside of the port in the dock area dependent direction. The horizontal direction for Left/Right
final double majorOffset = 50;
// Offset used in both directions in the other axis.
final double minorOffset = 8;
if (dockArea == DockArea.LEFT) {
newBendpoints = ImmutableList.of(new Point(featurePosition.x + majorOffset, featurePosition.y - minorOffset), new Point(featurePosition.x + majorOffset, featurePosition.y + minorOffset));
} else if (dockArea == DockArea.RIGHT) {
newBendpoints = ImmutableList.of(new Point(featurePosition.x - majorOffset, featurePosition.y - minorOffset), new Point(featurePosition.x - majorOffset, featurePosition.y + minorOffset));
} else if (dockArea == DockArea.TOP) {
newBendpoints = ImmutableList.of(new Point(featurePosition.x - minorOffset, featurePosition.y + majorOffset), new Point(featurePosition.x + minorOffset, featurePosition.y + majorOffset));
} else {
// BOTTOM
newBendpoints = ImmutableList.of(new Point(featurePosition.x - minorOffset, featurePosition.y - majorOffset), new Point(featurePosition.x + minorOffset, featurePosition.y - majorOffset));
}
// Update the bendpoints
mod.setBendpoints(connectionElement, newBendpoints);
}
use of org.osate.ge.internal.diagram.runtime.DockArea in project osate2 by osate.
the class DiagramElementLayoutUtil method applyShapeLayout.
private static void applyShapeLayout(final LayoutMapping mapping, final DiagramModification m) {
// Modify shapes
for (Entry<ElkGraphElement, Object> e : mapping.getGraphMap().entrySet()) {
final ElkGraphElement elkElement = e.getKey();
final Object mappedValue = e.getValue();
final boolean isTopLevelElement = isTopLevel(elkElement);
if (!(elkElement instanceof ElkShape)) {
continue;
}
final ElkShape elkShape = (ElkShape) elkElement;
if (!(mappedValue instanceof DiagramElement)) {
continue;
}
final DiagramElement de = (DiagramElement) mappedValue;
if (!(de.getGraphic() instanceof AgeShape)) {
continue;
}
if (de.getGraphic() instanceof Label) {
continue;
}
// Set Position. Don't set the position of top level elements
if (!isTopLevelElement && DiagramElementPredicates.isMoveableShape(de)) {
// Determine position for the element
double x = elkShape.getX();
double y = elkShape.getY();
// If the diagram element has a parent port, subtract the parent port position from the ELK port position to determine the relative position
if (de.getDockArea() == DockArea.GROUP) {
final ElkPort parentPort = (ElkPort) mapping.getGraphMap().inverse().get(de.getParent());
if (parentPort != null) {
final PortSide side = parentPort.getProperty(CoreOptions.PORT_SIDE);
if (PortSide.SIDES_NORTH_SOUTH.contains(side)) {
x = elkShape.getX() - parentPort.getX();
} else if (PortSide.SIDES_EAST_WEST.contains(side)) {
y = elkShape.getY() - parentPort.getY();
} else {
throw new GraphicalEditorException("Unexpected side: " + side);
}
}
}
DiagramElementLayoutUtil.moveElement(m, de, new Point(x, y));
// Set the dock area
if (de.getDockArea() != DockArea.GROUP && de.getDockArea() != null) {
final DockArea newDockArea = PortSideUtil.getDockArea(elkShape.getProperty(CoreOptions.PORT_SIDE));
if (newDockArea != null) {
m.setDockArea(de, newDockArea);
}
}
}
// Set the size
if (DiagramElementPredicates.isResizeable(de)) {
m.setSize(de, new Dimension(elkShape.getWidth(), elkShape.getHeight()));
}
}
}
use of org.osate.ge.internal.diagram.runtime.DockArea 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