use of org.eclipse.elk.core.math.ElkRectangle in project elk by eclipse.
the class PortLabelPlacementCalculator method constrainedOutsidePortLabelPlacement.
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Constrained Outside Port Labels
/**
* Place the port label cells outside of the node in the knowledge that there might not be enough space to place
* them without overlaps.
*/
private static void constrainedOutsidePortLabelPlacement(final NodeContext nodeContext, final PortSide portSide) {
Collection<PortContext> portContexts = nodeContext.portContexts.get(portSide);
// simply revert to simple port label placement
if (portContexts.size() <= 2 || portSide == PortSide.EAST || portSide == PortSide.WEST) {
simpleOutsidePortLabelPlacement(nodeContext, portSide);
return;
}
// If space-efficient port labels are active, the leftmost / topmost port's label must be placed to its left /
// above it
boolean portWithSpecialNeeds = nodeContext.portLabelsPlacement.contains(PortLabelPlacement.SPACE_EFFICIENT);
// Prepare things
OverlapRemovalDirection overlapRemovalDirection = portSide == PortSide.NORTH ? OverlapRemovalDirection.UP : OverlapRemovalDirection.DOWN;
VerticalLabelAlignment verticalLabelAlignment = portSide == PortSide.NORTH ? VerticalLabelAlignment.BOTTOM : VerticalLabelAlignment.TOP;
// Obtain a rectangle strip overlap remover, which will actually do most of the work
RectangleStripOverlapRemover overlapRemover = RectangleStripOverlapRemover.createForDirection(overlapRemovalDirection).withGap(nodeContext.portLabelSpacing);
// Iterate over our ports and add rectangles to the overlap remover. Also, calculate the start coordinate
double startCoordinate = portSide == PortSide.NORTH ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
for (PortContext portContext : portContexts) {
if (portContext.portLabelCell == null || !portContext.portLabelCell.hasLabels()) {
continue;
}
KVector portSize = portContext.port.getSize();
KVector portPosition = portContext.portPosition;
LabelCell portLabelCell = portContext.portLabelCell;
ElkRectangle portLabelCellRect = portLabelCell.getCellRectangle();
// Setup the label cell's cell rectangle
portLabelCellRect.width = portLabelCell.getMinimumWidth();
portLabelCellRect.height = portLabelCell.getMinimumHeight();
if (portWithSpecialNeeds) {
portLabelCellRect.x = portPosition.x - portLabelCell.getMinimumWidth() - nodeContext.portLabelSpacing;
portWithSpecialNeeds = false;
} else {
portLabelCellRect.x = portPosition.x + portSize.x + nodeContext.portLabelSpacing;
}
portLabelCell.setVerticalAlignment(verticalLabelAlignment);
portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.RIGHT);
// Add the rectangle to the overlap remover
overlapRemover.addRectangle(portLabelCellRect);
// Update start coordinate
startCoordinate = portSide == PortSide.NORTH ? Math.min(startCoordinate, portPosition.y) : Math.max(startCoordinate, portPosition.y + portContext.port.getSize().y);
}
// The start coordinate needs to be offset by the port-label space
startCoordinate += portSide == PortSide.NORTH ? -nodeContext.portLabelSpacing : nodeContext.portLabelSpacing;
// Invoke the overlap remover
overlapRemover.withStartCoordinate(startCoordinate).removeOverlaps();
// We need to update the label cell's coordinates to be relative to the ports
for (PortContext portContext : portContexts) {
if (portContext.portLabelCell == null || !portContext.portLabelCell.hasLabels()) {
continue;
}
ElkRectangle portLabelCellRect = portContext.portLabelCell.getCellRectangle();
// Setup the label cell's cell rectangle
portLabelCellRect.x -= portContext.portPosition.x;
portLabelCellRect.y -= portContext.portPosition.y;
}
}
use of org.eclipse.elk.core.math.ElkRectangle in project elk by eclipse.
the class PortPlacementCalculator method placeVerticalFreePorts.
/**
* Places ports on the eastern and western side for the unconstrained cases.
*/
private static void placeVerticalFreePorts(final NodeContext nodeContext, final PortSide portSide) {
// If there are no ports on the given side, abort
if (nodeContext.portContexts.get(portSide).isEmpty()) {
return;
}
// Retrieve the proper inside port label cell, which will give us hints as to where to place our ports
AtomicCell insidePortLabelCell = nodeContext.insidePortLabelCells.get(portSide);
ElkRectangle insidePortLabelCellRectangle = insidePortLabelCell.getCellRectangle();
ElkPadding insidePortLabelCellPadding = insidePortLabelCell.getPadding();
// Note that we don't have to distinguish any cases here because the port margins already include space
// required for labels, if such space is to be reserved. Yay!
PortAlignment portAlignment = nodeContext.getPortAlignment(portSide);
double availableSpace = insidePortLabelCellRectangle.height - insidePortLabelCellPadding.top - insidePortLabelCellPadding.bottom;
double calculatedPortPlacementHeight = insidePortLabelCell.getMinimumContentAreaSize().y;
double currentYPos = insidePortLabelCellRectangle.y + insidePortLabelCellPadding.top;
double spaceBetweenPorts = nodeContext.portPortSpacing;
double nodeWidth = nodeContext.nodeSize.x;
// to center to keep things from looking stupid
if ((portAlignment == PortAlignment.DISTRIBUTED || portAlignment == PortAlignment.JUSTIFIED) && nodeContext.portContexts.get(portSide).size() == 1) {
calculatedPortPlacementHeight = modifiedPortPlacementSize(nodeContext, portAlignment, calculatedPortPlacementHeight);
portAlignment = PortAlignment.CENTER;
}
if (availableSpace < calculatedPortPlacementHeight && !nodeContext.sizeOptions.contains(SizeOptions.PORTS_OVERHANG)) {
// the space between them to cram them into the available space.
if (portAlignment == PortAlignment.DISTRIBUTED) {
spaceBetweenPorts += (availableSpace - calculatedPortPlacementHeight) / (nodeContext.portContexts.get(portSide).size() + 1);
currentYPos += spaceBetweenPorts;
} else {
spaceBetweenPorts += (availableSpace - calculatedPortPlacementHeight) / (nodeContext.portContexts.get(portSide).size() - 1);
}
} else {
// case where we should fall back to centered alignment
if (availableSpace < calculatedPortPlacementHeight) {
calculatedPortPlacementHeight = modifiedPortPlacementSize(nodeContext, portAlignment, calculatedPortPlacementHeight);
portAlignment = PortAlignment.CENTER;
}
// otherwise expect)
switch(portAlignment) {
case BEGIN:
// There's nothing to do here
break;
case CENTER:
currentYPos += (availableSpace - calculatedPortPlacementHeight) / 2;
break;
case END:
currentYPos += availableSpace - calculatedPortPlacementHeight;
break;
case DISTRIBUTED:
// In this case, if there is not enough space available to place the ports, we are allowed to overhang.
// We thus need to ensure that we're only ever increasing the port spacing here
double additionalSpaceBetweenPorts = (availableSpace - calculatedPortPlacementHeight) / (nodeContext.portContexts.get(portSide).size() + 1);
spaceBetweenPorts += Math.max(0, additionalSpaceBetweenPorts);
currentYPos += spaceBetweenPorts;
break;
case JUSTIFIED:
// In this case, if there is not enough space available to place the ports, we are allowed to overhang.
// We thus need to ensure that we're only ever increasing the port spacing here
additionalSpaceBetweenPorts = (availableSpace - calculatedPortPlacementHeight) / (nodeContext.portContexts.get(portSide).size() - 1);
spaceBetweenPorts += Math.max(0, additionalSpaceBetweenPorts);
break;
}
}
// Iterate over all ports and place them
for (PortContext portContext : nodeContext.portContexts.get(portSide)) {
portContext.portPosition.x = calculateVerticalPortXCoordinate(portContext, nodeWidth);
portContext.portPosition.y = currentYPos + portContext.portMargin.top;
// Update the y coordinate for the next port
currentYPos += portContext.portMargin.top + portContext.port.getSize().y + portContext.portMargin.bottom + spaceBetweenPorts;
}
}
use of org.eclipse.elk.core.math.ElkRectangle in project elk by eclipse.
the class PortPlacementCalculator method placeHorizontalFreePorts.
/**
* Places ports on the northern and southern side for the unconstrained cases.
*/
private static void placeHorizontalFreePorts(final NodeContext nodeContext, final PortSide portSide) {
// If there are no ports on the given side, abort
if (nodeContext.portContexts.get(portSide).isEmpty()) {
return;
}
// Retrieve the proper inside port label cell, which will give us hints as to where to place our ports
AtomicCell insidePortLabelCell = nodeContext.insidePortLabelCells.get(portSide);
ElkRectangle insidePortLabelCellRectangle = insidePortLabelCell.getCellRectangle();
ElkPadding insidePortLabelCellPadding = insidePortLabelCell.getPadding();
// Note that we don't have to distinguish any cases here because the port margins already include space
// required for labels, if such space is to be reserved. Yay!
PortAlignment portAlignment = nodeContext.getPortAlignment(portSide);
double availableSpace = insidePortLabelCellRectangle.width - insidePortLabelCellPadding.left - insidePortLabelCellPadding.right;
double calculatedPortPlacementWidth = insidePortLabelCell.getMinimumContentAreaSize().x;
double currentXPos = insidePortLabelCellRectangle.x + insidePortLabelCellPadding.left;
double spaceBetweenPorts = nodeContext.portPortSpacing;
// to center to keep things from looking stupid
if ((portAlignment == PortAlignment.DISTRIBUTED || portAlignment == PortAlignment.JUSTIFIED) && nodeContext.portContexts.get(portSide).size() == 1) {
calculatedPortPlacementWidth = modifiedPortPlacementSize(nodeContext, portAlignment, calculatedPortPlacementWidth);
portAlignment = PortAlignment.CENTER;
}
if (availableSpace < calculatedPortPlacementWidth && !nodeContext.sizeOptions.contains(SizeOptions.PORTS_OVERHANG)) {
// the space between them to cram them into the available space.
if (portAlignment == PortAlignment.DISTRIBUTED) {
spaceBetweenPorts += (availableSpace - calculatedPortPlacementWidth) / (nodeContext.portContexts.get(portSide).size() + 1);
currentXPos += spaceBetweenPorts;
} else {
spaceBetweenPorts += (availableSpace - calculatedPortPlacementWidth) / (nodeContext.portContexts.get(portSide).size() - 1);
}
} else {
// case where we should fall back to centered alignment
if (availableSpace < calculatedPortPlacementWidth) {
calculatedPortPlacementWidth = modifiedPortPlacementSize(nodeContext, portAlignment, calculatedPortPlacementWidth);
portAlignment = PortAlignment.CENTER;
}
// otherwise expect)
switch(portAlignment) {
case BEGIN:
// There's nothing to do here
break;
case CENTER:
currentXPos += (availableSpace - calculatedPortPlacementWidth) / 2;
break;
case END:
currentXPos += availableSpace - calculatedPortPlacementWidth;
break;
case DISTRIBUTED:
// In this case, if there is not enough space available to place the ports, we are allowed to overhang.
// We thus need to ensure that we're only ever increasing the port spacing here
double additionalSpaceBetweenPorts = (availableSpace - calculatedPortPlacementWidth) / (nodeContext.portContexts.get(portSide).size() + 1);
spaceBetweenPorts += Math.max(0, additionalSpaceBetweenPorts);
currentXPos += spaceBetweenPorts;
break;
case JUSTIFIED:
// In this case, if there is not enough space available to place the ports, we are allowed to overhang.
// We thus need to ensure that we're only ever increasing the port spacing here
additionalSpaceBetweenPorts = (availableSpace - calculatedPortPlacementWidth) / (nodeContext.portContexts.get(portSide).size() - 1);
spaceBetweenPorts += Math.max(0, additionalSpaceBetweenPorts);
break;
}
}
// Iterate over all ports and place them
for (PortContext portContext : nodeContext.portContexts.get(portSide)) {
portContext.portPosition.x = currentXPos + portContext.portMargin.left;
portContext.portPosition.y = calculateHorizontalPortYCoordinate(portContext);
// Update the x coordinate for the next port
currentXPos += portContext.portMargin.left + portContext.port.getSize().x + portContext.portMargin.right + spaceBetweenPorts;
}
}
use of org.eclipse.elk.core.math.ElkRectangle in project elk by eclipse.
the class VerticalPortPlacementSizeCalculator method computeVerticalPortMargins.
/**
* Sets the vertical margins of all ports such that they include the space necessary to place their labels.
*/
private static void computeVerticalPortMargins(final NodeContext nodeContext, final PortSide portSide, final boolean portLabelsOutside) {
for (PortContext portContext : nodeContext.portContexts.get(portSide)) {
double labelHeight = portContext.portLabelCell != null ? portContext.portLabelCell.getMinimumHeight() : 0;
if (labelHeight > 0) {
if (portContext.labelsNextToPort) {
// The label is placed next to the port
double portHeight = portContext.port.getSize().y;
if (labelHeight > portHeight) {
if (nodeContext.portLabelsTreatAsGroup || portContext.portLabelCell.getLabels().size() == 1) {
// We are to center all of the labels
double overhang = (labelHeight - portHeight) / 2;
portContext.portMargin.top = overhang;
portContext.portMargin.bottom = overhang;
} else {
// Simulate centering the first port label
double firstLabelHeight = portContext.portLabelCell.getLabels().get(0).getSize().y;
double firstLabelOverhang = (firstLabelHeight - portHeight) / 2;
portContext.portMargin.top = Math.max(0, firstLabelOverhang);
portContext.portMargin.bottom = labelHeight - firstLabelOverhang - portHeight;
}
}
} else {
// The label is either placed outside (below the port) or possibly inside, but for a compound node,
// which means that it is placed below the port as well to keep it from overlapping with inside
// edges
portContext.portMargin.bottom = nodeContext.portLabelSpacing + labelHeight;
}
} else if (PortLabelPlacement.isFixed(nodeContext.portLabelsPlacement)) {
// The fixed port label is not considered with portContext.portLabelCell. Nevertheless, a port margin
// must be added if necessary.
ElkRectangle labelsBounds = ElkUtil.getLabelsBounds(portContext.port);
if (labelsBounds.y < 0) {
// Add the part of the label that is above the port to the top margin
portContext.portMargin.top = -labelsBounds.y;
}
if (labelsBounds.y + labelsBounds.height > portContext.port.getSize().y) {
// Add the part of the label that is below the port to the bottom margin
portContext.portMargin.bottom = labelsBounds.y + labelsBounds.height - portContext.port.getSize().y;
}
}
}
}
use of org.eclipse.elk.core.math.ElkRectangle in project elk by eclipse.
the class RectangleStripOverlapRemover method exportRectangle.
/**
* Applies the movement computed for the given rectangle node's rectangle to its original rectangle, paying
* attention to the overlap removal direction.
*
* @param rectangleNode
* the rectangle node whose results to apply to the original rectangle.
* @param stripSize
* strip size computed by the overlap removal algorithm.
*/
private void exportRectangle(final RectangleNode rectangleNode, final double stripSize) {
ElkRectangle rectangle = rectangleNode.rectangle;
ElkRectangle originalRectangle = rectangleNode.originalRectangle;
switch(overlapRemovalDirection) {
case UP:
originalRectangle.y = startCoordinate - rectangle.height - rectangle.y;
break;
case DOWN:
originalRectangle.y += startCoordinate;
break;
case LEFT:
originalRectangle.x = startCoordinate - rectangle.height - rectangle.y;
break;
case RIGHT:
originalRectangle.x = startCoordinate + rectangle.y;
break;
}
}
Aggregations