Replace static PNG isogeny graphs with interactive Cytoscape.js#6874
Replace static PNG isogeny graphs with interactive Cytoscape.js#6874edgarcosta wants to merge 12 commits intoLMFDB:mainfrom
Conversation
Addresses LMFDB#6686 (excessive whitespace) and LMFDB#2801 (interactive graphs). Static matplotlib-rendered PNGs are replaced with Cytoscape.js graphs featuring clickable nodes (navigate to curve pages), hover tooltips with curve metadata, and optimal curve highlighting. The properties sidebar now uses a compact server-generated SVG. Trivial (single-curve) isogeny classes now display their graph as well.
Compute container dimensions from actual node positions instead of using a fixed 600x400 box. Eliminates excessive vertical whitespace for small/horizontal graphs (e.g. 2-node classes).
For ECNF isogeny classes with topologies not handled by make_graph (e.g. 18 curves), fall back to cytoscape's cose layout instead of placing all nodes at origin. SVG sidebar uses circular layout as fallback. Add 'Number of curves' to ECNF properties sidebar.
- Extract CDN scripts into shared cytoscape_scripts.html template - Add layout dropdown with 18 options (7 built-in + 11 extensions) including cola, dagre, klay, elk variants, fcose, etc. - Default to preset layout when make_graph provides positions, elk(stress) otherwise; hide preset option when not available - Sidebar SVG: use layout_spring() fallback, remove labels, use smaller filled dots for a cleaner compact appearance
|
Many thanks @edgarcosta, this looks awesome!! I've just added a few small comments below, after doing some testing:
But overall, this is a definitely great improvement to what we currently have, thanks again!! :) |
|
These are cool @edgarcosta! Following up on a few of Robin's comments:
As we refine this, it would be nice to see how the subgroup diagrams look in this system. |
|
Thank you for your comments. Hopefully I will try to address them next Friday.
Not planning to address, but feel free to submit a patch if it is important for you:
also here are other demos of cytoscape: https://js.cytoscape.org/ |
|
This looks very good indeed -- thanks, @edgarcosta ! I don't see the need for having individual vertices draggable. For the larger diagrams (such as the last example shown) I would like the ability to rotate the whole diagram in 3D-graphics style -- this is what you get when you create an isogeny class in Sage directly (implemented by me a while ago) but I never worked out how to store a 3D graphics object which we could display in the LMFDB. This is absolutely not essential. I agree that we should be consistent in how much of the labels we use to decorate the graph nodes. In fact only the number at the end of the label is varying across the class so a minimal version would be to just have that (perhaps omitting the single '1' if the class is trivial). Having the pop-up info for the curves in the class is great. |
126dd4d to
ecf753e
Compare
- Adaptive SVG sizing in sidebar to reduce whitespace for small graphs - Shrink square graph coordinates to match double-square spacing - Spread out tent graph (maxdegree=12) to prevent node overlap - Set position for single-vertex graphs; compact container with no layout dropdown - Use short labels (e.g. "a1") for ECNF graph nodes - Add torsion and CM to ECNF node tooltips
ecf753e to
ac9c82e
Compare
lmfdb/static/isogeny_graph.js
Outdated
| tooltip.style.cssText = 'display:none; position:absolute; background:#fff; ' + | ||
| 'border:1px solid #ccc; border-radius:4px; padding:8px 12px; ' + | ||
| 'font-size:13px; line-height:1.5; box-shadow:0 2px 8px rgba(0,0,0,0.15); ' + | ||
| 'z-index:1000; pointer-events:none; max-width:280px;'; |
There was a problem hiding this comment.
I don't think it's necessary to set max-width (or at least, you can set it quite big without too much harm; I would always prefer the data to be on a single line)
lmfdb/static/isogeny_graph.js
Outdated
| 'label': 'data(label)', | ||
| 'font-size': '11px', | ||
| 'text-background-color': '#fff', | ||
| 'text-background-opacity': 0.85, |
There was a problem hiding this comment.
I prefer 'text-background-opacity: 1', so you don't get the ghost lines
lmfdb/static/isogeny_graph.js
Outdated
| 'height': 30, | ||
| 'shape': 'round-rectangle', | ||
| 'color': '#333', | ||
| 'cursor': 'pointer' |
There was a problem hiding this comment.
This line doesn't seem to do anything. Instead, you should change pointer on mouseover events, I'll comment below.
lmfdb/static/isogeny_graph.js
Outdated
| // Position tooltip near the node | ||
| var pos = node.renderedPosition(); | ||
| tooltip.style.left = (pos.x + 15) + 'px'; | ||
| tooltip.style.top = (pos.y - 10) + 'px'; |
There was a problem hiding this comment.
add $('html,body').css('cursor', 'pointer'); here
lmfdb/utils/utilities.py
Outdated
| return "data:image/png;base64," + quote(b64encode(buf)) | ||
|
|
||
|
|
||
| def graph_to_cytoscape_json(G): |
There was a problem hiding this comment.
Perhaps it makes sense to put this in a separate file?
lmfdb/static/isogeny_graph.js
Outdated
| var hw = node.width() / 2; | ||
| var hh = node.height() / 2; |
There was a problem hiding this comment.
add +10 to these to make account for the shadow around the box
- Move tooltip to document.body to prevent clipping by container overflow - Use white-space:nowrap instead of max-width for wider tooltips - Set text-background-opacity to 1 on edge labels to hide ghost lines - Remove ineffective cursor:pointer CSS; use jQuery on mouseover/mouseout - Add +10 margin to drag constraint for box shadow - Format torsion as "Z/2Z x Z/4Z" or "Trivial" instead of raw list
|
There seems to be a bug currently where selecting preset -> selecting different layout -> selecting preset again doesn't restore the preset layout.
|
|
@havarddj where they play a bigger role is in NF e.g. https://olive.lmfdb.xyz/EllipticCurve/4.4.2304.1/1.1/a/ |
Python sets graph_layouts list, passed to JS via templates. cytoscape_scripts.html conditionally loads only the CDN scripts needed for the chosen layouts. Default set: Preset, Elk-stress, Circle, Concentric, Klay, Dagre, Cola.
…ring This ensures the Preset layout option is always available, and the interactive graph starts with the same coordinates as the sidebar SVG.
…itions graph_to_cytoscape_json now returns (elements, has_preset). Python sets graph_default_layout to 'Preset' when positions are hardcoded or 'Elk-stress' when falling back to layout_spring. The default is passed through to JS so the initial layout and dropdown selection match.
Move make_graph, graph_to_cytoscape_json, graph_to_svg, setup_isogeny_graph, and GRAPH_LAYOUTS into a dedicated module, eliminating ~160 lines of near-identical code that was duplicated between elliptic_curves/isog_class.py and ecnf/isog_class.py. The unified make_graph uses ECNF's more defensive guards (checking both maxdegree and n) while keeping EC/Q's vertex_labels parameter.

A draft attempt to resolve excessive whitespace in isogeny graphs: #6686 and #2801
There is some cleanup to do, but here are some samples:
⚫ : https://olive.lmfdb.xyz/EllipticCurve/Q/37/a/ (to be improved, but how?)
*-*: https://olive.lmfdb.xyz/EllipticCurve/Q/26/b/*-*-*: https://olive.lmfdb.xyz/EllipticCurve/Q/11/a/*-*-*-*: https://olive.lmfdb.xyz/EllipticCurve/Q/27/a/T : https://olive.lmfdb.xyz/EllipticCurve/Q/17/a/
🔲 : https://olive.lmfdb.xyz/EllipticCurve/Q/20/a/
🔲 🔲 : https://olive.lmfdb.xyz/EllipticCurve/Q/14/a/
>-<: https://olive.lmfdb.xyz/EllipticCurve/Q/21/a/🐙 : https://olive.lmfdb.xyz/EllipticCurve/Q/15/a/
🐙 : https://olive.lmfdb.xyz/EllipticCurve/Q/30/a/ (something went wrong)
💥 : https://olive.lmfdb.xyz/EllipticCurve/4.4.2304.1/1.1/a/
This branch is being tracked in olive.lmfdb.xyz
@havarddj ready for your comments!