Skip to content

feat: plot() draws arrowheads, parallel edges and loops; clearer option names - #51

Merged
petercorke merged 4 commits into
mainfrom
feat/plot-arrows
Oct 12, 2026
Merged

petercorke merged 4 commits into
mainfrom
feat/plot-arrows

Conversation

@petercorke

Copy link
Copy Markdown
Owner

Summary

plot() now draws a directed graph with arrowheads, and makes parallel edges and loops visible. It also gets option names that say what they are.

What was wrong

plot() drew a straight line from each vertex to each of its neighbours, so:

Case 0.6.5
directed graph identical to an undirected one: no arrowheads
undirected edge drawn twice, once from each end
parallel edges exactly on top of each other: invisible
self-loop a zero-length line: invisible

Drawing

Each edge is drawn once, as a FancyArrowPatch (Graph._edge_layout() decides the geometry):

  • arrowhead at the end vertex of a directed edge, stopping on the rim of the vertex marker (the shrink is in points, so it follows markersize)
  • parallel and antiparallel edges bow to either side as arcs
  • loops are circles at their vertex, sized in points (so independent of axis scaling), rotated and grown for each further loop, and not clipped by the axes
  • 3D has the same, via a small projected-arrow patch that follows a rotation of the view
  • highlight_edge() / highlight_path() follow the arc

Options

0.6.5 1.0
vopt vertex_opt
eopt edge_opt
(new) arrow_opt, default dict(arrowstyle="-|>", mutation_scale=15) for a directed graph
text labels: bool and text_opt: dict
colorcomponents color_components
force2d force_2d
  • The old names still work, with a DeprecationWarning naming the replacement; both an old and its new name is a TypeError; the positional order of the existing parameters is unchanged (the new ones are keyword-only).
  • eopt was passed to Line2D; edge_opt goes to a patch. Common keys carry over (color, linewidth, linestyle, alpha, label); a key that is not an edge option is reported with a UserWarning instead of being silently dropped.
  • The colorcomponents option of export(), dotfile() and the exporters was new in 1.0, never released, so it is renamed outright (second commit).

Behaviour changes

  • Without component colors every vertex and edge is one color; it was each in turn from the matplotlib cycle.
  • Drawing is about 2x slower than the lines it replaces: 3,000 edges plot in 1.2 s and draw in 0.7 s (0.5-0.7 s and 0.2-0.3 s before).

Tests

tests/test_plot_arrows.py (46): edges drawn once; head style and position for directed and undirected; arrow_opt overrides; arcs for parallel and antiparallel edges; loops (visible, headed, distinct, unclipped); edge_opt, vertex_opt, labels, text_opt, component colors, force_2d; every old name (warns, behaves the same, positional order, both-names error); 3D; highlight. Against main, 17 of the 19 drawing tests fail. test_plot_args.py moves to the new names, so the suite emits no deprecation warnings from plot.

Found on the way (not in this PR)

DGraph.nc, and so plot(), component() and samecomponent(), loops forever for many directed graphs: the smallest is a 4-cycle (0->2->1->3->0), and a random 100-vertex digraph hangs. It is in DGraph._graphcolor's label merging, and is the same on 0.6.5. A separate fix is to follow.

RTB: petercorke/robotics-toolbox-python#738 tracks adopting the new names.

🤖 Generated with Claude Code

petercorke and others added 3 commits October 12, 2026 13:10
plot() drew a straight line from each vertex to each neighbour, so a directed
graph looked like an undirected one, an undirected edge was drawn twice,
parallel edges lay exactly on top of each other, and loops were not visible at
all.  Each edge is now drawn once, as an arc patch:

- a directed graph has arrowheads, at the end vertex, stopping on the rim of
  the vertex marker
- edges that join the same pair of vertices, whichever way they point, bow to
  either side, so parallel and antiparallel edges can all be seen
- a loop is a circle at its vertex, sized in points, rotated and grown for the
  next one at that vertex, and not clipped by the axes
- 3D plots have the same, following a rotation of the view
- highlight_edge() and highlight_path() follow the arc

The options are named vertex_opt, edge_opt, arrow_opt, text_opt, labels,
color_components and force_2d.  The 0.6.5 names (vopt, eopt, text,
colorcomponents, force2d) still work, with a DeprecationWarning that names the
replacement; both an old and its new name is a TypeError; the positional
order is unchanged.  text=False is labels=False and a text dict is text_opt.
edge_opt keys that are not edge options (it took Line2D options) are reported
rather than ignored.

Without component colors every vertex and edge is now one color, where it had
been each in turn from the color cycle.

Drawing is about 2x slower than the lines it replaces: 3000 edges plot in
1.2 s and draw in 0.7 s.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
export(), dotfile() and the exporters took colorcomponents, which is the 0.6.5
name of a plot() option and has no underscore.  They are new in 1.0 and not
released, so this is a plain rename, with no deprecation.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 87 complexity · 0 duplication

Metric Results
Complexity 87
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@codecov

codecov Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.41%. Comparing base (5acb105) to head (c57e94b).

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #51      +/-   ##
==========================================
+ Coverage   95.88%   96.41%   +0.53%     
==========================================
  Files           3        3              
  Lines        1238     1339     +101     
==========================================
+ Hits         1187     1291     +104     
+ Misses         51       48       -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@petercorke
petercorke merged commit 3898a89 into main Oct 12, 2026
14 checks passed
@petercorke
petercorke deleted the feat/plot-arrows branch October 12, 2026 03:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant