use of org.eclipse.elk.core.math.ElkRectangle in project elk by eclipse.
the class EndLabelPostprocessor method processNode.
private void processNode(final LNode node) {
assert node.hasProperty(InternalProperties.END_LABELS);
// The node should have a non-empty list of label cells, or something went TERRIBLY WRONG!!!
Map<LPort, LabelCell> endLabelCells = node.getProperty(InternalProperties.END_LABELS);
assert !endLabelCells.isEmpty();
KVector nodePos = node.getPosition();
for (LabelCell labelCell : endLabelCells.values()) {
ElkRectangle labelCellRect = labelCell.getCellRectangle();
labelCellRect.move(nodePos);
labelCell.applyLabelLayout();
}
// Remove label cells
node.setProperty(InternalProperties.END_LABELS, null);
}
use of org.eclipse.elk.core.math.ElkRectangle in project elk by eclipse.
the class FinalSplineBendpointsCalculator method calculateControlPointsSloppy.
/**
* As opposed to the conservative strategy to insert control points (see
* {@link #calculateControlPointsConservative(LEdge, SplineSegment)}), the sloppy version tries to use less control
* points to make the splines curvier. The method handles only the case in which one of the two involved nodes is a
* {@link SplineEdgeRouter#isNormalNode(LNode) normal} node.
*
* Let {@code e = (u,v)} be the edge for which control points are to be determined. The conservative strategy adds
* control points {@code a,b,c,d} as depicted in the figure below. For a normal node {@code u}, it is checked if
* {@code a,b} can be omitted. This is the case if the path of the resulting spline doesn't intersect a node that
* lies above or below {@code u}. The same is checked for a regular node {@code v} and the control points
* {@code c,d}. In case all control points could be omitted, a single control point is added halfway in between the
* two nodes, where the y-coordinate is computed as implemented in
* {@link #computeSloppyCenterY(LEdge, double, double)}. Note that if no control points can be omitted,
* the same control points are used as with the conservative strategy.
*
* <pre>
* v
* ___
* | |
* u c--d| |
* ___ | |___|
* | | |
* | |a--b
* |___|
* </pre>
*
* To illustrate the three possible scenarios in which control points are omitted.
* <pre>
* shortcut src shortcut tgt shortcut both
* </pre>
*
* <pre>
* ___ ___ ___
* | | | | | |
* +--+| | .---| | .---| |
* ___ | |___| ___ | |___| ___ + |___|
* | | | | | | | | |
* | |---` | |+--+ | |---`
* |___| |___| |___|
* </pre>
*/
private void calculateControlPointsSloppy(final LEdge edge, final SplineSegment containingSegment) {
EdgeInformation ei = containingSegment.edgeInformation.get(edge);
assert ei.normalSourceNode || ei.normalTargetNode;
double startXPos = containingSegment.boundingBox.x;
double endXPos = containingSegment.boundingBox.x + containingSegment.boundingBox.width;
final double ySourceAnchor = ei.startY;
final double yTargetAnchor = ei.endY;
final boolean edgePointsDownwards = ySourceAnchor < yTargetAnchor;
// pre-compute a number of coordinates that we might use as control points
final KVector sourceStraightCP = new KVector(startXPos, ySourceAnchor);
final KVector targetStraightCP = new KVector(endXPos, yTargetAnchor);
final double centerXPos = (startXPos + endXPos) / 2;
final KVector sourceVerticalCP = new KVector(centerXPos, ySourceAnchor);
final KVector targetVerticalCP = new KVector(centerXPos, yTargetAnchor);
// evaluate if a rather direct curve is possible
double centerYPos = computeSloppyCenterY(edge, ySourceAnchor, yTargetAnchor);
KVector v1 = containingSegment.sourcePort.getAbsoluteAnchor();
KVector v2 = new KVector(centerXPos, centerYPos);
KVector v3 = containingSegment.targetPort.getAbsoluteAnchor();
// approx will be an array holding two values, the zeroth of which represents the
// center of the curve
KVector[] approx = ElkMath.approximateBezierSegment(2, v1, v2, v3);
boolean shortCutSource = false;
final LNode src = containingSegment.sourcePort.getNode();
// the node's layer)
if (src != null && src.getLayer() != null && ei.normalSourceNode) {
// possible intersections must only be checked if there are nodes
// with which the edge could potentially intersect
final boolean needToCheckSrc = edgePointsDownwards && src.id < src.getLayer().getNodes().size() - 1 || !edgePointsDownwards && src.id > 0;
if (!needToCheckSrc) {
shortCutSource = true;
} else {
// check within src's layer
if (needToCheckSrc) {
int neighborIndex = src.id;
if (edgePointsDownwards) {
neighborIndex++;
} else {
neighborIndex--;
}
LNode neighbor = src.getLayer().getNodes().get(neighborIndex);
ElkRectangle box = nodeToBoundingBox(neighbor);
shortCutSource = !(ElkMath.intersects(box, v1, approx[0]) || ElkMath.contains(box, v1, approx[0]));
}
}
}
boolean shortCutTarget = false;
final LNode tgt = containingSegment.targetPort.getNode();
// see comment above
if (tgt != null && tgt.getLayer() != null && ei.normalTargetNode) {
final boolean needToCheckTgt = edgePointsDownwards && tgt.id > 0 || !edgePointsDownwards && tgt.id < tgt.getLayer().getNodes().size() - 1;
// tgt's layer
if (!needToCheckTgt) {
shortCutTarget = true;
} else {
int neighborIndex = tgt.id;
if (edgePointsDownwards) {
neighborIndex--;
} else {
neighborIndex++;
}
LNode neighbor = tgt.getLayer().getNodes().get(neighborIndex);
ElkRectangle box = nodeToBoundingBox(neighbor);
shortCutTarget = !(ElkMath.intersects(box, approx[0], v3) || ElkMath.contains(box, approx[0], v3));
}
}
// now add the control points
if (shortCutSource && shortCutTarget) {
edge.getBendPoints().add(v2);
}
if (!shortCutSource) {
edge.getBendPoints().addAll(sourceStraightCP, sourceVerticalCP);
}
if (!shortCutTarget) {
edge.getBendPoints().addAll(targetVerticalCP, targetStraightCP);
}
}
use of org.eclipse.elk.core.math.ElkRectangle in project elk by eclipse.
the class LabelAndNodeSizeProcessor method placeExternalPortDummyLabels.
/**
* Places the labels of the given external port dummy such that it results in correct node margins later on that
* will reserve enough space for the labels to be placed once label and node placement is called on the graph.
*/
private void placeExternalPortDummyLabels(final LNode dummy, final Set<PortLabelPlacement> graphPortLabelPlacement, final boolean placeNextToPortIfPossible, final boolean treatAsGroup) {
double labelPortSpacing = dummy.getProperty(LayeredOptions.SPACING_LABEL_PORT);
double labelLabelSpacing = dummy.getProperty(LayeredOptions.SPACING_LABEL_LABEL);
KVector dummySize = dummy.getSize();
// External port dummies have exactly one port (see ElkGraphImporter)
LPort dummyPort = dummy.getPorts().get(0);
KVector dummyPortPos = dummyPort.getPosition();
ElkRectangle portLabelBox = computePortLabelBox(dummyPort, labelLabelSpacing);
if (portLabelBox == null) {
return;
}
// TODO We could handle FIXED here as well
if (graphPortLabelPlacement.contains(PortLabelPlacement.INSIDE)) {
// (port label placement has to support this case first, though)
switch(dummy.getProperty(InternalProperties.EXT_PORT_SIDE)) {
case NORTH:
portLabelBox.x = (dummySize.x - portLabelBox.width) / 2 - dummyPortPos.x;
portLabelBox.y = labelPortSpacing;
break;
case SOUTH:
portLabelBox.x = (dummySize.x - portLabelBox.width) / 2 - dummyPortPos.x;
portLabelBox.y = -labelPortSpacing - portLabelBox.height;
break;
case EAST:
if (labelNextToPort(dummyPort, true, placeNextToPortIfPossible)) {
double labelHeight = treatAsGroup ? portLabelBox.height : dummyPort.getLabels().get(0).getSize().y;
portLabelBox.y = (dummySize.y - labelHeight) / 2 - dummyPortPos.y;
} else {
portLabelBox.y = dummySize.y + labelPortSpacing - dummyPortPos.y;
}
portLabelBox.x = -labelPortSpacing - portLabelBox.width;
break;
case WEST:
if (labelNextToPort(dummyPort, true, placeNextToPortIfPossible)) {
double labelHeight = treatAsGroup ? portLabelBox.height : dummyPort.getLabels().get(0).getSize().y;
portLabelBox.y = (dummySize.y - labelHeight) / 2 - dummyPortPos.y;
} else {
portLabelBox.y = dummySize.y + labelPortSpacing - dummyPortPos.y;
}
portLabelBox.x = labelPortSpacing;
break;
}
} else if (graphPortLabelPlacement.contains(PortLabelPlacement.OUTSIDE)) {
switch(dummy.getProperty(InternalProperties.EXT_PORT_SIDE)) {
case NORTH:
case SOUTH:
portLabelBox.x = dummyPortPos.x + labelPortSpacing;
break;
case EAST:
case WEST:
if (labelNextToPort(dummyPort, false, placeNextToPortIfPossible)) {
double labelHeight = treatAsGroup ? portLabelBox.height : dummyPort.getLabels().get(0).getSize().y;
portLabelBox.y = (dummySize.y - labelHeight) / 2 - dummyPortPos.y;
} else {
portLabelBox.y = dummyPortPos.y + labelPortSpacing;
}
break;
}
}
// Place the labels
double currentY = portLabelBox.y;
for (LLabel label : dummyPort.getLabels()) {
KVector labelPos = label.getPosition();
labelPos.x = portLabelBox.x;
labelPos.y = currentY;
currentY += label.getSize().y + labelLabelSpacing;
}
}
use of org.eclipse.elk.core.math.ElkRectangle in project elk by eclipse.
the class ComponentsCompactor method transformLGraph.
// ------------------------------------------------------------------------------------------------
// private API
// ------------------------------------------------------------------------------------------------
/**
* Converts a {@link LGraph} into an component, i.e. a set of rectangles
* describing the space occupied by the component and a set of external extensions.
*/
private IComponent<LNode, Set<LEdge>> transformLGraph(final LGraph graph) {
InternalComponent component = new InternalComponent(graph);
if (!component.containsRegularNodes) {
createDummyNode(graph);
}
// #1 convert the nodes to hull points
Hullpoints hullPoints = componentHullPoints(graph);
// #2 convert external edges,
// while doing this, add further hull points contributed by inner segments
// and remember extreme points of external segments
Multimap<Direction, LEdge> externalExtensions = HashMultimap.create();
OuterSegments outerSegments = new OuterSegments();
for (LNode node : graph.getLayerlessNodes()) {
for (LEdge edge : node.getOutgoingEdges()) {
if (isExternalEdge(edge)) {
IExternalExtension<LEdge> iee = transformLEdge(edge, hullPoints, outerSegments);
externalExtensions.put(iee.getDirection(), iee.getRepresentative());
}
}
}
// #3 create the common external extensions for this component
// (there can be 4 per component, one in each direction)
List<InternalUnionExternalExtension> extensions = Lists.newArrayList();
for (PortSide ps : component.getExternalExtensionSides()) {
double min = outerSegments.min[ps.ordinal()];
double max = outerSegments.max[ps.ordinal()];
double extent = outerSegments.extent[ps.ordinal()];
ElkRectangle extension = null;
ElkRectangle placeholder = null;
switch(ps) {
case WEST:
extension = new ElkRectangle(graphTopLeft.x, min, hullPoints.topLeft.x - graphTopLeft.x, max - min);
placeholder = new ElkRectangle(graphTopLeft.x, min, extent, max - min);
hullPoints.add(extension.getTopRight());
hullPoints.add(extension.getBottomRight());
break;
case EAST:
extension = new ElkRectangle(hullPoints.bottomRight.x, min, graphBottomRight.x - hullPoints.bottomRight.x, max - min);
placeholder = new ElkRectangle(graphBottomRight.x - extent, min, extent, max - min);
hullPoints.add(extension.getTopLeft());
hullPoints.add(extension.getBottomLeft());
break;
case NORTH:
extension = new ElkRectangle(min, graphTopLeft.y, max - min, hullPoints.topLeft.y - graphTopLeft.y);
placeholder = new ElkRectangle(min, graphTopLeft.y, max - min, extent);
hullPoints.add(extension.getBottomLeft());
hullPoints.add(extension.getBottomRight());
break;
case SOUTH:
extension = new ElkRectangle(min, hullPoints.bottomRight.y, max - min, graphBottomRight.y - hullPoints.bottomRight.y);
placeholder = new ElkRectangle(min, graphBottomRight.y - extent, max - min, extent);
hullPoints.add(extension.getTopLeft());
hullPoints.add(extension.getTopRight());
break;
}
// instantiate the external extension object
if (extension != null) {
InternalUnionExternalExtension iuee = new InternalUnionExternalExtension();
iuee.side = ps;
iuee.extension = extension;
iuee.placeholder = placeholder;
iuee.edges = Sets.newHashSet(externalExtensions.get(portSideToDirection(ps)));
extensions.add(iuee);
}
}
// #4 calculate the hull for the component
component.externalExtensions.addAll(extensions);
component.rectilinearConvexHull = RectilinearConvexHull.of(hullPoints).splitIntoRectangles();
return component;
}
use of org.eclipse.elk.core.math.ElkRectangle in project elk by eclipse.
the class RectilinearConvexHullTest method visualize.
private static void visualize(final List<Point> points, final RectilinearConvexHull rch) {
KVector offset = new KVector(10, 10);
double scale = 10;
@SuppressWarnings("serial") JPanel p = new JPanel() {
public void paint(java.awt.Graphics g) {
super.paint(g);
g.setColor(Color.DARK_GRAY);
g.fillPolygon(toPolygon(rch, offset, scale));
g.setColor(Color.GRAY);
for (Point p : points) {
g.fillRect((int) (p.x * scale + offset.x) - 1, (int) (p.y * scale + offset.y) - 1, 3, 3);
}
g.setColor(Color.PINK);
for (ElkRectangle r : rch.splitIntoRectangles()) {
g.drawRect((int) (r.x * scale + offset.x), (int) ((r.y) * scale + offset.y), (int) (r.width * scale), (int) (r.height * scale));
}
g.setColor(Color.GRAY);
}
};
JFrame f = new JFrame();
f.setSize(1500, 1500);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
f.getContentPane().add(p);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Aggregations