/** * Computes the Y coordinate so that the EIP is centered with respect to its children. * * @param eip the EIP * @param dim the dimension of the figure associated to the EIP * @return the new Y coordinate, -1 if there is no child */ private int computeCenteredYCoordinate(EipNode eip, Dimension dim) { // Find the minimal and maximal values int childMinY = -1, childMaxY = -1; AbstractNode lastNode = null; for (EipConnection conn : eip.getOutgoingConnections()) { lastNode = conn.getTarget(); int childY = this.nodeToCoY.get(lastNode); childMaxY = childY; if (childMinY == -1) childMinY = childY; } // Put it at the middle... int y = childMaxY + childMinY; // ... but take the figure's height in account. // When there is only 1 child, the EIP and the child should be aligned if (eip.getOutgoingConnections().size() == 1) { Dimension childDim = getFigureSize(lastNode); y -= dim.height - childDim.height; } else if (eip.getOutgoingConnections().size() > 1) { Dimension childDim = getFigureSize(lastNode); y += childDim.height - dim.height; } y = y / 2; return y; }
/** * Computes the level and the coordinates for a node of the sub-graph. * * <p>The algorithm relies on the following predicates: * * <ol> * <li>The X coordinate of a node depends on the level and the width of the previous levels. * <li>The width of a level depends on the width of the biggest figure of this level. * <li>The Y coordinate of a node depends on whether this node is a leaf or an intermediate node * in the tree. * <li>The Y coordinate of a leaf is the highest computed Y + a specific padding + the height of * the preceding figure. * <li>The Y coordinate of an intermediate node is the middle of the max and min Y of the node's * children. * </ol> * * <p>Mathematically, it gives: * * <ol> * <li>X( node ) = X( node-1 ) + padding + Width( node-1 ) * <li>Width( level ) = Math.max( Width( node )) where node is any node in the level / column. * <li>... * <li>Y( node ) = Math.max( Y( node2 )) where node2 is any node that has been processed before * the current node. * <li>Y( node ) = [ Y( node_child_max ) + Y( node_child_min ) - Height( node ) ] / 2 * </ol> * * @param node * @param level the tree level of the node (1 for the root level) * @param maxY the biggest computed Y coordinate (for any column or level) * @return the new biggest computed Y coordinate (can be the same than the received one) */ private int computeNodeStatistics(AbstractNode node, int level, int maxY) { // Register this node this.nodeToLevel.put(node, level); ArrayList<AbstractNode> nodes = this.levelToNodes.get(level); if (nodes == null) nodes = new ArrayList<AbstractNode>(); nodes.add(node); this.levelToNodes.put(level, nodes); // Get the size of this figure Dimension dim = getFigureSize(node); // Update the width of the tree level. // The X coordinate of a node depends on the width for this level. // This is why it can only be computed once the entire tree has been explored. Integer levelWidth = this.levelToWidth.get(level); if (levelWidth == null) levelWidth = -1; if (dim.width > levelWidth) { levelWidth = dim.width; this.levelToWidth.put(level, levelWidth); } // Compute the Y coordinate of the node // This last one depends on the Y coordinates of the children // Note that there are only two sub-classes of AbstractNode int y; if (node instanceof Endpoint) { maxY += GRID_PADDING_Y; y = maxY; maxY += dim.height; } else { // Process all the children for (EipConnection conn : ((EipNode) node).getOutgoingConnections()) maxY = computeNodeStatistics(conn.getTarget(), level + 1, maxY); // Determine the Y coordinate of the current EIP // The parent's location depends on the children y = computeCenteredYCoordinate((EipNode) node, dim); // No child found => treat it as an end-point if (y == -1) { maxY += GRID_PADDING_Y; y = maxY; maxY += dim.height; } } // Store the Y coordinate for a later use this.nodeToCoY.put(node, y); return maxY; }