private GridLocationMap<V, E> computeGridLocationMap(GDirectedGraph<V, E> g,
			GDirectedGraph<V, E> tree, V v) {

		Collection<E> edges = tree.getOutEdges(v);

		if (edges.isEmpty()) {
			GridLocationMap<V, E> grid = new GridLocationMap<>(v, 1, 1);
			return grid;
		}

		// get all child grids and merge them side by side

		List<E> sortedEdges = new ArrayList<>(edges);
		sortedEdges.sort(edgeComparator);
		E edge = sortedEdges.get(0);
		V child = edge.getEnd();
		int totalEdges = sortedEdges.size();

		GridLocationMap<V, E> childGrid = computeGridLocationMap(g, tree, child);
		int leftChildRootCol = childGrid.getRootColumn();
		int rightChildRootCol = leftChildRootCol;
		for (int i = 1; i < totalEdges; i++) {
			edge = sortedEdges.get(i);
			child = edge.getEnd();
			GridLocationMap<V, E> nextGrid = computeGridLocationMap(g, tree, child);
			int shift = merge(childGrid, nextGrid, i, totalEdges);
			rightChildRootCol = nextGrid.getRootColumn() + shift;
		}
		int rootCol = (leftChildRootCol + rightChildRootCol) / 2;
		if (leftAligned) {
			rootCol = 1;
		}
		GridLocationMap<V, E> grid = new GridLocationMap<>(v, 1, rootCol);
		grid.add(childGrid, 2, 0);  // move child grid down 2: 1 for new root, 1 for edge row

		return grid;
	}
Creates a GridLocationMap for the subtree rooted at the given vertex. It does this by
recursively getting grid maps for each of its children and then merging them together
side by side.
@param g the original graph
@param tree the graph after edge removal to convert it into a tree
@param v the root of the subtree to get a grid map for
@return a GridLocationMap with the given vertex and all of its children position in the 
grid.
Ghidra/Features/FunctionGraph/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/flowchart/AbstractFlowChartLayout.java