use of org.osate.ge.graphics.Dimension 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.graphics.Dimension in project osate2 by osate.
the class DiagramElementLayoutUtil method getPortAnchorOffset.
/**
* Gets the position of a port anchor relative to another point
* @param element the docked element for which to get the anchor offset
* @param nonGroupDockArea dock are of the group. Must not be group. For grouped docked elements,
* it should be the first non group dock area in the hierarchy.
* @param referencePosition is the absolute position to use as the reference point for the returned position.
* @param layoutInfoProvider the layout info provider to use to calculate sized.
* @return the position of the anchor relative to the specified point.
*/
private static Point getPortAnchorOffset(final DiagramElement element, final DockArea nonGroupDockArea, final Point referencePosition, final LayoutInfoProvider layoutInfoProvider) {
// Find offset based on orientation and nature of the diagram element
final Dimension labelsSize = layoutInfoProvider.getDockedElementLabelsSize(element);
final double anchorOffset;
if (DiagramElementPredicates.isResizeable(element) && element.hasSize()) {
// Feature groups
anchorOffset = nonGroupDockArea.isLeftOrRight() ? element.getHeight() - labelsSize.height : element.getWidth() - labelsSize.width;
} else {
anchorOffset = layoutInfoProvider.getPortGraphicSize(element).height;
}
// Find the position of the element
final Point elementAbsPosition = getAbsolutePosition(element);
final Point anchorPosition;
if (nonGroupDockArea == DockArea.LEFT) {
anchorPosition = new Point(elementAbsPosition.x - referencePosition.x, elementAbsPosition.y - referencePosition.y + labelsSize.height + anchorOffset / 2.0);
} else if (nonGroupDockArea == DockArea.RIGHT) {
anchorPosition = new Point(elementAbsPosition.x - referencePosition.x + element.getWidth(), elementAbsPosition.y - referencePosition.y + labelsSize.height + anchorOffset / 2.0);
} else if (nonGroupDockArea == DockArea.TOP) {
anchorPosition = new Point(elementAbsPosition.x - referencePosition.x + labelsSize.width + anchorOffset / 2.0, elementAbsPosition.y - referencePosition.y);
} else {
// BOTTOM
anchorPosition = new Point(elementAbsPosition.x - referencePosition.x + labelsSize.width + anchorOffset / 2.0, elementAbsPosition.y - referencePosition.y + element.getHeight());
}
return anchorPosition;
}
use of org.osate.ge.graphics.Dimension 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.graphics.Dimension in project osate2 by osate.
the class ElkGraphBuilder method createPort.
/**
* @param parent
* @param side
* @param dockedElement
* @param portBorderOffset additional offset from the border. Used to set the ELK port border offset for nested ports.
* @param mapping
* @return
*/
private ElkPort createPort(final ElkNode parent, final PortSide side, final DiagramElement dockedElement, final double portBorderOffset, final LayoutMapping mapping) {
final List<DiagramElement> dockedChildren = getDockedChildren(dockedElement);
final Dimension untransformedGraphicSize = layoutInfoProvider.getPortGraphicSize(dockedElement);
final Dimension transformedGraphicSize = transformDimension(untransformedGraphicSize, side);
final Dimension untransformedLabelsSize = layoutInfoProvider.getDockedElementLabelsSize(dockedElement);
final Dimension transformedLabelsSize = transformDimension(untransformedLabelsSize, side);
// Create child ports and sort them by the size of the dimension parallel to the docked side
final List<ElkPort> childPorts = dockedChildren.stream().map(ce -> createPort(parent, side, ce, getOrthogonalSize(transformedGraphicSize, side), mapping)).sorted((p1, p2) -> Double.compare(getSize(p1, side), getSize(p1, side))).collect(Collectors.toCollection(ArrayList::new));
// If the port has child ports, then we assume it is a feature group.
// The child ports are split into two bins and positioned such that they do not overlap the center of the feature group.
// The parent port will be sized accordingly.
final double maxChildBinSize;
if (childPorts.size() > 0) {
// Split the ports into two lists which have roughly equal heights/widths(depending on the docked side)
double[] binSize = { 0.0, 0.0 };
@SuppressWarnings("unchecked") final List<ElkPort>[] binLists = new ArrayList[] { new ArrayList<>(), new ArrayList<>() };
for (final ElkPort childPort : childPorts) {
final double size = getSize(childPort, side);
final int binIndex = binSize[0] <= binSize[1] ? 0 : 1;
binLists[binIndex].add(childPort);
binSize[binIndex] += size + paddingSize;
}
// Determine the total size of the feature
maxChildBinSize = Math.max(binSize[0], binSize[1]);
// Set the position of the port relative to its parent because the size and position of the parent will be selected after its children are sized.
for (int i = 0; i < 2; i++) {
double childPosition = transformedLabelsSize.height + i * (maxChildBinSize + transformedGraphicSize.height);
for (final ElkPort childPort : binLists[i]) {
setPositionAlongSide(childPort, side, childPosition);
childPosition += getSize(childPort, side) + paddingSize;
}
}
} else {
maxChildBinSize = 0.0;
}
// Size the parent port based on the bin size.
final double newGraphicSize = 2.0 * maxChildBinSize + transformedGraphicSize.height;
final double totalSize = transformedLabelsSize.height + newGraphicSize;
final ElkPort newPort = ElkGraphUtil.createPort(parent);
mapping.getGraphMap().put(newPort, dockedElement);
newPort.setProperty(CoreOptions.PORT_SIDE, side);
// Determine max orthogonal size of children
double maxChildOrthogonalSize = 0;
for (final ElkPort childPort : childPorts) {
maxChildOrthogonalSize = Math.max(maxChildOrthogonalSize, getOrthogonalSize(childPort, side));
}
// Determine size and position
final Dimension newSize;
if (DiagramElementPredicates.isResizeable(dockedElement)) {
newSize = new Dimension(Math.max(untransformedLabelsSize.width, maxChildOrthogonalSize + getOrthogonalSize(transformedGraphicSize, side)), totalSize);
} else {
newSize = new Dimension(Math.max(untransformedLabelsSize.width, untransformedGraphicSize.width), transformedLabelsSize.height + untransformedGraphicSize.height);
}
// final graph.
if (omitNestedPorts) {
for (final ElkPort childPort : childPorts) {
mapping.getGraphMap().remove(childPort);
EcoreUtil.remove(childPort);
}
}
final Dimension transformedNewSize = transformDimension(newSize, side);
newPort.setWidth(transformedNewSize.width);
newPort.setHeight(transformedNewSize.height);
// Set port border offset
if (PortSide.SIDES_NORTH_SOUTH.contains(side)) {
newPort.setProperty(CoreOptions.PORT_BORDER_OFFSET, -newPort.getHeight() - portBorderOffset);
} else {
newPort.setProperty(CoreOptions.PORT_BORDER_OFFSET, -newPort.getWidth() - portBorderOffset);
}
// Set the port anchor based on where the actual graphic will be.
final Dimension transformedPortAnchor = transformDimension(new Dimension(untransformedGraphicSize.width / 2, transformedLabelsSize.height + maxChildBinSize + untransformedGraphicSize.height / 2.0), side);
newPort.setProperty(CoreOptions.PORT_ANCHOR, new KVector(transformedPortAnchor.width, transformedPortAnchor.height));
return newPort;
}
use of org.osate.ge.graphics.Dimension in project osate2 by osate.
the class ElkGraphBuilder method createElkGraphElementsForConnections.
/**
* Creates ELK edges for connection diagram nodes which are descendants of the specified node.
* Even though the results of the ELK edge routing are not used, it is still important because it affects the placements of shapes.
*/
private void createElkGraphElementsForConnections(final DiagramNode dn, final LayoutMapping mapping) {
for (final DiagramElement de : dn.getChildren()) {
if (de.getGraphic() instanceof AgeConnection) {
final AgeConnection connection = (AgeConnection) de.getGraphic();
// Flow indicators are represented by a node in the container which has a port and an edge connecting that port to the starting element
final ElkConnectableShape edgeStart = getConnectableShape(de.getStartElement(), mapping);
ElkConnectableShape edgeEnd = null;
if (connection.isFlowIndicator) {
// Find the first undocked ancestor for the flow indicator
final DiagramElement undockedContainer = DiagramElementUtil.getUndockedDiagramElement(de.getParent());
if (undockedContainer == null) {
// Ignore the flow indicator if unable to find a containing element which isn't docked.
continue;
}
// Find the ELK shape for the ancestor
final ElkConnectableShape endContainer = getConnectableShape(undockedContainer, mapping);
if (!(endContainer instanceof ElkNode)) {
// Ignore the flow indicator if the container isn't a node.
continue;
}
// Create the node for the end of the flow indicator
final ElkNode endNode = ElkGraphUtil.createNode((ElkNode) endContainer);
endContainer.setDimensions(0, 0);
endNode.setProperty(CoreOptions.NODE_SIZE_CONSTRAINTS, EnumSet.noneOf(SizeConstraint.class));
endNode.setProperty(CoreOptions.NODE_SIZE_OPTIONS, EnumSet.noneOf(SizeOptions.class));
// Create port
final ElkPort endPort = ElkGraphUtil.createPort(endNode);
endPort.setProperty(CoreOptions.PORT_SIDE, PortSide.WEST);
endPort.setX(0);
endPort.setY(0);
endPort.setWidth(0);
endPort.setHeight(0);
edgeEnd = endPort;
} else {
edgeEnd = getConnectableShape(de.getEndElement(), mapping);
}
if (edgeStart != null && edgeEnd != null) {
final ElkConnectableShape start = edgeStart;
final ElkConnectableShape end = edgeEnd;
boolean insideSelfLoopsYo = true;
// An example of this sort of edge is a steady state state transition in the EMV2
if (start == end) {
insideSelfLoopsYo = false;
}
final ElkEdge newEdge = ElkGraphUtil.createSimpleEdge(start, end);
// Allow edges with the same start and end shape because they layout as intended.
if (start == end && start instanceof ElkPort) {
continue;
}
// Ensure the edge has at least one section. Fixes NPE that can occur when laying out connections
// with the same source and destination port.
ElkGraphUtil.createEdgeSection(newEdge);
newEdge.setProperty(CoreOptions.INSIDE_SELF_LOOPS_YO, insideSelfLoopsYo);
mapping.getGraphMap().put(newEdge, de);
createElkLabels(de, newEdge, mapping);
// along with other edges
if (connection.isFlowIndicator && newEdge.getLabels().isEmpty()) {
final ElkLabel spacingLabel = createElkLabel(newEdge, "<Spacing>", new Dimension(10, 10));
if (!layoutConnectionLabels) {
spacingLabel.setProperty(CoreOptions.NO_LAYOUT, true);
}
}
}
}
createElkGraphElementsForConnections(de, mapping);
}
}
Aggregations