AffineTransform doCreateArrowTransform(double arrowPlacementTolerance, Shape edgeShape,
			Shape vertexShape) {

		GeneralPath path = new GeneralPath(edgeShape);
		double[] seg = new double[6];
		Point2D p1 = null;
		Point2D p2 = null;
		AffineTransform at = new AffineTransform();

		// Find the line segment whose endpoint is in the end vertex (this handles straight lines
		// and articulated lines).  Use that line's start point to for the arrow position and use
		// the line's angle to rotate the arrow head.
		for (PathIterator i = path.getPathIterator(null, 1); !i.isDone(); i.next()) {
			int type = i.currentSegment(seg);
			if (type == PathIterator.SEG_MOVETO) {
				p2 = new Point2D.Double(seg[0], seg[1]);
			}
			else if (type == PathIterator.SEG_LINETO) {
				p1 = p2;
				p2 = new Point2D.Double(seg[0], seg[1]);
				if (vertexShape.contains(p2)) {
					Line2D lineSegment = new Line2D.Double(p1, p2);
					Line2D line =
						findClosestLineSegment(arrowPlacementTolerance, lineSegment, vertexShape);
					return createArrowTransformFromLine(line);
				}
			}
		}
		return at;
	}
Basic class to calculate the position of an edge arrow

@param <V> the vertex type
@param <E> the edge type
Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/edge/VisualEdgeArrowRenderingSupport.java