Class LayoutUtil
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic enumThe layouts that can serve as the default layout: the ones that need nothing but the graph.static final classLays out a graph by linearly summing repulsive force between all nodes and attractive force between adjacent nodes.static final classLays out a graph by placing springs between the nodes and letting the system settle (one node at a time).static interfaceCallback interface for reporting layout progress.static interfaceSupplies the rendered box size of each node, so a layout can space nodes by what they actually take up on screen.static classNo-op listener for headless / non-GUI use.static classSwing adapter that wraps aProgressMonitor. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionstatic voidarrangeByLayout(Graph graph, HashMap<String, PointXy> layout) arrangeByLayout.static booleanarrangeBySourceGraph(Graph resultGraph, Graph sourceGraph) Arranges the nodes in the result graph according to their positions in the source graph.static voidcircleLayout(Graph graph) Arranges the nodes in the graph in a circle, using an estimate of each node's rendered size.static voidcircleLayout(Graph graph, LayoutUtil.NodeSize size) Arranges the non-latent nodes in a circle just large enough that no two node boxes overlap and, for a circle of a few nodes, an edge between any two of them would be at leastMIN_EDGE_LENGTHlong from box to box (seeMIN_EDGE_LENGTH_MAX_RADIUS), places each latent near its adjacent nodes without overlapping anything, and shifts the result so the bounding box of all node boxes sits at (LAYOUT_MARGIN,LAYOUT_MARGIN) — i.e.static voiddefaultLayout(Graph graph) Arranges the nodes in the graph by the default layout (seesetDefaultLayout(DefaultLayout)), with estimated node sizes.static voiddefaultLayout(Graph graph, LayoutUtil.NodeSize size) Arranges the nodes in the graph by the default layout (seesetDefaultLayout(DefaultLayout)), with the given node sizes for the layouts that use them.static voiddefaultLayoutConditional(Graph graph) Arranges the nodes using the default layout unless they all have positions already.static LayoutUtil.NodeSizeA size estimate for use when no display sizes are available: about 8 px per character of the name plus padding, with the same 60 px floor and ~30 px height the workbench's measured node boxes use.static voidfruchtermanReingoldLayout(Graph graph) fruchtermanReingoldLayout.static voidfruchtermanReingoldLayout(Graph graph, double naturalEdgeLength) Fruchterman-Reingold with a chosen natural edge length: the distance at which the textbook pull along an edge and push between its two endpoints balance.static LayoutUtil.DefaultLayoutReturns the layout used for graphs that have not been laid out.static booleanisLaggedGraph(Graph graph) Returns true if the graph is a lagged (time-series) graph, i.e., at least one node has a positive lag index in its name.static voidkamadaKawaiLayout(Graph graph, boolean randomlyInitialized, double naturalEdgeLength, double springConstant, double stopEnergy) kamadaKawaiLayout.static voidkamadaKawaiLayout(Graph graph, boolean randomlyInitialized, double naturalEdgeLength, double springConstant, double stopEnergy, LayoutUtil.LayoutProgressListener listener) Overload that accepts an optional progress listener.static intReturns the lag index encoded in a lagged variable name of the formbase:k(as produced by TsUtils.createLagData), or 0 if the name carries no integer lag suffix.static voidlayoutByCausalOrder(Graph graph) Lays the graph out in rows by causal order, using an estimate of each node's rendered size.static voidlayoutByCausalOrder(Graph graph, LayoutUtil.NodeSize size) Lays the graph out in rows by causal order: the nodes with no parents in the top row, then the nodes all of whose parents are above, and so on down.static voidlayoutByKnowledgeIndices(Graph graph) Arranges the nodes of the given graph in tiers based on numerical indices derived from the node names, and positions them accordingly in a 2D layout.static voidlayoutByKnowledgeTiers(Graph graph, Knowledge knowledge) Arranges the nodes of the given graph in tiers based on their grouping as defined in the provided knowledge object.static voidlayoutInCenteredRows(List<List<Node>> tiers, LayoutUtil.NodeSize size) Places the given rows of nodes one under another, top row first, each row centered on the widest row.orderRowsToReduceCrossings(Graph graph, List<List<Node>> rows) Orders the nodes within each of the given rows to reduce the crossings of the edges between rows, by the ordering step of Richard's layout (seeorderTiers(Graph, List, List)).static voidrepositionLatents(Graph graph) Repositions latent nodes in the given graph based on their non-latent neighbors.static voidrepositionLatents(Graph graph, LayoutUtil.NodeSize size) Places each latent near its adjacent nodes without overlapping any other node's box.static voidrichardsLayout(Graph graph) Arranges the graph the way Richard lays out a graph for presentation: layered by causal depth, with each layer shifted a bit further right than the one above it, so that the flow reads down and to the right, and with within-layer orderings chosen by barycenter sweeps to reduce edge crossings.static voidrichardsLayout(Graph graph, LayoutUtil.NodeSize size) Richard's layout with the given node sizes and default spacing: a 30 px horizontal gap between node boxes, 90 px between layer centerlines, and a 50 px rightward shear per layer.static voidrichardsLayout(Graph graph, LayoutUtil.NodeSize size, double xGap, double yGap, double shearPerLayer) Richard's layout, fully parameterized.static voidrichardsLayout(Graph graph, LayoutUtil.NodeSize size, double xGap, double yGap, double shearPerLayer, boolean nudge) Richard's layout, with the nudge pass switchable.static voidSets the layout used for graphs that have not been laid out, for this process.static voidsquareLayout(Graph graph) squareLayout.
-
Constructor Details
-
LayoutUtil
public LayoutUtil()Constructor.
-
-
Method Details
-
estimatedNodeSize
A size estimate for use when no display sizes are available: about 8 px per character of the name plus padding, with the same 60 px floor and ~30 px height the workbench's measured node boxes use.- Returns:
- the estimate
-
layoutByKnowledgeTiers
Arranges the nodes of the given graph in tiers based on their grouping as defined in the provided knowledge object. Nodes not assigned to any tier are arranged first, followed by nodes in each tier, with both groups laid out in a 2D space with horizontal and vertical spacing. Latent nodes are repositioned after arranging other nodes.- Parameters:
graph- the graph whose nodes are to be arranged and positioned.knowledge- the knowledge object containing tier information and ungrouped variables for the graph nodes.
-
lagIndex
Returns the lag index encoded in a lagged variable name of the formbase:k(as produced by TsUtils.createLagData), or 0 if the name carries no integer lag suffix. Names whose suffix after the last colon is not a non-negative integer are treated as unlagged rather than throwing.- Parameters:
name- the variable name.- Returns:
- the lag index, or 0.
-
isLaggedGraph
Returns true if the graph is a lagged (time-series) graph, i.e., at least one node has a positive lag index in its name. Such graphs should be laid out bylayoutByKnowledgeIndices(Graph), with the current time slice in the bottom row and each earlier lag in a row above, regardless of any knowledge tiers.- Parameters:
graph- the graph.- Returns:
- true if some node name has a lag suffix
:kwith k > 0.
-
layoutByKnowledgeIndices
Arranges the nodes of the given graph in tiers based on numerical indices derived from the node names, and positions them accordingly in a 2D layout. The method processes the nodes to determine their tier, sorts them, and spaces them in both horizontal and vertical directions. Latent nodes are repositioned after arranging all other nodes.- Parameters:
graph- the graph whose nodes are to be arranged and positioned.
-
kamadaKawaiLayout
public static void kamadaKawaiLayout(Graph graph, boolean randomlyInitialized, double naturalEdgeLength, double springConstant, double stopEnergy) kamadaKawaiLayout.
- Parameters:
graph- aGraphobjectrandomlyInitialized- a booleannaturalEdgeLength- a doublespringConstant- a doublestopEnergy- a double
-
kamadaKawaiLayout
public static void kamadaKawaiLayout(Graph graph, boolean randomlyInitialized, double naturalEdgeLength, double springConstant, double stopEnergy, LayoutUtil.LayoutProgressListener listener) Overload that accepts an optional progress listener.- Parameters:
graph- aGraphobjectrandomlyInitialized- a booleannaturalEdgeLength- a doublespringConstant- a doublestopEnergy- a doublelistener- aLayoutUtil.LayoutProgressListener, or null for Swing default
-
fruchtermanReingoldLayout
-
fruchtermanReingoldLayout
Fruchterman-Reingold with a chosen natural edge length: the distance at which the textbook pull along an edge and push between its two endpoints balance. Because hubs here repel in proportion to the square root of their degree and overlapping node boxes are separated afterwards, edges settle to about twice this value in practice (a natural length of 80 gives mean edge lengths of 150 to 180 pixels on measurement models). Zero or less means automatic, 20 + 20 times the average degree; 40 to 60 keeps a latent measurement model compact.- Parameters:
graph- the graph.naturalEdgeLength- the natural edge length in pixels, or 0 for automatic.
-
arrangeByLayout
-
getDefaultLayout
Returns the layout used for graphs that have not been laid out.- Returns:
- the default layout.
-
setDefaultLayout
Sets the layout used for graphs that have not been laid out, for this process. It starts as Richard's layout.- Parameters:
layout- the default layout; null for Richard's layout.
-
defaultLayoutConditional
Arranges the nodes using the default layout unless they all have positions already.- Parameters:
graph- the graph to be arranged.
-
defaultLayout
Arranges the nodes in the graph by the default layout (seesetDefaultLayout(DefaultLayout)), with estimated node sizes.- Parameters:
graph- the graph to be arranged.
-
defaultLayout
Arranges the nodes in the graph by the default layout (seesetDefaultLayout(DefaultLayout)), with the given node sizes for the layouts that use them.- Parameters:
graph- the graph to be arranged.size- supplies the rendered box size of each node.
-
circleLayout
Arranges the nodes in the graph in a circle, using an estimate of each node's rendered size. SeecircleLayout(Graph, NodeSize).- Parameters:
graph- the graph to be arranged.
-
circleLayout
Arranges the non-latent nodes in a circle just large enough that no two node boxes overlap and, for a circle of a few nodes, an edge between any two of them would be at leastMIN_EDGE_LENGTHlong from box to box (seeMIN_EDGE_LENGTH_MAX_RADIUS), places each latent near its adjacent nodes without overlapping anything, and shifts the result so the bounding box of all node boxes sits at (LAYOUT_MARGIN,LAYOUT_MARGIN) — i.e. comfortably in the top-left of the canvas.The radius is chosen from the node boxes: two boxes at angles a and b on a circle of radius r are separated by r|cos a − cos b| horizontally and r|sin a − sin b| vertically, and are apart if either separation exceeds the corresponding box extent plus the gap. The smallest such r is computed for every pair (not only neighbors — with wide labels and few nodes, the node across the circle is the binding one), and the radius is the largest of those.
The edge between two nodes runs along the line between their centers, which are r times the unit-circle distance d between their angles apart, and the part of it inside a w by h box is the lesser of (w / 2) / |ux| and (h / 2) / |uy| for the line's unit direction (ux, uy). The direction does not depend on r, so the smallest r that leaves the edge the minimum length is (minimum + the two parts inside the boxes) / d.
- Parameters:
graph- the graph to be arranged.size- supplies each node's rendered box size.
-
squareLayout
-
layoutByCausalOrder
Lays the graph out in rows by causal order, using an estimate of each node's rendered size. SeelayoutByCausalOrder(Graph, NodeSize).- Parameters:
graph- aGraphobject
-
layoutByCausalOrder
Lays the graph out in rows by causal order: the nodes with no parents in the top row, then the nodes all of whose parents are above, and so on down. The nodes within each row are ordered to reduce edge crossings, by the ordering step of Richard's layout. Each row is centered on the widest row. (The rows used to all start at the left margin, so that a row of one or two nodes sat at the far left above or below a wide row, and the edges between them ran at a long slant.)The nodes of a row are spaced by their own box widths, with
CAUSAL_ORDER_X_GAPbetween neighbors, and the rows by the tallest box, withCAUSAL_ORDER_Y_GAPbetween rows. (Every node used to be allowed 80 pixels whatever its label, so that nodes with long names overlapped.) For boxes of the default size, 60 by 30, the spacing is what it was.- Parameters:
graph- aGraphobjectsize- supplies each node's rendered box size.
-
layoutInCenteredRows
Places the given rows of nodes one under another, top row first, each row centered on the widest row. The nodes of a row are spaced by their own box widths, withCAUSAL_ORDER_X_GAPbetween neighbors, and the rows by the tallest box of all, withCAUSAL_ORDER_Y_GAPbetween rows; an empty row takes no space. This is the arrangement of the causal order layout, and of any other layout that sorts the nodes into rows (the workbench's distance-from-selected layout, for one).- Parameters:
tiers- the rows, top row first; nodes not in any row are not moved.size- supplies each node's rendered box size.
-
richardsLayout
Arranges the graph the way Richard lays out a graph for presentation: layered by causal depth, with each layer shifted a bit further right than the one above it, so that the flow reads down and to the right, and with within-layer orderings chosen by barycenter sweeps to reduce edge crossings. Node sizes are estimated; seerichardsLayout(Graph, NodeSize)to supply real display sizes.- Parameters:
graph- the graph to be arranged.
-
richardsLayout
Richard's layout with the given node sizes and default spacing: a 30 px horizontal gap between node boxes, 90 px between layer centerlines, and a 50 px rightward shear per layer.- Parameters:
graph- the graph to be arranged.size- supplies the rendered box size of each node.
-
richardsLayout
public static void richardsLayout(Graph graph, LayoutUtil.NodeSize size, double xGap, double yGap, double shearPerLayer) Richard's layout, fully parameterized.The method is deterministic: the same graph in the same node order gives the same layout every time. It proceeds in four steps:
- Layering. Nodes are assigned to layers by causal depth using the directed structure only (each node goes in the first layer where all of the nodes with arrows into it are already placed). If the directed structure has cycles, one edge per cycle found by a depth-first search is set aside for the layering, so those edges, and only those, are drawn pointing up; everything downstream of a cycle is still layered by depth. For PAGs this means an edge like x o-> y places y below x, which matches the "y is not an ancestor of x" reading of the arrowhead.
- Crossing reduction. Alternating down and up barycenter sweeps reorder each layer by the mean position of its neighbors in the adjacent layer, and after each sweep every layer is walked and any two adjacent nodes whose exchange reduces the crossing count are exchanged, until no exchange helps (the transpose step of Gansner et al.), which removes the crossings the barycenter sort alone leaves behind. The barycenter of a node is the mean position of its neighbors in the sweep direction; all edge types vote here, including edges that span more than one layer. The ordering with the fewest straight-line crossings seen over all sweeps is kept. Crossings are counted here with every layer spread over the same width; after the coordinates are assigned and sheared, neighbors in a layer are exchanged once more wherever that reduces the crossings of the edges as actually placed, which matters for edges that span more than one layer; this is done before and again after the nudge described below.
- Coordinate assignment. Within each layer, nodes are packed by their actual box widths, then pulled toward the mean x of their neighbors over several relaxation passes, preserving the layer order and minimum separation, so children line up under their parents.
- Shear. Each layer is shifted right by
shearPerLayer * layerIndex, so exogenous variables sit at the upper left and downstream variables render down and to the right.
- Parameters:
graph- the graph to be arranged.size- supplies the rendered box size of each node.xGap- horizontal gap kept between node boxes in a layer.yGap- vertical distance between layer centerlines.shearPerLayer- rightward shift added per layer of depth.
-
richardsLayout
public static void richardsLayout(Graph graph, LayoutUtil.NodeSize size, double xGap, double yGap, double shearPerLayer, boolean nudge) Richard's layout, with the nudge pass switchable. Whennudgeis false, the post-processing step that moves nodes sideways off the straight lines of long edges is skipped.- Parameters:
graph- the graph to be arranged.size- supplies the rendered box size of each node.xGap- horizontal gap kept between node boxes in a layer.yGap- vertical distance between layer centerlines.shearPerLayer- rightward shift added per layer of depth.nudge- whether to nudge nodes off long edge lines.
-
orderRowsToReduceCrossings
Orders the nodes within each of the given rows to reduce the crossings of the edges between rows, by the ordering step of Richard's layout (seeorderTiers(Graph, List, List)). Edges within a row are not counted. The given lists are not changed.- Parameters:
graph- the graph; every node in the rows must be one of its nodes.rows- the rows, top first.- Returns:
- the rows, reordered, as new lists.
-
arrangeBySourceGraph
-
repositionLatents
Repositions latent nodes in the given graph based on their non-latent neighbors.- Parameters:
graph- the graph containing the nodes to be repositioned.
-
repositionLatents
Places each latent near its adjacent nodes without overlapping any other node's box. A latent is anchored at the centroid of its measured neighbors (falling back to all neighbors, then the graph center), and a ring search then finds the nearest point whose box clears every already-placed box byNODE_GAP. Ties are broken toward the outside of the graph so the latent floats just off its cluster rather than into the crowded interior. Latents with more measured neighbors are placed first, and each placed latent becomes an obstacle for the rest.- Parameters:
graph- the graph containing the nodes to be repositioned.size- supplies each node's rendered box size.
-