Search in sources :

Example 1 with LabelCell

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;
    }
}
Also used : RectangleStripOverlapRemover(org.eclipse.elk.alg.common.overlaps.RectangleStripOverlapRemover) LabelCell(org.eclipse.elk.alg.common.nodespacing.cellsystem.LabelCell) VerticalLabelAlignment(org.eclipse.elk.alg.common.nodespacing.cellsystem.VerticalLabelAlignment) OverlapRemovalDirection(org.eclipse.elk.alg.common.overlaps.RectangleStripOverlapRemover.OverlapRemovalDirection) KVector(org.eclipse.elk.core.math.KVector) ElkRectangle(org.eclipse.elk.core.math.ElkRectangle) PortContext(org.eclipse.elk.alg.common.nodespacing.internal.PortContext)

Example 2 with LabelCell

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;
    }
}
Also used : LabelCell(org.eclipse.elk.alg.common.nodespacing.cellsystem.LabelCell) KVector(org.eclipse.elk.core.math.KVector) ElkRectangle(org.eclipse.elk.core.math.ElkRectangle) PortContext(org.eclipse.elk.alg.common.nodespacing.internal.PortContext)

Example 3 with LabelCell

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);
        }
    }
}
Also used : LabelCell(org.eclipse.elk.alg.common.nodespacing.cellsystem.LabelCell) NodeLabelLocation(org.eclipse.elk.alg.common.nodespacing.internal.NodeLabelLocation)

Example 4 with LabelCell

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;
    }
}
Also used : LayoutTestRunner(org.eclipse.elk.alg.test.framework.LayoutTestRunner) ModelResourcePath(org.eclipse.elk.alg.test.framework.io.ModelResourcePath) Iterator(java.util.Iterator) LLabel(org.eclipse.elk.alg.layered.graph.LLabel) Algorithm(org.eclipse.elk.alg.test.framework.annotations.Algorithm) Assert.assertNotNull(org.junit.Assert.assertNotNull) RunWith(org.junit.runner.RunWith) Assert.assertTrue(org.junit.Assert.assertTrue) DefaultConfiguration(org.eclipse.elk.alg.test.framework.annotations.DefaultConfiguration) Collectors(java.util.stream.Collectors) LayeredOptions(org.eclipse.elk.alg.layered.options.LayeredOptions) LabelCell(org.eclipse.elk.alg.common.nodespacing.cellsystem.LabelCell) List(java.util.List) AbstractResourcePath(org.eclipse.elk.alg.test.framework.io.AbstractResourcePath) Lists(com.google.common.collect.Lists) InternalProperties(org.eclipse.elk.alg.layered.options.InternalProperties) TestAfterProcessor(org.eclipse.elk.alg.test.framework.annotations.TestAfterProcessor) LGraph(org.eclipse.elk.alg.layered.graph.LGraph) LNode(org.eclipse.elk.alg.layered.graph.LNode) GraphResourceProvider(org.eclipse.elk.alg.test.framework.annotations.GraphResourceProvider) LLabel(org.eclipse.elk.alg.layered.graph.LLabel) LabelCell(org.eclipse.elk.alg.common.nodespacing.cellsystem.LabelCell)

Example 5 with LabelCell

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;
}
Also used : LMargin(org.eclipse.elk.alg.layered.graph.LMargin) LabelCell(org.eclipse.elk.alg.common.nodespacing.cellsystem.LabelCell) KVector(org.eclipse.elk.core.math.KVector) ElkRectangle(org.eclipse.elk.core.math.ElkRectangle)

Aggregations

LabelCell (org.eclipse.elk.alg.common.nodespacing.cellsystem.LabelCell)13 ElkRectangle (org.eclipse.elk.core.math.ElkRectangle)7 KVector (org.eclipse.elk.core.math.KVector)6 PortContext (org.eclipse.elk.alg.common.nodespacing.internal.PortContext)5 LPort (org.eclipse.elk.alg.layered.graph.LPort)3 VerticalLabelAlignment (org.eclipse.elk.alg.common.nodespacing.cellsystem.VerticalLabelAlignment)2 RectangleStripOverlapRemover (org.eclipse.elk.alg.common.overlaps.RectangleStripOverlapRemover)2 OverlapRemovalDirection (org.eclipse.elk.alg.common.overlaps.RectangleStripOverlapRemover.OverlapRemovalDirection)2 LLabel (org.eclipse.elk.alg.layered.graph.LLabel)2 Lists (com.google.common.collect.Lists)1 HashMap (java.util.HashMap)1 Iterator (java.util.Iterator)1 List (java.util.List)1 Collectors (java.util.stream.Collectors)1 AtomicCell (org.eclipse.elk.alg.common.nodespacing.cellsystem.AtomicCell)1 StripContainerCell (org.eclipse.elk.alg.common.nodespacing.cellsystem.StripContainerCell)1 NodeLabelLocation (org.eclipse.elk.alg.common.nodespacing.internal.NodeLabelLocation)1 LGraph (org.eclipse.elk.alg.layered.graph.LGraph)1 LMargin (org.eclipse.elk.alg.layered.graph.LMargin)1 LNode (org.eclipse.elk.alg.layered.graph.LNode)1