use of org.eclipse.elk.core.options.PortSide 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.options.PortSide in project elk by eclipse.
the class CompoundGraphPreprocessor method introduceHierarchicalEdgeSegment.
// //////////////////////////////////////////////////////////////////////////////////////////
// General Hierarchical Edge Segment Processing
/**
* Does the actual work of creating a new hierarchical edge segment between an external port and
* a given opposite port. The external port used for the segment is returned. This method does
* not put any created edges into the cross hierarchy map!
*
* <p>
* The method first decides on an external port to use for the segment. If the default external
* port passed to the method is not {@code null} and if external ports are to be merged in the
* current graph, the default external port is reused. An exception are segments that start or
* end in the parent node; each such segments gets its own external port.
* </p>
*
* <p>
* If a new external port is created, the method also creates a dummy node for it as well as an
* actual port on the parent node, if no such port already exists, as well as a dummy edge for
* the connection. Thus, the newly created external port has everything it needs to be properly
* represented and initialized.
* </p>
*
* <p>
* The original edge is added to the list of original edges in the external port used for the
* segment. The dummy edge is associated with the original hierarchy-crossing edge in the cross
* hierarchy map.
* </p>
*
* @param graph
* the layered graph.
* @param parentNode
* the graph's parent node, or {@code null} if the graph is at the top level.
* @param origEdge
* the hierarchy-crossing edge that is being broken.
* @param oppositePort
* the port that will be one of the two end points of the new segment.
* @param portType
* the type of the port to create as one of the segment's edge points.
* @param defaultExternalPort
* a default external port we can reuse if external ports should be merged. If this
* is {@code null}, a new external port is always created. If this port is reused, it
* is returned by this method.
* @return the (created or reused) external port used as one endpoint of the edge segment.
*/
private ExternalPort introduceHierarchicalEdgeSegment(final LGraph graph, final LNode parentNode, final LEdge origEdge, final LPort oppositePort, final PortType portType, final ExternalPort defaultExternalPort) {
// check if external ports are to be merged
boolean mergeExternalPorts = graph.getProperty(LayeredOptions.MERGE_HIERARCHY_EDGES);
// check if the edge connects to the parent node instead of to the outside world; if so, the
// parentEndPort will be non-null
LPort parentEndPort = null;
if (portType == PortType.INPUT && origEdge.getSource().getNode() == parentNode) {
parentEndPort = origEdge.getSource();
} else if (portType == PortType.OUTPUT && origEdge.getTarget().getNode() == parentNode) {
parentEndPort = origEdge.getTarget();
}
// only create a new external port if the current one is null or if ports are not to be merged
// or if the connection actually ends at the parent node
ExternalPort externalPort = defaultExternalPort;
if (externalPort == null || !mergeExternalPorts || parentEndPort != null) {
// create a dummy node that will represent the external port
PortSide externalPortSide = PortSide.UNDEFINED;
if (parentEndPort != null) {
externalPortSide = parentEndPort.getSide();
} else {
// for people to set compound node port constraints to FREE
if (parentNode.getProperty(LayeredOptions.PORT_CONSTRAINTS).isSideFixed()) {
externalPortSide = portType == PortType.INPUT ? PortSide.WEST : PortSide.EAST;
}
}
LNode dummyNode = createExternalPortDummy(graph, parentNode, portType, externalPortSide, origEdge);
// create a dummy edge to be connected to the port
LEdge dummyEdge = createDummyEdge(parentNode.getGraph(), origEdge);
if (portType == PortType.INPUT) {
// if the external port is an input port, the source of the edge must be connected to
// the new dummy node
dummyEdge.setSource(dummyNode.getPorts().get(0));
dummyEdge.setTarget(oppositePort);
} else {
// if the external port is an output port, the target of the edge must be connected to
// the new dummy node
dummyEdge.setSource(oppositePort);
dummyEdge.setTarget(dummyNode.getPorts().get(0));
}
// create the external port (the port is to be exported if the connection is not just to the
// parent node)
externalPort = new ExternalPort(origEdge, dummyEdge, dummyNode, (LPort) dummyNode.getProperty(InternalProperties.ORIGIN), portType, parentEndPort == null);
} else {
// we use an existing external port, so simply add the original edge to its list of
// original edges
externalPort.origEdges.add(origEdge);
// merge the properties of the original edges
double thickness = Math.max(externalPort.newEdge.getProperty(LayeredOptions.EDGE_THICKNESS), origEdge.getProperty(LayeredOptions.EDGE_THICKNESS));
externalPort.newEdge.setProperty(LayeredOptions.EDGE_THICKNESS, thickness);
}
crossHierarchyMap.put(origEdge, new CrossHierarchyEdge(externalPort.newEdge, graph, portType));
return externalPort;
}
use of org.eclipse.elk.core.options.PortSide in project elk by eclipse.
the class LGraphUtil method createPort.
// /////////////////////////////////////////////////////////////////////////////
// Handling of Ports
/**
* Create a port for an edge that is not connected to a port. This is necessary because ELK
* Layered wants all edges to have a source port and a target port. The port side is computed
* from the given absolute end point position of the edge.
*
* @param node
* the node at which the edge is incident
* @param endPoint
* the absolute point where the edge ends, or {@code null} if unknown
* @param type
* the port type
* @param layeredGraph
* the layered graph
* @return a new port
*/
public static LPort createPort(final LNode node, final KVector endPoint, final PortType type, final LGraph layeredGraph) {
LPort port;
Direction direction = getDirection(layeredGraph);
boolean mergePorts = layeredGraph.getProperty(LayeredOptions.MERGE_EDGES);
if ((mergePorts || node.getProperty(LayeredOptions.HYPERNODE)) && !node.getProperty(LayeredOptions.PORT_CONSTRAINTS).isSideFixed()) {
// Hypernodes have one output port and one input port
PortSide defaultSide = PortSide.fromDirection(direction);
port = provideCollectorPort(layeredGraph, node, type, type == PortType.OUTPUT ? defaultSide : defaultSide.opposed());
} else {
port = new LPort();
port.setNode(node);
if (endPoint != null) {
KVector pos = port.getPosition();
pos.x = endPoint.x - node.getPosition().x;
pos.y = endPoint.y - node.getPosition().y;
pos.bound(0, 0, node.getSize().x, node.getSize().y);
port.setSide(calcPortSide(port, direction));
} else {
PortSide defaultSide = PortSide.fromDirection(direction);
port.setSide(type == PortType.OUTPUT ? defaultSide : defaultSide.opposed());
}
Set<GraphProperties> graphProperties = layeredGraph.getProperty(InternalProperties.GRAPH_PROPERTIES);
PortSide portSide = port.getSide();
switch(direction) {
case LEFT:
case RIGHT:
if (portSide == PortSide.NORTH || portSide == PortSide.SOUTH) {
graphProperties.add(GraphProperties.NORTH_SOUTH_PORTS);
}
break;
case UP:
case DOWN:
if (portSide == PortSide.EAST || portSide == PortSide.WEST) {
graphProperties.add(GraphProperties.NORTH_SOUTH_PORTS);
}
break;
}
}
return port;
}
use of org.eclipse.elk.core.options.PortSide in project elk by eclipse.
the class LGraphUtil method initializePort.
/**
* Initialize the side, offset, and anchor point of the given port. The port is assumed to
* be also present in the original graph structure. The port's current position and the size
* of the corresponding node are used to determine missing values.
*
* @param port a port
* @param portConstraints the port constraints of the containing node
* @param direction the overall layout direction
* @param anchorPos the anchor position, or {@code null} if it shall be determined automatically
*/
public static void initializePort(final LPort port, final PortConstraints portConstraints, final Direction direction, final KVector anchorPos) {
PortSide portSide = port.getSide();
if (portSide == PortSide.UNDEFINED && portConstraints.isSideFixed()) {
// calculate the port side and offset from the port's current position
portSide = calcPortSide(port, direction);
port.setSide(portSide);
// frustration-free
if (!port.getAllProperties().containsKey(LayeredOptions.PORT_BORDER_OFFSET) && portSide != PortSide.UNDEFINED && (port.getPosition().x != 0 || port.getPosition().y != 0)) {
port.setProperty(LayeredOptions.PORT_BORDER_OFFSET, calcPortOffset(port, portSide));
}
}
// if the port constraints are set to fixed ratio, remember the current ratio
if (portConstraints.isRatioFixed()) {
double ratio = 0.0;
switch(portSide) {
case NORTH:
case SOUTH:
double nodeWidth = port.getNode().getSize().x;
if (nodeWidth > 0) {
ratio = port.getPosition().x / nodeWidth;
}
break;
case EAST:
case WEST:
double nodeHeight = port.getNode().getSize().y;
if (nodeHeight > 0) {
ratio = port.getPosition().y / nodeHeight;
}
break;
}
port.setProperty(InternalProperties.PORT_RATIO_OR_POSITION, ratio);
}
KVector portSize = port.getSize();
KVector portAnchor = port.getAnchor();
// if the port anchor property is set, use it as anchor point
if (anchorPos != null) {
portAnchor.x = anchorPos.x;
portAnchor.y = anchorPos.y;
// Since we have applied an explicit anchor, assume the user knows what they are doing and fix it
port.setExplicitlySuppliedPortAnchor(true);
} else if (portConstraints.isSideFixed() && portSide != PortSide.UNDEFINED) {
// set the anchor point according to the port side
switch(portSide) {
case NORTH:
portAnchor.x = portSize.x / 2;
break;
case EAST:
portAnchor.x = portSize.x;
portAnchor.y = portSize.y / 2;
break;
case SOUTH:
portAnchor.x = portSize.x / 2;
portAnchor.y = portSize.y;
break;
case WEST:
portAnchor.y = portSize.y / 2;
break;
}
} else {
// the port side will be decided later, so set the anchor to the center point
portAnchor.x = portSize.x / 2;
portAnchor.y = portSize.y / 2;
}
}
use of org.eclipse.elk.core.options.PortSide in project elk by eclipse.
the class ElkUtil method resizeNode.
/**
* Resize a node to the given width and height, adjusting port and label positions if needed.
*
* @param node a node
* @param newWidth the new width to set
* @param newHeight the new height to set
* @param movePorts whether port positions should be adjusted
* @param moveLabels whether label positions should be adjusted
* @return a vector holding the width and height resizing ratio
*/
public static KVector resizeNode(final ElkNode node, final double newWidth, final double newHeight, final boolean movePorts, final boolean moveLabels) {
KVector oldSize = new KVector(node.getWidth(), node.getHeight());
KVector newSize = effectiveMinSizeConstraintFor(node);
newSize.x = Math.max(newSize.x, newWidth);
newSize.y = Math.max(newSize.y, newHeight);
double widthRatio = newSize.x / oldSize.x;
double heightRatio = newSize.y / oldSize.y;
double widthDiff = newSize.x - oldSize.x;
double heightDiff = newSize.y - oldSize.y;
// update port positions
if (movePorts) {
Direction direction = node.getParent() == null ? node.getProperty(CoreOptions.DIRECTION) : node.getParent().getProperty(CoreOptions.DIRECTION);
boolean fixedPorts = node.getProperty(CoreOptions.PORT_CONSTRAINTS) == PortConstraints.FIXED_POS;
for (ElkPort port : node.getPorts()) {
PortSide portSide = port.getProperty(CoreOptions.PORT_SIDE);
if (portSide == PortSide.UNDEFINED) {
portSide = calcPortSide(port, direction);
port.setProperty(CoreOptions.PORT_SIDE, portSide);
}
switch(portSide) {
case NORTH:
if (!fixedPorts) {
port.setX(port.getX() * widthRatio);
}
break;
case EAST:
port.setX(port.getX() + widthDiff);
if (!fixedPorts) {
port.setY(port.getY() * heightRatio);
}
break;
case SOUTH:
if (!fixedPorts) {
port.setX(port.getX() * widthRatio);
}
port.setY(port.getY() + heightDiff);
break;
case WEST:
if (!fixedPorts) {
port.setY(port.getY() * heightRatio);
}
break;
}
}
}
// resize the node AFTER ports have been placed, since calcPortSide needs the old size
node.setDimensions(newSize.x, newSize.y);
// update label positions
if (moveLabels) {
for (ElkLabel label : node.getLabels()) {
double midx = label.getX() + label.getWidth() / 2;
double midy = label.getY() + label.getHeight() / 2;
double widthPercent = midx / oldSize.x;
double heightPercent = midy / oldSize.y;
if (widthPercent + heightPercent >= 1) {
if (widthPercent - heightPercent > 0 && midy >= 0) {
// label is on the right
label.setX(label.getX() + widthDiff);
label.setY(label.getY() + heightDiff * heightPercent);
} else if (widthPercent - heightPercent < 0 && midx >= 0) {
// label is on the bottom
label.setX(label.getX() + widthDiff * widthPercent);
label.setY(label.getY() + heightDiff);
}
}
}
}
// set fixed size option for the node: now the size is assumed to stay as determined here
node.setProperty(CoreOptions.NODE_SIZE_CONSTRAINTS, SizeConstraint.fixed());
return new KVector(widthRatio, heightRatio);
}
Aggregations