Editing a path diagram with the mouse

Feng Ji

This example uses a three-factor CFA to introduce the lvmPlot editor. We will move a factor and its indicators, reposition a loading label, and save the edited diagram. All of the positioning is done with the mouse. The coefficient values still come from the fitted model.

The screenshots were taken from the local lvmPlot 0.1.1 development build, using the example below. The numbered circles and dashed black arrows are tutorial annotations, not editor controls. Each figure has an expandable, unannotated screenshot if you need to read a small button or label.

Find your way around the editor

The large area on the right is the drawing surface. The left sidebar contains selection, alignment, appearance, and file controls. The sidebar scrolls separately from the drawing surface. The download buttons are near its bottom; they are not missing when only the upper controls are visible.

Figure 1. The editor before any changes. The download controls are farther down the left sidebar.
Figure 1. The editor before any changes. The download controls are farther down the left sidebar.
Show the unannotated full-size screenshot
Unannotated editor screenshot: Figure 1. The editor before any changes. The download controls are farther down the left sidebar.
Number Where to start What you can do there
1 Edit tab Drag the diagram here. Export preview, next to it, shows the R-rendered result.
2 The visual ellipse Press inside the node and move it while holding the left mouse button.
3 A coefficient such as 0.42 Press on the number or its white background to move only that label.
4 Selected nodes See which nodes are selected. The controls below act on that selection.

1. Open the example

This walkthrough keeps the same CFA throughout. The general vignette covers scripted layouts and other inputs.

Install the packages used below if you do not already have them:

install.packages(c("lvmPlot", "lavaan", "shiny", "jsonlite", "svglite", "ragg"))

If you have a newer .tar.gz archive from the maintainer, install it after these dependencies. The export preview and snapshot behavior described here require version 0.1.1 or later:

install.packages(file.choose(), repos = NULL, type = "source")
packageVersion("lvmPlot")

Restart R if you replaced a version that was already loaded. Then run this example in the console. The data are included with lavaan.

library(lavaan)
library(lvmPlot)

model <- '
  visual  =~ x1 + x2 + x3
  textual =~ x4 + x5 + x6
  speed   =~ x7 + x8 + x9
'

fit <- cfa(model, data = HolzingerSwineford1939)

lvmPlot(
  fit,
  mode = "edit",
  label = "std",
  stars = FALSE,
  diagram = "all",
  export_name = "three-factor-cfa",
  launch = TRUE
)

The browser opens a local Shiny application. Leave R running while you use it. You should see three factor ellipses and nine indicator rectangles, with loadings and factor covariances. The call uses label = "std" to display standardized coefficients. With the default automatic label setting, the estimates may be hidden, leaving no numerical labels to drag.

2. Drag a node

  1. Stay in Edit. Put the pointer inside the visual ellipse, not on a number beside an arrow.
  2. Hold the left mouse button and move upward, slightly to the left.
  3. Release the button. The factor stays at its new location and the attached arrows follow it.
Figure 2a. Before: press inside visual and drag upward in the direction shown.
Figure 2a. Before: press inside visual and drag upward in the direction shown.
Show the unannotated full-size screenshot
Unannotated editor screenshot: Figure 2a. Before: press inside visual and drag upward in the direction shown.
Figure 2b. After: visual has moved, while x1, x2 and x3 remain on the original row.
Figure 2b. After: visual has moved, while x1, x2 and x3 remain on the original row.
Show the unannotated full-size screenshot
Unannotated editor screenshot: Figure 2b. After: visual has moved, while x1, x2 and x3 remain on the original row.

The blue outline means that visual is selected. It is an editing highlight, not a change to the model or an element of the exported figure. You can move an indicator rectangle in the same way. For the next step, leave the indicators where they are so that your diagram resembles the screenshots.

If a node refuses to move, check whether it is locked. Unlock all releases the locked nodes. If it moves in larger steps than you want, turn off Snap to grid. You can also focus the canvas and use the arrow keys to nudge selected nodes; Shift plus an arrow gives a larger step.

3. Drag the coefficient, not the node

Find the loading from visual to x2. Its standardized value is approximately 0.42 in this example.

  1. Put the pointer directly on 0.42 or its white label background.
  2. Hold the left mouse button and move the number upward, a little to the right.
  3. Release it. Check that the nodes stayed put and the displayed value is still 0.42.
Figure 3a. Before: the numbered pointer identifies the label to grab. Drag the number itself, not its path.
Figure 3a. Before: the numbered pointer identifies the label to grab. Drag the number itself, not its path.
Show the unannotated full-size screenshot
Unannotated editor screenshot: Figure 3a. Before: the numbered pointer identifies the label to grab. Drag the number itself, not its path.
Figure 3b. After: 0.42 is higher on the page. The factor and all three indicators have stayed in place.
Figure 3b. After: 0.42 is higher on the page. The factor and all three indicators have stayed in place.
Show the unannotated full-size screenshot
Unannotated editor screenshot: Figure 3b. After: 0.42 is higher on the page. The factor and all three indicators have stayed in place.

Keep the number close enough to its path that it cannot be mistaken for an estimate on a nearby arrow. An overlap can sometimes be fixed by a small move; placing the label much farther away may make the diagram harder to read.

Double-click the coefficient label to return it to automatic placement. Try that once, then place it by hand again. You can leave most coefficients in their automatic positions and move just the few that need more room.

A manually placed label stays at its graph coordinates. If you move visual or x2 again, the 0.42 may need another adjustment. This is why it is usually easier to arrange the nodes first and place individual coefficients afterward. Dragging does not change the loading estimate or refit the model.

4. Change a displayed name

Select the visual ellipse again. Enter Visual ability in Selected label and click Apply label. The longer name appears in the diagram, but the model variable remains visual. Layouts and parameter tables still use that original name.

Figure 4. Select the node, type its display name in Selected label (1), then click Apply label (2). The diagram now says Visual ability.
Figure 4. Select the node, type its display name in Selected label (1), then click Apply label (2). The diagram now says Visual ability.
Show the unannotated full-size screenshot
Unannotated editor screenshot: Figure 4. Select the node, type its display name in Selected label (1), then click Apply label (2). The diagram now says Visual ability.

Undo and redo restore node positions, display labels, and coefficient-label positions. They do not track every style control. Use undo for a single edit; Reset layout resets the arrangement and also clears locks.

5. Look at what will be exported

Open Export preview and click Refresh export preview. Check the visual node and the loading you moved. The factor should have its new name and position, and the 0.42 should appear beside the arrow where you placed it. If it still looks crowded, return to Edit, make a small adjustment, and refresh again.

Figure 5. Choose Export preview (1), then Refresh export preview (2). In this R-rendered image, the renamed factor is higher (3), and the manually positioned 0.42 remains near its path (4).
Figure 5. Choose Export preview (1), then Refresh export preview (2). In this R-rendered image, the renamed factor is higher (3), and the manually positioned 0.42 remains near its path (4).
Show the unannotated full-size screenshot
Unannotated editor screenshot: Figure 5. Choose Export preview (1), then Refresh export preview (2). In this R-rendered image, the renamed factor is higher (3), and the manually positioned 0.42 remains near its path (4).

This preview is drawn by R rather than by the editing canvas. The two renderers can differ in font widths, node sizes, and line routing. Those differences are more noticeable with long labels or small output figures, so the export preview is the useful view for judging the result. It updates when you request a refresh, not continuously while you drag.

The preview image is raster; PDF and SVG downloads remain vector graphics. Selection highlights, the editing grid, and lock indicators do not appear in the files. Background and Grid controls affect the editing canvas, while standard exports have a white background.

6. Save both the figure and the editable arrangement

Put the pointer over the left sidebar and scroll down until you see SVG, PDF, and PNG. Scrolling over the diagram is not the same operation. The screenshot below shows the bottom of the sidebar.

Figure 6. The bottom of the sidebar: image downloads (1), Figure R (2), State JSON (3), and Browse under Load state JSON (4).
Figure 6. The bottom of the sidebar: image downloads (1), Figure R (2), State JSON (3), and Browse under Load state JSON (4).
Show the unannotated full-size screenshot
Unannotated editor screenshot: Figure 6. The bottom of the sidebar: image downloads (1), Figure R (2), State JSON (3), and Browse under Load state JSON (4).
If you need to… Use this control What to keep
Put the edited figure in a paper or presentation PDF, SVG, or PNG (1) The rendered artwork. PDF and SVG are vector formats.
Reproduce the saved figure from R Figure R (2) A script containing the graph snapshot, positions, and rendering code.
Continue dragging this arrangement later State JSON (3) The editor state; keep the fitted model separately.
Reopen that arrangement Browse… under Load state JSON (4) Choose the previously downloaded State JSON in a compatible editor session.

Download a PDF, SVG, or PNG from the editor. The download waits for the current browser edits to reach R before preparing the file. If the R session has stopped or synchronization fails, the editor reports the problem rather than downloading an older diagram. Restore the connection and retry.

A point that is easy to miss: fit itself has not changed. If you stop Shiny and call plot_lvm(fit), R draws another diagram from the original fitted object. That call cannot retrieve the positions you chose in the browser.

Save State JSON before closing the editor if you want to continue working on the arrangement. It records node positions and display labels, manual coefficient positions, styling, selection, and locks, along with display settings such as label type and decimal places. It also records information used to check that the state belongs to a compatible diagram.

Figure R is useful for a different reason. It contains the saved graph and its estimates, together with code to reproduce the figure. For a manuscript, keep this script and the State JSON alongside the artwork. Layout CSV and Layout R are narrower exports for transferring node coordinates; they are not substitutes for the complete editor state.

7. Return to the figure later

To resume editing, rerun the example with the same model and diagram settings, then select the saved file under Load state JSON. You can also save fit with saveRDS() and load it again instead of repeating the estimation. State JSON does not contain the observations or the fitted lavaan object.

Node names and paths must be compatible. Changing a display label from visual to Visual ability is fine; renaming the model variable or adding a path can make an old state file incompatible. A structural-only view may also have different nodes and paths from the full diagram.

If you refit the same model to updated data, compatible state can reuse your arrangement with the new coefficients. Check the labels again if those numbers take up more space. The old Figure R script still contains the estimates from when it was downloaded, not the new fit.

A short way to check your saving routine is to move a node somewhere obvious, save State JSON, and reopen the same model in another editing session. Load that JSON using control 4 in Figure 6. The deliberately moved node should return to its saved position. Do this before spending a long time on a layout.

Run that script when you need to reproduce the saved figure exactly as a graph snapshot. For the export name used above:

# Run in the directory where you want the output figures.
source("three-factor-cfa-figure.R")

With lvmPlot installed, the script can run in a fresh R session without the original fit. It reconstructs the graph, applies the saved positions and style, and writes PDF, SVG, and PNG files in the working directory. Rendering may still depend on the fonts available to the graphics device.

Keep the statistical analysis separately so that you can refit the model if needed. When you finish an editing session, use RStudio’s Stop button or press Esc in the R console. Closing the browser tab alone may leave Shiny running.

Working with several nodes

After the single-node example, try selecting a small group. Click x1, hold Shift, and click x2 and x3. All three rectangles should have a blue outline. Scroll within the sidebar to find the alignment controls.

Figure 7. The three indicators are selected (1). Align row (2) puts selected, unlocked nodes on one horizontal line; Distribute X (3) spaces them evenly. These indicators already form a row, so Align row makes no visible change here.
Figure 7. The three indicators are selected (1). Align row (2) puts selected, unlocked nodes on one horizontal line; Distribute X (3) spaces them evenly. These indicators already form a row, so Align row makes no visible change here.
Show the unannotated full-size screenshot
Unannotated editor screenshot: Figure 7. The three indicators are selected (1). Align row (2) puts selected, unlocked nodes on one horizontal line; Distribute X (3) spaces them evenly. These indicators already form a row, so Align row makes no visible change here.

Align row uses the average vertical coordinate of the selected, unlocked nodes. Distribute X needs at least three such nodes and uses the outermost horizontal positions. A locked node is excluded, rather than used as an alignment anchor. If only one node changes, look at the selection before repeating the command.

To protect a finished group, scroll back to the top of the sidebar and click Lock. Unlock all releases locked nodes. Fit view recenters the canvas if the diagram has moved out of view. Arrange the nodes before placing individual coefficients; later node moves can leave manually placed numbers behind at their saved graph coordinates.

When the result is not what you expected

What you see What to try
There are no numbers to drag Use label = "std" in this CFA example, or choose a coefficient display under Edge labels.
A node will not move Check whether it is locked. Use Unlock all, then try again inside the node.
A node moves in jumps Clear Snap to grid for finer mouse placement.
A coefficient is in the wrong place Double-click that number to restore automatic placement, or drag it again.
The canvas moved out of view Scroll to Fit view in the sidebar.
You cannot find the save buttons Scroll inside the left sidebar, as shown in Figure 6.
Export preview still shows an earlier edit Click Refresh export preview after the change. The preview is not continuously refreshed.
A download cannot be prepared Keep the R session running, restore the connection if needed, and click the export button again.
plot_lvm(fit) shows the original arrangement That call does not contain your browser edits. Use the downloaded Figure R, or reopen State JSON in the editor.

For a figure you intend to keep, save the artwork, State JSON, and Figure R before closing Shiny. Those files serve different purposes; a PDF alone does not preserve the editable arrangement.