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;
}
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;
}
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);
}
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);
}
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();
}
Aggregations