use of org.eclipse.elk.alg.common.nodespacing.cellsystem.LabelCell 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.alg.common.nodespacing.cellsystem.LabelCell in project elk by eclipse.
the class PortLabelPlacementCalculator method simpleOutsidePortLabelPlacement.
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Simple Outside Port Labels
/**
* Place the port label cells outside of the node.
*/
private static void simpleOutsidePortLabelPlacement(final NodeContext nodeContext, final PortSide portSide) {
Collection<PortContext> portContexts = nodeContext.portContexts.get(portSide);
// If there are only two ports on a side, we place the first port's label on its other side to make it
// especially clear which port it belongs to. The same applies if the user requested space-efficient mode
boolean placeFirstPortDifferently = NodeLabelAndSizeUtilities.isFirstOutsidePortLabelPlacedDifferently(nodeContext, portSide);
for (PortContext portContext : portContexts) {
// If the port doesn't have labels, skip
if (portContext.portLabelCell == null || !portContext.portLabelCell.hasLabels()) {
continue;
}
// Retrieve information about the port itself
KVector portSize = portContext.port.getSize();
// Retrieve the label cell and its rectangle and set the rectangle's size (we will use the rectangle to
// place the cell relative to the port below)
LabelCell portLabelCell = portContext.portLabelCell;
ElkRectangle portLabelCellRect = portLabelCell.getCellRectangle();
portLabelCellRect.width = portLabelCell.getMinimumWidth();
portLabelCellRect.height = portLabelCell.getMinimumHeight();
// Calculate the position of the port's label space
switch(portSide) {
case NORTH:
if (portContext.labelsNextToPort) {
portLabelCellRect.x = (portSize.x - portLabelCellRect.width) / 2;
portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.CENTER);
} else if (placeFirstPortDifferently) {
portLabelCellRect.x = -portLabelCellRect.width - nodeContext.portLabelSpacing;
portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.RIGHT);
} else {
portLabelCellRect.x = portSize.x + nodeContext.portLabelSpacing;
portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.LEFT);
}
portLabelCellRect.y = -portLabelCellRect.height - nodeContext.portLabelSpacing;
portLabelCell.setVerticalAlignment(VerticalLabelAlignment.BOTTOM);
break;
case SOUTH:
if (portContext.labelsNextToPort) {
portLabelCellRect.x = (portSize.x - portLabelCellRect.width) / 2;
portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.CENTER);
} else if (placeFirstPortDifferently) {
portLabelCellRect.x = -portLabelCellRect.width - nodeContext.portLabelSpacing;
portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.RIGHT);
} else {
portLabelCellRect.x = portSize.x + nodeContext.portLabelSpacing;
portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.LEFT);
}
portLabelCellRect.y = portSize.y + nodeContext.portLabelSpacing;
portLabelCell.setVerticalAlignment(VerticalLabelAlignment.TOP);
break;
case EAST:
if (portContext.labelsNextToPort) {
double labelHeight = nodeContext.portLabelsTreatAsGroup ? portLabelCellRect.height : portLabelCell.getLabels().get(0).getSize().y;
portLabelCellRect.y = (portSize.y - labelHeight) / 2;
portLabelCell.setVerticalAlignment(VerticalLabelAlignment.CENTER);
} else if (placeFirstPortDifferently) {
portLabelCellRect.y = -portLabelCellRect.height - nodeContext.portLabelSpacing;
portLabelCell.setVerticalAlignment(VerticalLabelAlignment.BOTTOM);
} else {
portLabelCellRect.y = portSize.y + nodeContext.portLabelSpacing;
portLabelCell.setVerticalAlignment(VerticalLabelAlignment.TOP);
}
portLabelCellRect.x = portSize.x + nodeContext.portLabelSpacing;
portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.LEFT);
break;
case WEST:
if (portContext.labelsNextToPort) {
double labelHeight = nodeContext.portLabelsTreatAsGroup ? portLabelCellRect.height : portLabelCell.getLabels().get(0).getSize().y;
portLabelCellRect.y = (portSize.y - labelHeight) / 2;
portLabelCell.setVerticalAlignment(VerticalLabelAlignment.CENTER);
} else if (placeFirstPortDifferently) {
portLabelCellRect.y = -portLabelCellRect.height - nodeContext.portLabelSpacing;
portLabelCell.setVerticalAlignment(VerticalLabelAlignment.BOTTOM);
} else {
portLabelCellRect.y = portSize.y + nodeContext.portLabelSpacing;
portLabelCell.setVerticalAlignment(VerticalLabelAlignment.TOP);
}
portLabelCellRect.x = -portLabelCellRect.width - nodeContext.portLabelSpacing;
portLabelCell.setHorizontalAlignment(HorizontalLabelAlignment.RIGHT);
break;
}
// The next port definitely doesn't have special needs anymore
placeFirstPortDifferently = false;
}
}
use of org.eclipse.elk.alg.common.nodespacing.cellsystem.LabelCell in project elk by eclipse.
the class CellSystemConfigurator method configureCellSystemSizeContributions.
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Size Contribution Configuration
/**
* Configures the cell system's constraints such that they work properly when calculating the required node space.
*/
public static void configureCellSystemSizeContributions(final NodeContext nodeContext) {
// calculate the node's size
if (nodeContext.sizeConstraints.isEmpty()) {
return;
}
// Go through the different size constraint components
if (nodeContext.sizeConstraints.contains(SizeConstraint.PORTS)) {
// The northern and southern inside port label cells have the correct width for the node
nodeContext.insidePortLabelCells.get(PortSide.NORTH).setContributesToMinimumWidth(true);
nodeContext.insidePortLabelCells.get(PortSide.SOUTH).setContributesToMinimumWidth(true);
// For the eastern and western cells, they only give a correct height if port placement is free instead of
// constrained (the constrained case is handled separately in the node size calculator). This is due to the
// fact that in the cell layout, the north and south cells are above and below the east and west cells,
// which would cause the node to become too high
boolean freePortPlacement = nodeContext.portConstraints != PortConstraints.FIXED_RATIO && nodeContext.portConstraints != PortConstraints.FIXED_POS;
nodeContext.insidePortLabelCells.get(PortSide.EAST).setContributesToMinimumHeight(freePortPlacement);
nodeContext.insidePortLabelCells.get(PortSide.WEST).setContributesToMinimumHeight(freePortPlacement);
// The main row needs to contribute height for the east and west port label cells to be able to contribute
// their height
nodeContext.nodeContainerMiddleRow.setContributesToMinimumHeight(freePortPlacement);
// Port labels only contribute their size if ports are accounted for as well
if (nodeContext.sizeConstraints.contains(SizeConstraint.PORT_LABELS)) {
// The port label cells contribute the space they need for inside port label placement
nodeContext.insidePortLabelCells.get(PortSide.NORTH).setContributesToMinimumHeight(true);
nodeContext.insidePortLabelCells.get(PortSide.SOUTH).setContributesToMinimumHeight(true);
nodeContext.insidePortLabelCells.get(PortSide.EAST).setContributesToMinimumWidth(true);
nodeContext.insidePortLabelCells.get(PortSide.WEST).setContributesToMinimumWidth(true);
// The main row needs to contribute Width for the east and west port label cells to be able to
// contribute their width
nodeContext.nodeContainerMiddleRow.setContributesToMinimumWidth(true);
}
}
if (nodeContext.sizeConstraints.contains(SizeConstraint.NODE_LABELS)) {
// The inside node label cell needs to contribute both width and height, as needs the middle row
nodeContext.insideNodeLabelContainer.setContributesToMinimumHeight(true);
nodeContext.insideNodeLabelContainer.setContributesToMinimumWidth(true);
nodeContext.nodeContainerMiddleRow.setContributesToMinimumHeight(true);
nodeContext.nodeContainerMiddleRow.setContributesToMinimumWidth(true);
// All node label cells need to contribute height and width, but outside node labels only do so unless they
// are configured to overhang
boolean overhang = nodeContext.sizeOptions.contains(SizeOptions.OUTSIDE_NODE_LABELS_OVERHANG);
for (NodeLabelLocation location : NodeLabelLocation.values()) {
LabelCell labelCell = nodeContext.nodeLabelCells.get(location);
if (labelCell != null) {
if (location.isInsideLocation()) {
labelCell.setContributesToMinimumHeight(true);
labelCell.setContributesToMinimumWidth(true);
} else {
labelCell.setContributesToMinimumHeight(!overhang);
labelCell.setContributesToMinimumWidth(!overhang);
}
}
}
}
// If the middle cell contributes to the node size, we need to set that up as well
if (nodeContext.sizeConstraints.contains(SizeConstraint.MINIMUM_SIZE) && nodeContext.sizeOptions.contains(SizeOptions.MINIMUM_SIZE_ACCOUNTS_FOR_PADDING)) {
// The middle row now needs to contribute width and height, and the center cell of the inside node label
// container needs to contribute width and height as well
nodeContext.nodeContainerMiddleRow.setContributesToMinimumHeight(true);
nodeContext.nodeContainerMiddleRow.setContributesToMinimumHeight(true);
// label container's center cell
if (!nodeContext.insideNodeLabelContainer.isContributingToMinimumHeight()) {
nodeContext.insideNodeLabelContainer.setContributesToMinimumHeight(true);
nodeContext.insideNodeLabelContainer.setContributesToMinimumWidth(true);
nodeContext.insideNodeLabelContainer.setOnlyCenterCellContributesToMinimumSize(true);
}
}
}
use of org.eclipse.elk.alg.common.nodespacing.cellsystem.LabelCell in project elk by eclipse.
the class EndLabelSorterTest method testNode.
private void testNode(final LNode node) {
switch(node.getType()) {
case NORMAL:
// Build a list of label strings that we can check
if (node.hasProperty(InternalProperties.END_LABELS)) {
for (LabelCell labelCell : node.getProperty(InternalProperties.END_LABELS).values()) {
List<String> labelStrings = labelCell.getLabels().stream().map(label -> label.getText()).collect(Collectors.toList());
failIfUnsorted(labelStrings);
}
}
break;
case LABEL:
List<LLabel> labels = node.getProperty(InternalProperties.REPRESENTED_LABELS);
assertNotNull(labels);
List<String> labelStrings = labels.stream().map(label -> label.getText()).collect(Collectors.toList());
failIfUnsorted(labelStrings);
break;
}
}
use of org.eclipse.elk.alg.common.nodespacing.cellsystem.LabelCell in project elk by eclipse.
the class EndLabelPreprocessor method updateNodeMargins.
// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node Margins
/**
* Updates the node's margins to account for its end labels.
*/
private void updateNodeMargins(final LNode node, final LabelCell[] labelCells) {
LMargin nodeMargin = node.getMargin();
KVector nodeSize = node.getSize();
// Calculate the rectangle that describes the node's current margin
ElkRectangle nodeMarginRectangle = new ElkRectangle(-nodeMargin.left, -nodeMargin.top, nodeMargin.left + nodeSize.x + nodeMargin.right, nodeMargin.top + nodeSize.y + nodeMargin.bottom);
// Union the rectangle with each rectangle that describes a label cell
for (LabelCell labelCell : labelCells) {
if (labelCell != null) {
nodeMarginRectangle.union(labelCell.getCellRectangle());
}
}
// Reapply the new rectangle to the margin
nodeMargin.left = -nodeMarginRectangle.x;
nodeMargin.top = -nodeMarginRectangle.y;
nodeMargin.right = nodeMarginRectangle.width - nodeMargin.left - nodeSize.x;
nodeMargin.bottom = nodeMarginRectangle.height - nodeMargin.top - nodeSize.y;
}
Aggregations