Search in sources :

Example 66 with KVector

use of org.eclipse.elk.core.math.KVector in project elk by eclipse.

the class ComponentsCompactor method transformLEdge.

/**
 * Converts an external edge to an external extension. While doing so, the the original edge
 * contributes three things as explained in the code.
 */
private IExternalExtension<LEdge> transformLEdge(final LEdge externalEdge, final Hullpoints hullPoints, final OuterSegments outerSegments) {
    InternalExternalExtension externalExtension = new InternalExternalExtension(externalEdge);
    // #1 convert the edge's path into a set of segments
    Segments segments = edgeToSegments(externalEdge, externalExtension);
    // #2 all 'inner' segments contribute to the hull (consider the edge's thickness)
    double thickness = Math.max(externalEdge.getProperty(LayeredOptions.EDGE_THICKNESS).doubleValue(), 1);
    for (Pair<KVector, KVector> segment : segments.innerSegments) {
        ElkRectangle rect = segmentToRectangle(segment.getFirst(), segment.getSecond(), thickness);
        hullPoints.add(rect);
    }
    // #3 the 'outer' segment, being the segment that actually connects to the external port,
    // contributes to the 'union external segment' that we create
    // for one direction of the component
    PortSide side = externalExtension.externalPortSide;
    ElkRectangle outerSegmentRect = segmentToRectangle(segments.outerSegment.getFirst(), segments.outerSegment.getSecond(), thickness);
    if (side == PortSide.WEST || side == PortSide.EAST) {
        outerSegments.min[side.ordinal()] = Math.min(outerSegments.min[side.ordinal()], outerSegmentRect.y);
        outerSegments.max[side.ordinal()] = Math.max(outerSegments.max[side.ordinal()], outerSegmentRect.y + outerSegmentRect.height);
    } else {
        outerSegments.min[side.ordinal()] = Math.min(outerSegments.min[side.ordinal()], outerSegmentRect.x);
        outerSegments.max[side.ordinal()] = Math.max(outerSegments.max[side.ordinal()], outerSegmentRect.x + outerSegmentRect.width);
    }
    // extent
    double extent = Double.NEGATIVE_INFINITY;
    LMargin margins = externalExtension.externalPort.getNode().getMargin();
    switch(side) {
        case WEST:
            extent = margins.right;
            break;
        case EAST:
            extent = margins.left;
            break;
        case NORTH:
            extent = margins.bottom;
            break;
        case SOUTH:
            extent = margins.top;
            break;
    }
    outerSegments.extent[side.ordinal()] = Math.max(outerSegments.extent[side.ordinal()], extent);
    return externalExtension;
}
Also used : LMargin(org.eclipse.elk.alg.layered.graph.LMargin) KVector(org.eclipse.elk.core.math.KVector) ElkRectangle(org.eclipse.elk.core.math.ElkRectangle) PortSide(org.eclipse.elk.core.options.PortSide)

Example 67 with KVector

use of org.eclipse.elk.core.math.KVector in project elk by eclipse.

the class ComponentsCompactor method edgeToSegments.

private Segments edgeToSegments(final LEdge edge, final InternalExternalExtension externalExtension) {
    LPort externalPort = externalExtension.externalPort;
    PortSide externalPortSide = externalExtension.externalPortSide;
    // extract the correct segment that is to be used for the external extension
    KVector p1 = edge.getSource().getAbsoluteAnchor();
    KVector p2 = edge.getTarget().getAbsoluteAnchor();
    // of the diagram yet, hence we adjust their position here
    if (externalPort == edge.getSource()) {
        p1 = getExternalPortPosition(p1, externalPortSide);
        p2 = getPortPositionOnMargin(edge.getTarget());
    } else {
        p1 = getPortPositionOnMargin(edge.getSource());
        p2 = getExternalPortPosition(p2, externalPortSide);
    }
    KVectorChain points = new KVectorChain(edge.getBendPoints());
    points.addFirst(p1);
    points.addLast(p2);
    boolean outerSegmentIsFirst = edge.getSource() == externalPort;
    // for easy processing, make it a list of segments
    Segments segments = new Segments();
    for (int i = 0; i < points.size() - 1; ++i) {
        Pair<KVector, KVector> segment = Pair.of(points.get(i), points.get(i + 1));
        if ((outerSegmentIsFirst && i == 0) || (!outerSegmentIsFirst && i == points.size() - 2)) {
            segments.outerSegment = segment;
        } else {
            segments.innerSegments.add(segment);
        }
    }
    return segments;
}
Also used : KVectorChain(org.eclipse.elk.core.math.KVectorChain) LPort(org.eclipse.elk.alg.layered.graph.LPort) PortSide(org.eclipse.elk.core.options.PortSide) KVector(org.eclipse.elk.core.math.KVector) Point(org.eclipse.elk.alg.layered.compaction.recthull.Point)

Example 68 with KVector

use of org.eclipse.elk.core.math.KVector in project elk by eclipse.

the class ComponentsCompactor method compact.

// ------------------------------------------------------------------------------------------------
// public API
// ------------------------------------------------------------------------------------------------
/**
 * @param graphs
 *            the components to be compacted
 * @param originalGraphsSize
 *            the size of the overall graph as it is currently
 * @param spacing
 *            the desired spacing to be preserved between any pair of components
 */
public void compact(final List<LGraph> graphs, final KVector originalGraphsSize, final double spacing) {
    // determine the extreme points of the current diagram,
    // we will reuse this 'frame' to cut external extensions at appropriate lengths
    graphTopLeft = new KVector(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
    graphBottomRight = new KVector(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
    for (LGraph graph : graphs) {
        for (LNode node : graph.getLayerlessNodes()) {
            graphTopLeft.x = Math.min(graphTopLeft.x, node.getPosition().x - node.getMargin().left);
            graphTopLeft.y = Math.min(graphTopLeft.y, node.getPosition().y - node.getMargin().top);
            graphBottomRight.x = Math.max(graphBottomRight.x, node.getPosition().x + node.getSize().x + node.getMargin().right);
            graphBottomRight.y = Math.max(graphBottomRight.y, node.getPosition().y + node.getSize().y + node.getMargin().bottom);
        }
    }
    // from the lgraphs, create connected components
    IConnectedComponents<LNode, Set<LEdge>> ccs = new InternalConnectedComponents();
    for (LGraph graph : graphs) {
        IComponent<LNode, Set<LEdge>> c = transformLGraph(graph);
        ccs.getComponents().add(c);
        ((InternalComponent) c).containsRegularNodes |= !c.getExternalExtensionSides().isEmpty();
    }
    // for every component we create an element in the compactor
    compactor = OneDimensionalComponentsCompaction.init(ccs, spacing);
    // execute compaction
    compactor.compact(new BasicProgressMonitor());
    yetAnotherOffset = new KVector();
    compactedGraphSize = compactor.getGraphSize();
    // apply the positions
    for (IComponent<LNode, Set<LEdge>> cc : ccs.getComponents()) {
        // retrieve the common offset for the currently handled connected component
        KVector offset = compactor.getOffset(cc);
        // move it
        LGraphUtil.offsetGraph(((InternalComponent) cc).graph, offset.x, offset.y);
        // adjust positions of external ports
        for (LNode n : ((InternalComponent) cc).getNodes()) {
            if (n.getType() == NodeType.EXTERNAL_PORT) {
                KVector newPos = getExternalPortPosition(n.getPosition(), n.getProperty(InternalProperties.EXT_PORT_SIDE));
                n.getPosition().reset().add(newPos);
            }
        }
    }
    // external edges contribute to the graph's size ... however, only certain segments do.
    for (IComponent<LNode, Set<LEdge>> cc : ccs.getComponents()) {
        for (LEdge e : ((InternalComponent) cc).getExternalEdges()) {
            KVectorChain vc = new KVectorChain(e.getBendPoints());
            vc.add(0, e.getSource().getAbsoluteAnchor());
            vc.add(e.getTarget().getAbsoluteAnchor());
            KVector last = null;
            for (KVector v : vc) {
                if (last == null) {
                    last = v;
                    continue;
                }
                if (DoubleMath.fuzzyEquals(last.x, v.x, EPSILON)) {
                    yetAnotherOffset.x = Math.min(yetAnotherOffset.x, last.x);
                    compactedGraphSize.x = Math.max(compactedGraphSize.x, last.x);
                } else if (DoubleMath.fuzzyEquals(last.y, v.y, EPSILON)) {
                    yetAnotherOffset.y = Math.min(yetAnotherOffset.y, last.y);
                    compactedGraphSize.y = Math.max(compactedGraphSize.y, last.y);
                }
                last = v;
            }
        }
    }
    yetAnotherOffset.negate();
    compactedGraphSize.add(yetAnotherOffset);
}
Also used : Set(java.util.Set) LEdge(org.eclipse.elk.alg.layered.graph.LEdge) KVectorChain(org.eclipse.elk.core.math.KVectorChain) LNode(org.eclipse.elk.alg.layered.graph.LNode) KVector(org.eclipse.elk.core.math.KVector) LGraph(org.eclipse.elk.alg.layered.graph.LGraph) BasicProgressMonitor(org.eclipse.elk.core.util.BasicProgressMonitor)

Example 69 with KVector

use of org.eclipse.elk.core.math.KVector in project elk by eclipse.

the class SimpleRowGraphPlacer method combine.

@Override
public void combine(final List<LGraph> components, final LGraph target) {
    if (components.size() == 1) {
        LGraph source = components.get(0);
        if (source != target) {
            target.getLayerlessNodes().clear();
            moveGraph(target, source, 0, 0);
            target.copyProperties(source);
            target.getPadding().copy(source.getPadding());
            target.getSize().x = source.getSize().x;
            target.getSize().y = source.getSize().y;
        }
        return;
    } else if (components.isEmpty()) {
        target.getLayerlessNodes().clear();
        target.getSize().x = 0;
        target.getSize().y = 0;
        return;
    }
    assert !components.contains(target);
    // assign priorities
    for (LGraph graph : components) {
        int priority = 0;
        for (LNode node : graph.getLayerlessNodes()) {
            priority += node.getProperty(LayeredOptions.PRIORITY);
        }
        graph.id = priority;
    }
    // sort the components by their priority and size.
    // If preserve order is set, we do not consider the size.
    Collections.sort(components, new Comparator<LGraph>() {

        public int compare(final LGraph graph1, final LGraph graph2) {
            int prio = graph2.id - graph1.id;
            if (prio == 0) {
                if (graph1.getProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY) == OrderingStrategy.NONE) {
                    double size1 = graph1.getSize().x * graph1.getSize().y;
                    double size2 = graph2.getSize().x * graph2.getSize().y;
                    return Double.compare(size1, size2);
                }
            }
            return prio;
        }
    });
    LGraph firstComponent = components.get(0);
    target.getLayerlessNodes().clear();
    target.copyProperties(firstComponent);
    // determine the maximal row width by the maximal box width and the total area
    double maxRowWidth = 0.0f;
    double totalArea = 0.0f;
    for (LGraph graph : components) {
        KVector size = graph.getSize();
        maxRowWidth = Math.max(maxRowWidth, size.x);
        totalArea += size.x * size.y;
    }
    maxRowWidth = Math.max(maxRowWidth, (float) Math.sqrt(totalArea) * target.getProperty(LayeredOptions.ASPECT_RATIO));
    double componentSpacing = target.getProperty(LayeredOptions.SPACING_COMPONENT_COMPONENT);
    // place nodes iteratively into rows
    double xpos = 0, ypos = 0, highestBox = 0, broadestRow = componentSpacing;
    for (LGraph graph : components) {
        KVector size = graph.getSize();
        if (xpos + size.x > maxRowWidth) {
            // place the graph into the next row
            xpos = 0;
            ypos += highestBox + componentSpacing;
            highestBox = 0;
        }
        KVector offset = graph.getOffset();
        offsetGraph(graph, xpos + offset.x, ypos + offset.y);
        offset.reset();
        broadestRow = Math.max(broadestRow, xpos + size.x);
        highestBox = Math.max(highestBox, size.y);
        xpos += size.x + componentSpacing;
    }
    target.getSize().x = broadestRow;
    target.getSize().y = ypos + highestBox;
    // if compaction is desired, do so!
    if (firstComponent.getProperty(LayeredOptions.COMPACTION_CONNECTED_COMPONENTS)) {
        ComponentsCompactor compactor = new ComponentsCompactor();
        compactor.compact(components, target.getSize(), componentSpacing);
        // therefore we have to use the final drawing's offset
        for (LGraph h : components) {
            h.getOffset().reset().add(compactor.getOffset());
        }
        // set the new graph size
        target.getSize().reset().add(compactor.getGraphSize());
    }
    // finally move the components to the combined graph
    moveGraphs(target, components, 0, 0);
}
Also used : LNode(org.eclipse.elk.alg.layered.graph.LNode) LGraph(org.eclipse.elk.alg.layered.graph.LGraph) KVector(org.eclipse.elk.core.math.KVector)

Example 70 with KVector

use of org.eclipse.elk.core.math.KVector in project elk by eclipse.

the class CompoundGraphPostprocessor method process.

@Override
public void process(final LGraph graph, final IElkProgressMonitor monitor) {
    monitor.begin("Compound graph postprocessor", 1);
    // whether bend points should be added whenever crossing a hierarchy boundary
    boolean addUnnecessaryBendpoints = graph.getProperty(LayeredOptions.UNNECESSARY_BENDPOINTS);
    // restore the cross-hierarchy map that was built by the preprocessor
    Multimap<LEdge, CrossHierarchyEdge> crossHierarchyMap = graph.getProperty(InternalProperties.CROSS_HIERARCHY_MAP);
    // remember all dummy edges we encounter; these need to be removed at the end
    Set<LEdge> dummyEdges = Sets.newHashSet();
    // iterate over all original edges
    for (LEdge origEdge : crossHierarchyMap.keySet()) {
        // find all cross-hierarchy edges the original edge was split into, and sort them from source to target
        List<CrossHierarchyEdge> crossHierarchyEdges = new ArrayList<CrossHierarchyEdge>(crossHierarchyMap.get(origEdge));
        Collections.sort(crossHierarchyEdges, new CrossHierarchyEdgeComparator(graph));
        // find the original source and target ports for the original edge
        LPort sourcePort = crossHierarchyEdges.get(0).getActualSource();
        LPort targetPort = crossHierarchyEdges.get(crossHierarchyEdges.size() - 1).getActualTarget();
        // determine the reference graph for all bend points
        LNode referenceNode = sourcePort.getNode();
        LGraph referenceGraph;
        if (LGraphUtil.isDescendant(targetPort.getNode(), referenceNode)) {
            referenceGraph = referenceNode.getNestedGraph();
        } else {
            referenceGraph = referenceNode.getGraph();
        }
        // check whether there are any junction points
        KVectorChain junctionPoints = clearJunctionPoints(origEdge, crossHierarchyEdges);
        // reset bend points (we have computed new ones anyway)
        origEdge.getBendPoints().clear();
        // apply the computed layouts to the cross-hierarchy edge
        KVector lastPoint = null;
        for (CrossHierarchyEdge chEdge : crossHierarchyEdges) {
            // transform all coordinates from the graph of the dummy edge to the reference graph
            KVector offset = new KVector();
            LGraphUtil.changeCoordSystem(offset, chEdge.getGraph(), referenceGraph);
            LEdge ledge = chEdge.getEdge();
            KVectorChain bendPoints = new KVectorChain();
            bendPoints.addAllAsCopies(0, ledge.getBendPoints());
            bendPoints.offset(offset);
            // Note: if an NPE occurs here, that means ELK Layered has replaced the original edge
            KVector sourcePoint = new KVector(ledge.getSource().getAbsoluteAnchor());
            KVector targetPoint = new KVector(ledge.getTarget().getAbsoluteAnchor());
            sourcePoint.add(offset);
            targetPoint.add(offset);
            if (lastPoint != null) {
                KVector nextPoint;
                if (bendPoints.isEmpty()) {
                    nextPoint = targetPoint;
                } else {
                    nextPoint = bendPoints.getFirst();
                }
                // we add the source point as a bend point to properly connect the hierarchy levels
                // either if the last point of the previous hierarchy edge segment is a certain
                // level of tolerance away or if we are required to add unnecessary bend points
                boolean xDiffEnough = Math.abs(lastPoint.x - nextPoint.x) > OrthogonalRoutingGenerator.TOLERANCE;
                boolean yDiffEnough = Math.abs(lastPoint.y - nextPoint.y) > OrthogonalRoutingGenerator.TOLERANCE;
                if ((!addUnnecessaryBendpoints && xDiffEnough && yDiffEnough) || (addUnnecessaryBendpoints && (xDiffEnough || yDiffEnough))) {
                    origEdge.getBendPoints().add(sourcePoint);
                }
            }
            origEdge.getBendPoints().addAll(bendPoints);
            if (bendPoints.isEmpty()) {
                lastPoint = sourcePoint;
            } else {
                lastPoint = bendPoints.getLast();
            }
            // copy junction points
            copyJunctionPoints(ledge, junctionPoints, offset);
            // add offset to target port with a special property
            if (chEdge.getActualTarget() == targetPort) {
                if (targetPort.getNode().getGraph() != chEdge.getGraph()) {
                    // the target port is in a different coordinate system -- recompute the offset
                    offset = new KVector();
                    LGraphUtil.changeCoordSystem(offset, targetPort.getNode().getGraph(), referenceGraph);
                }
                origEdge.setProperty(InternalProperties.TARGET_OFFSET, offset);
            }
            // copy labels back to the original edge
            copyLabelsBack(ledge, origEdge, referenceGraph);
            // remember the dummy edge for later removal (dummy edges may be in use by several
            // different original edges, which is why we cannot just go and remove it now)
            dummyEdges.add(ledge);
        }
        // restore the original source port and target port
        origEdge.setSource(sourcePort);
        origEdge.setTarget(targetPort);
    }
    // remove the dummy edges from the graph (dummy ports and dummy nodes are retained)
    for (LEdge dummyEdge : dummyEdges) {
        dummyEdge.setSource(null);
        dummyEdge.setTarget(null);
    }
    monitor.done();
}
Also used : LEdge(org.eclipse.elk.alg.layered.graph.LEdge) KVectorChain(org.eclipse.elk.core.math.KVectorChain) ArrayList(java.util.ArrayList) LGraph(org.eclipse.elk.alg.layered.graph.LGraph) LPort(org.eclipse.elk.alg.layered.graph.LPort) LNode(org.eclipse.elk.alg.layered.graph.LNode) KVector(org.eclipse.elk.core.math.KVector)

Aggregations

KVector (org.eclipse.elk.core.math.KVector)292 KVectorChain (org.eclipse.elk.core.math.KVectorChain)52 ElkNode (org.eclipse.elk.graph.ElkNode)49 LNode (org.eclipse.elk.alg.layered.graph.LNode)39 LPort (org.eclipse.elk.alg.layered.graph.LPort)37 ElkRectangle (org.eclipse.elk.core.math.ElkRectangle)36 LEdge (org.eclipse.elk.alg.layered.graph.LEdge)35 Test (org.junit.Test)30 ElkEdge (org.eclipse.elk.graph.ElkEdge)28 ElkLabel (org.eclipse.elk.graph.ElkLabel)27 ElkEdgeSection (org.eclipse.elk.graph.ElkEdgeSection)26 ElkPadding (org.eclipse.elk.core.math.ElkPadding)25 LLabel (org.eclipse.elk.alg.layered.graph.LLabel)20 PortSide (org.eclipse.elk.core.options.PortSide)19 ElkPort (org.eclipse.elk.graph.ElkPort)18 SizeConstraint (org.eclipse.elk.core.options.SizeConstraint)17 LGraph (org.eclipse.elk.alg.layered.graph.LGraph)15 ArrayList (java.util.ArrayList)13 Layer (org.eclipse.elk.alg.layered.graph.Layer)12 LMargin (org.eclipse.elk.alg.layered.graph.LMargin)11