private void routeAroundColumnVertices(Vertex2d start, Vertex2d end,
			Vertex2dFactory vertex2dFactory, List<Point2D> articulations, double edgeX) {

		if (useSimpleRouting()) {
			return;
		}

		boolean goingDown = true;
		int startRow = start.rowIndex;
		int endRow = end.rowIndex;
		if (startRow > endRow) { // going upwards
			goingDown = false;
			endRow = start.rowIndex;
			startRow = end.rowIndex;
		}

		int startColumn = Math.min(start.columnIndex, end.columnIndex);
		int endColumn = Math.max(start.columnIndex, end.columnIndex);
		if (goingDown) {
			endRow -= 1;
			endColumn -= 1;

			if (start.columnIndex <= end.columnIndex) {
				startRow += 1;
			}
		}
		else {
			// going up we swing out to the right; grab the column that is out to the right
			Column<FGVertex> rightColumn = vertex2dFactory.getColumn(edgeX);
			endColumn = rightColumn.index;
		}

		List<Vertex2d> toCheck = new LinkedList<>();
		for (int row = startRow; row < endRow + 1; row++) {

			for (int col = startColumn; col < endColumn + 1; col++) {

				// assume any other vertex in our column can clip (it will not clip when
				// the 'spacing' above pushes the edge away from this column, like for
				// large row delta values)
				Vertex2d otherVertex = vertex2dFactory.get(row, col);
				if (otherVertex != null) {
					toCheck.add(otherVertex);
				}
			}
		}

		// always process the vertices from the start vertex so that the articulation adjustments
		// are correct
		if (!goingDown) {
			Collections.reverse(toCheck);
		}

		int delta = endRow - startRow;
		for (Vertex2d otherVertex : toCheck) {

			int padding = VERTEX_TO_EDGE_AVOIDANCE_PADDING;
			int distanceSpacing = padding + delta; // adding the delta makes overlap less likely

			// Condensing is when the graph will pull nodes closer together on the x axis to
			// reduce whitespace and make the entire graph easier to see.   In this case, update
			// the offset to avoid running into the moved vertices.

			// Condensing Note: we have guilty knowledge that our parent class my condense the
			// vertices and edges towards the center of the graph after we calculate positions.
			// To prevent the edges from moving to far behind the vertices, we will compensate a
			// bit for that effect using this offset value.   The getEdgeOffset() method is
			// updated for the condense factor.

			int vertexToEdgeOffset = otherVertex.getEdgeOffset();
			int exaggerationFactor = 1;
			if (isCondensedLayout()) {
				exaggerationFactor = 4; // determined by trial-and-error; can be made into an option
			}

			double centerX = otherVertex.getX();
			boolean goingLeft = edgeX < centerX;

			if (!goingDown) {
				// for now, any time an edge goes up, we route it to the right
				goingLeft = false;
			}

			VertexClipper vertexClipper = new VertexClipper(goingLeft, goingDown);

			// no need to check the 'y' value, as the end vertex is above/below this one
			if (vertexClipper.isClippingX(otherVertex, edgeX)) {

				/*
					 Must route around this vertex - new points:
					 -p1 - just above the intersection point
					 -p2 - just past the left edge
					 -p3 - just past the bottom of the vertex
					 -p4 - back at the original x value
				
					 	   |
					   .___|
					   | .-----.
					   | |     |
					   | '-----'
					   '---.
					   	   |
				*/

				// p1 - same x; y just above vertex
				double x = edgeX;
				double y = vertexClipper.getTopOffset(otherVertex, vertexToEdgeOffset);
				articulations.add(new Point2D.Double(x, y));

				// Maybe merge points if they are too close together.  Visually, many lines
				// moving around intersecting vertices looks busy.  When the intersecting
				// vertices are close together, we remove some of the articulations in order to
				// smooth out the edges.
				if (articulations.size() > 2) {

					/*
						The last articulation is the one added before this method was called, which
						lies just below the intersecting vertex.   The articulation before that is
						the one that is the one that is sending the x value straight into the
						intersecting vertex.  Delete that point as well so that the entire edge is
						shifted to the outside of the intersecting vertex.  This will get repeated
						for each vertex that is intersecting.
					*/
					Point2D previousArticulation = articulations.get(articulations.size() - 2);
					int closenessHeight = 50;
					double previousY = previousArticulation.getY();
					if (vertexClipper.isTooCloseY(y, previousY, closenessHeight)) {
						articulations.remove(articulations.size() - 1);
						articulations.remove(articulations.size() - 1);
						Point2D newPrevious = articulations.get(articulations.size() - 1);
						y = newPrevious.getY();
					}
				}

				// p2 - move over; same y
				int offset = Math.max(vertexToEdgeOffset, distanceSpacing);
				offset *= exaggerationFactor;
				x = vertexClipper.getSideOffset(otherVertex, offset);
				articulations.add(new Point2D.Double(x, y));

				// p3 - same x; move y above/below the vertex
				y = vertexClipper.getBottomOffset(otherVertex, vertexToEdgeOffset);
				articulations.add(new Point2D.Double(x, y));

				// p4 - move over back to our original x; same y
				x = edgeX;
				articulations.add(new Point2D.Double(x, y));
			}
		}
	}
An amount by which edges entering a vertex from the left are offset to avoid overlapping
Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java