Merge dev into main - #132
Conversation
Implements the corridor routing algorithm that bypasses the cone spanner / visibility graph entirely. The pipeline is: CDT → BFS on dual graph → sleeve → funnel → polyline Key components: - findContainingTriangle: R-tree accelerated triangle lookup - findSleeveBFS: BFS on CDT dual graph avoiding obstacle interiors - funnelFromDiagonals: standalone funnel algorithm - corridorRoute: public API combining all steps Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add Corridor to EdgeRoutingMode enum - Add routeCorridorEdges() that builds CDT on padded obstacles and routes all edges through the CDT dual graph with funnel optimization - Wire into routeEdges() dispatch in driver.ts - Add 'Corridor' option to SVG and WebGL renderer dropdown menus - Export routeCorridorEdges from @msagl/core Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two fixes: 1. Replace BFS with A* using Euclidean distance between triangle centroids as edge weights and straight-line distance to target as heuristic. This produces much shorter paths by selecting better homotopy classes. 2. The obstacle crossing logic (canCrossEdge) remains — it correctly allows crossing source/target obstacle boundaries while blocking other obstacles. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Port locations added as isolated sites (null Owner) inside other nodes' obstacles created 'holes' in the obstacle-interior check, allowing A* to traverse through obstacle triangles. Fix: build CDT with only obstacle polylines, no port sites. findContainingTriangle locates the starting triangle at query time. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The RelativeFloatingPort constructor wasn't assigning ports to edges. Use SplineRouter.CreatePortsIfNeeded which handles this correctly, and add a null guard for edges that still lack ports. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When corridorRoute returns null (no path found), fall back to a straight line between source and target. This ensures every edge gets a curve, preventing the tileMap from crashing on null.parStart. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
SplineRouter.CreatePortsIfNeeded creates RelativeFloatingPort objects but never assigns them to edge.sourcePort/targetPort — they were garbage collected. Now we assign ports directly: ed.sourcePort = RelativeFloatingPort.mk(...) Added integration test on gameofthrones.json (407 nodes, 2639 edges): all edges routed successfully, CDT 8ms + routing 329ms. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bezier smoothing (SmoothedPolyline.createCurve) can push curves outside the corridor and through obstacle nodes. Replace with Polyline.toCurve() which produces exact line segments that stay within the CDT corridor. Also adds diagnostic test confirming 0/2639 edges cross through non-endpoint node boundaries on the gameofthrones graph. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Compute where source→target line exits the source padded obstacle and enters the target. Route through free space between those boundary points. This eliminates sharp turns near node boundaries caused by funnel waypoints on obstacle corners. Arrowhead trimming clips the final center→boundary straight segments at the actual node boundary. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Route center-to-center to find the globally optimal funnel path, then trim obstacle-interior waypoints so edges leave/enter nodes in the actual optimal direction (toward the first/last free-space waypoint). Arrowhead trimming clips the center→free-space segment at the node boundary. Falls back to keeping original obstacle-corner waypoints when trimming would cross a nearby obstacle (0 crossings on GOT graph). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rp corners Instead of trimming waypoints post-funnel, virtually collapse the source/target obstacle boundary vertices to their node centers in sleeveToDiagonals(). The funnel then routes directly from center to center through smooth free-space diagonals — no sharp turns at obstacle corners. Arrowhead trimming clips the curve at node boundaries. Degenerate diagonals (both endpoints collapsed to same point) are skipped, keeping the funnel well-formed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The previous canCrossEdge blocked crossing any constrained edge not belonging to source/target obstacles. This prevented the A* from reaching the target when obstacles' padded boundaries touch or overlap, creating impenetrable walls of constrained edges in free space. Fix: remove the constrained-edge crossing check entirely. The triangleIsInsideObstacle check on neighbor triangles is sufficient to prevent entering obstacle interiors — a triangle is inside an obstacle only when all 3 sites belong to the same non-allowed obstacle. Crossing a constrained boundary edge enters a triangle with at most 2 sites from that obstacle, which is correctly classified as free space. Tested on 40 .gv files (16,597 edges): 0 sleeve failures, 0 null curves. Edge length ratio unchanged (GOT: 1.028, root.gv: 1.027). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
tileMap.buildUpToLevel() was hardcoded to use SplineRouter for rerouting edges on subsets of visible nodes at different zoom levels. This caused 'failed to create sleeve' errors from pathOptimizer.ts when the edge routing mode was Corridor. Now checks the edge routing mode and uses routeCorridorEdges when in Corridor mode, falling back to SplineRouter otherwise. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instead of running A* independently for each edge, group edges by source node and run one Dijkstra from each source triangle. The shortest-path tree reaches all target triangles, allowing sleeve extraction via parent pointers. Target obstacle-interior triangles are reached but not expanded through, preventing paths from crossing through non-target obstacles. GOT benchmark: 156ms (was 224ms with per-edge A*) — 30% speedup. Edge length ratio: 1.035 (was 1.028) — slightly longer paths since Dijkstra has no target heuristic, but still within 3.5% of spline. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Apply modelMatrix coordinate offset in zoomTo() so the canvas correctly centers on the found node in tile-space coordinates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Preprocesses the CDT dual (triangles as nodes, shared edges as arcs) for O(1) shortest-path queries via bidirectional upward Dijkstra. Edge-difference heuristic with lazy priority updates for contraction ordering. Shortcut edges encode the contracted middle node for path unpacking. GOT benchmark (3258 triangles): - Preprocessing: 81ms - 100 queries: 38ms (0.38ms per query) - All queries produce valid sleeves Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace full vertex collapse with constrained movement: each source/ target obstacle vertex moves toward the node center only as far as all adjacent sleeve triangle orientations are preserved. For vertex v moving toward center s, each triangle (v,a,b) gives constraint t < c0/|c1| where c0 = cross(b-a, v-a), c1 = cross(b-a, s-v). Take t* = min of all upper bounds. O(k) per vertex. Results on GOT: 0.7% crossings (was 1.5% with full collapse). Edge length ratio unchanged. Also updates figure dump test to generate combined figures showing original sleeve + diagonals + route in a single image. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…agonals shown Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ged (green solid), no path Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Only changed boundary edges shown: orange dashed (original) and green solid (after legal vertex movement). Unchanged edges hidden. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Prevents tiny sliver edges inside source/target obstacles when two adjacent vertices both move toward the center but don't fully collapse. Also fixes the figure rendering. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…orange Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Set MAX_LEVELS / MAX_TILE_LEVELS to 30 (effectively no cap) in maxPerTile.spec.ts and benchmarkLoadingAllGraphs.spec.ts, and update the report headers / LaTeX caption accordingly. On all benchmark graphs the natural capacity-met stop in TileMap.subdivideLevel fires first (verified by 'done subdividing at level X because each tile contains less than 500' in every run); the cap was never binding. Per-graph levels and per-tile maxima are bit-identical to the previous Z=8 runs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mizer The benchmark runs each graph through layoutGraphWithMds twice on fresh geometry, once with EdgeRoutingMode.Sleeve and once with EdgeRoutingMode.Spline, and reports the wall-clock time and the sum of edge curve lengths for each router. Disabled by default; opt in with MSAGL_BENCH=1. While bringing it up, the spline router crashed on Game of Thrones with 'Cannot read properties of undefined (reading containsPoint)'. Two distinct null-triangle cases existed in PathOptimizer: 1. findChannelTriangles fed res.containsEnd back as the next start triangle without checking for null. getAllCrossedTriangles is a best-effort BFS that can leave containsEnd null on degenerate input (collinear points, points exactly on triangle boundaries). The fix relocates the next start triangle via the CDT's triangle R-tree (getRectangleNodeOnTriangles + PointIsInsideOfTriangle) when the BFS doesn't deliver one. getAllCrossedTriangles itself now returns an empty result on null input instead of throwing. 2. findSourceTriangle returns undefined when no triangle of the local CDT contains poly.start. The caller in run() now treats that as a 'failed to create sleeve' case and bails out, mirroring the existing handling for getSleeve returning null. Both code paths are degeneracy fallbacks: this.poly is left unchanged and the spline router falls back to the unrefined polyline for that edge, instead of aborting the whole routing call. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A stripped-down WebGL demo of @msagl that always uses sleeve routing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…deploy script Sample dropdown previously fetched two JSON graphs cross-origin from raw.githubusercontent.com. To support the anonymous review demo at https://gjs-sleeve.surge.sh, the example now serves all nine paper graphs from its own deployment with no cross-origin dependency: * settings.ts: replace SAMPLE_GRAPHS with all 9 graphs from the paper benchmark (gameofthrones, composers, ca-GrQc, facebook_combined, ca-HepTh, delaunay_n15, ca-HepPh, ca-CondMat, deezer_europe), each pointing at a relative ./graphs/* URL. Add a per-graph etaSec estimate and a SLOW_GRAPH_ETA_SEC = 30 threshold. * app.ts: when the user picks a graph whose etaSec is at or above the threshold, window.confirm() warns 'loading might take a long time' before fetching; cancel resets the dropdown. * graphs/: stage the nine graphs (large edge-list / mtx / csv files pre-gzipped; the parser auto-gunzips via DecompressionStream). Total payload ~3.6 MB. .gitignore: scope the 'graphs' ignore to repo root so the example's per-graph data is tracked. * package.json: build script now copies examples/webgl-sleeve/graphs/ into website/static/webgl-sleeve/graphs/ so the surge publish dir is self-contained. * deploy-webgl-sleeve.sh (new, at repo root): build the example then 'surge website/static/webgl-sleeve gjs-sleeve.surge.sh'. Verifies a surge identity (cached login or SURGE_LOGIN/SURGE_TOKEN env vars). Supports --skip-build for republishing already-built assets. * website/static/webgl-sleeve/app.js: rebuilt artifact. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Renderer: new private _maxTileLevels (default 8) plus setMaxTileLevels /
maxTileLevels accessors; _update() forwards to TileMap.buildUpToLevel
instead of the hardcoded 8.
- webgl-sleeve example: ?maxLevels= URL param plumbs into the renderer;
?graph= alongside ?url= for deep-linking by SAMPLE_GRAPHS label;
window.__msaglReady / __msaglRenderer exposed for the harness.
- New bench/smoothness.mjs Puppeteer harness: serves the bundled demo
on a random port, runs (graph × {pyramid, singleLayer} × trials),
captures rAF frame times, PerformanceObserver long tasks, and a
Tileset2D probe that sums visible-tile element counts at peak zoom.
Writes JSONL. ca-HepPh pyramid hits the in-browser limit; the other
three benchmark graphs complete the full ablation.
Used to produce Table 4 / §6 (Browsing smoothness) in the GD 2026
submission paper.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rewrite of the browsing-smoothness harness to match what the GD 2026 paper's tab:smoothness reports: - Replace the (graph, maxLevels) ablation matrix with a single per-graph run; widen the graph list to the nine benchmarks the paper uses (gameofthrones, composers, four SNAP collab networks, facebook_combined, deezer_europe, delaunay_n15). - Drive each trial as three deterministic dives picked by a seeded mulberry32 PRNG inside the laid-out graph footprint (window.__msaglGraphBox); each dive interpolates minZoom -> maxZoom -> minZoom over a wall-clock budget so jank does not stretch the measurement window. - Probe the deck.gl Tileset2D at peak zoom of each dive and record per-frame elements and tiles visible, so the report can show the largest number of graphical objects the browser had to draw. - Bump protocolTimeout and keep one puppeteer browser warm across graphs, while still recycling pages between trials so released graph state can be GCed. Add a header comment pointing readers at the paper-side post-processor and canonical JSONL, which now live in ~/dev/paper_msagljs/ next to the rest of the benchmark artefacts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Renderer (renderer-webgl): - Interpolate node box size per frame between the current and next-finer tile level (GeometryLayer sizeLerp uniform + instanceSizesFiner) so node size is continuous across tile-level switches instead of jumping. - Render node labels at a fixed pixel size derived from the box's minimum on-screen size over the band: getLabelSize * min(scaleCurr, 2*scaleFiner) * 2^(nativeZoom-1). No pulsation and the label never overflows its box at any zoom, on uniform (GoT) and adaptive-scale (Composers) graphs alike. - Draw earlier-listed style layers (nodes) on the nearer depth slice so opaque node boxes render over edges and hide the edge ends that run to node centers. - Make edges twice thinner and slightly transparent. Routing (core): - Add a required trimEdges flag to routeSleeveEdges: the full-graph layout (driver) trims curves to node borders and keeps arrowheads, while the tile pyramid routes coarse levels untrimmed (curves to node centers) so edges stay connected under per-level/per-frame node scaling. When not trimming, clear any arrowheads left by a prior trimmed pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
# Conflicts: # .github/workflows/test.yml
On the finest tile level there is no finer level to interpolate toward (scaleCurr == scaleFiner == 1), so node boxes grow with zoom while fixed-pixel labels stayed put on screen. Render finest-level labels in 'common' units so they scale at the same speed as the box; coarser levels keep their fixed-pixel labels. The transition stays continuous across the level switch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds an interactive @msagl/renderer-webgl citation-graph viewer with: - author/title search; repeated Enter walks one author's papers by citation rank (inline pill, no dropdown over the canvas); mode switch keeps the query - LOD-aware zoom so rarely-cited nodes (only in the finest tiles) become visible and highlighted: new Renderer.zoomToNode(); SearchControl uses it too - defaults to rise_3k.json (~2.8k papers / ~10k citations) Ships the built viewer under website/static/citation-graph so it appears on the GitHub Pages site. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…serves them The webgl-sleeve sample graphs 404'd on GitHub Pages because the graphs/ folder was only produced by the example build script and never tracked in website/static, so Docusaurus deploy shipped the demo without them. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the vague "nine sample graphs" wording with a table naming each bundled sample and a deep link (?graph=<file>) that loads it directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…plines, None) The EdgeRoutingMode reference explained Spline, SplineBundling, Rectilinear, StraightLine and Sleeve but left RectilinearToCenter, SugiyamaSplines and None without their own sections. Add them so every enum value is described. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.9 to 2.0.10. - [Release notes](https://github.com/chimurai/http-proxy-middleware/releases) - [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.10/CHANGELOG.md) - [Commits](chimurai/http-proxy-middleware@v2.0.9...v2.0.10) --- updated-dependencies: - dependency-name: http-proxy-middleware dependency-version: 2.0.10 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
In TileMap.addNodeToLevel, dataByEntity.get(entity) can return undefined for edges/labels that have no clips at the current tile level. Iterating that undefined value with for..of threw 'TypeError: i.get is not a function or its return value is not iterable', aborting the WebGL render. Guard every dataByEntity.get(...) lookup with a '?? []' fallback so missing entities are skipped instead of crashing the render. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c1c16320-f8f4-42d3-9e4c-889223f2a531
Add ../JSONfiles/gameofthrones.json to the sample-graph dropdown in both the SVG and WebGL renderer examples, alongside the existing composers.json JSON sample. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c1c16320-f8f4-42d3-9e4c-889223f2a531
…on) load without crashing The deployed website/static/renderer-svg/app.js was stale (from 2023) and predated the JSON/JGF parser fixes, so loading citation-style JSON graphs threw an uncaught error on the live GitHub Pages site. Rebuilt from current source. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cc0d891e-4cbf-471e-bec0-4b95c4cb95d3
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Teach compatible agents to embed live graph viewers, run core layouts, and export document-ready vector assets. Add locked helper tooling and CI smoke coverage for discovery, browser loading, and SVG/PDF/PNG/EPS/PS output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| return s.toFixed(2) | ||
| } | ||
| lines.push( | ||
| r.name.replace(/_/g, '\\_') + ' & ' + |
| } | ||
| lines.push( | ||
| r.name.replace(/_/g, '\\_') + ' & ' + | ||
| r.distinctSources.toLocaleString('en-US').replace(/,/g, '\\,') + ' & ' + |
| lines.push( | ||
| r.name.replace(/_/g, '\\_') + ' & ' + | ||
| r.distinctSources.toLocaleString('en-US').replace(/,/g, '\\,') + ' & ' + | ||
| r.vcRoots.toLocaleString('en-US').replace(/,/g, '\\,') + ' & ' + |
| fmt(r.astarMs) + ' & ' + | ||
| fmt(r.dijkstraMs) + ' & ' + | ||
| fmt(r.dijkstraVcMs) + ' & ' + | ||
| '$' + pct(r.dijkstraMs, r.astarMs).replace('%', '\\,\\%').replace('+', '+').replace('-', '-') + '$ & ' + |
| fmt(r.dijkstraMs) + ' & ' + | ||
| fmt(r.dijkstraVcMs) + ' & ' + | ||
| '$' + pct(r.dijkstraMs, r.astarMs).replace('%', '\\,\\%').replace('+', '+').replace('-', '-') + '$ & ' + | ||
| '$' + pct(r.dijkstraVcMs, r.dijkstraMs).replace('%', '\\,\\%').replace('+', '+').replace('-', '-') + '$ \\\\') |
| } | ||
|
|
||
| function texEscape(name: string): string { | ||
| return name.replace(/_/g, '\\_') |
| fmt(r.astarMs) + ' & ' + | ||
| fmt(r.dijkstraMs) + ' & ' + | ||
| fmt(r.dijkstraVcMs) + ' & ' + | ||
| '$' + pct(r.dijkstraMs, r.astarMs).replace('%', '\\,\\%').replace('+', '+').replace('-', '-') + '$ & ' + |
| fmt(r.astarMs) + ' & ' + | ||
| fmt(r.dijkstraMs) + ' & ' + | ||
| fmt(r.dijkstraVcMs) + ' & ' + | ||
| '$' + pct(r.dijkstraMs, r.astarMs).replace('%', '\\,\\%').replace('+', '+').replace('-', '-') + '$ & ' + |
| fmt(r.dijkstraMs) + ' & ' + | ||
| fmt(r.dijkstraVcMs) + ' & ' + | ||
| '$' + pct(r.dijkstraMs, r.astarMs).replace('%', '\\,\\%').replace('+', '+').replace('-', '-') + '$ & ' + | ||
| '$' + pct(r.dijkstraVcMs, r.dijkstraMs).replace('%', '\\,\\%').replace('+', '+').replace('-', '-') + '$ \\\\') |
| fmt(r.dijkstraMs) + ' & ' + | ||
| fmt(r.dijkstraVcMs) + ' & ' + | ||
| '$' + pct(r.dijkstraMs, r.astarMs).replace('%', '\\,\\%').replace('+', '+').replace('-', '-') + '$ & ' + | ||
| '$' + pct(r.dijkstraVcMs, r.dijkstraMs).replace('%', '\\,\\%').replace('+', '+').replace('-', '-') + '$ \\\\') |
|
CI status: all build matrices, the new |
There was a problem hiding this comment.
Pull request overview
Promotes the substantially-ahead dev branch changes to main, including a new distributable msagljs Agent Skill, new website demos/docs, and significant renderer/core updates (notably sleeve routing + tile-pyramid UX improvements) across the monorepo.
Changes:
- Add an
msagljsAgent Skill (docs, references, examples, and CI smoke tests for live rendering + static export formats). - Introduce/extend sleeve routing support end-to-end (core routing mode + driver wiring + WebGL demo/docs updates).
- Improve WebGL renderer UX/interaction (top-bar layout, edge picking/highlighting, continuous node sizing across tile levels) and add new demo pages.
Reviewed changes
Copilot reviewed 126 out of 157 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| website/static/webgl-sleeve/index.html | New deployed WebGL sleeve demo page |
| website/static/renderer-webgl/index.html | Refresh renderer-webgl deployed UI (top bar, URL loader, smooth corners toggle, error banner) |
| website/static/citation-graph/index.html | New deployed citation-graph demo page |
| website/docs/intro.md | Link to new sleeve/tile-pyramid demos page |
| website/docs/demos.md | New docs page describing demos + paper links |
| website/docs/api.md | Document new routing modes (RectilinearToCenter, Sleeve) and clarify others |
| tsconfig.skill-examples.json | Add TS config for skill example typechecking |
| SUPPORT.md | Minor wording normalization |
| skills/msagljs/SKILL.md | New skill manifest + usage guidance |
| skills/msagljs/scripts/smoke-live-page.mjs | New puppeteer smoke test for live SVG embedding |
| skills/msagljs/scripts/package.json | Skill helper tool dependencies for smoke/export scripts |
| skills/msagljs/references/troubleshooting.md | Skill troubleshooting reference |
| skills/msagljs/references/packages-and-inputs.md | Skill package selection + parsing inputs reference |
| skills/msagljs/references/live-web-embedding.md | Skill guidance for live SVG/WebGL embedding |
| skills/msagljs/references/layout-and-routing.md | Skill guidance for layout/routing selection |
| skills/msagljs/references/document-export.md | Skill guidance for static SVG/PDF/PNG/EPS/PS export |
| skills/msagljs/references/core-layout.md | Skill guidance for headless core layout workflow |
| skills/msagljs/references/browser-renderers.md | Skill guidance for renderer usage + lifecycle |
| skills/msagljs/examples/network.txt | Skill example edge-list input |
| skills/msagljs/examples/network.json | Skill example JSON input |
| skills/msagljs/examples/network.jgf | Skill example JGF input |
| skills/msagljs/examples/network.dot | Skill example DOT input |
| skills/msagljs/examples/live-webgl-page.ts | Skill example: live WebGL embedding |
| skills/msagljs/examples/live-svg-page.ts | Skill example: live SVG embedding + SVG download |
| skills/msagljs/examples/live-page.html | Skill example HTML host page |
| skills/msagljs/examples/latex-figure.tex | Skill example LaTeX include snippet |
| skills/msagljs/examples/core-layout.ts | Skill example: core layout without renderer |
| README.md | Document discovery/installation of msagljs Agent Skill |
| modules/renderer-webgl/src/layers/graph-layer.ts | Edge picking support + layer depth ordering + tile-level props plumbing |
| modules/renderer-webgl/src/layers/graph-highlighter.ts | Add edge index/picking color mapping + direct node highlighting helpers |
| modules/renderer-webgl/src/layers/get-edge-layers.ts | Support pickable edges + tweak default edge styling |
| modules/renderer-webgl/src/layers/geometry-layer.ts | Continuous cross-tile node size interpolation + cluster-highlight dampening |
| modules/renderer-webgl/src/layers/curve-layer.ts | Add per-edge picking colors + hovered-edge highlighting/thickening |
| modules/renderer-webgl/src/index.ts | Export TooltipProvider from renderer entrypoint |
| modules/renderer-webgl/src/controls/search-control.ts | Use renderer.zoomToNode() helper (remove GeomNode dependency) |
| modules/renderer-webgl/package.json | Bump version + update internal dependency ranges |
| modules/renderer-svg/package.json | Bump version + update internal dependency ranges |
| modules/renderer-common/src/layout.ts | Worker layout supersede handling + applyLayoutSettings helper + new options (smoothCorners) |
| modules/renderer-common/src/index.ts | Add smoothCorners to LayoutOptions |
| modules/renderer-common/package.json | Bump version + update internal dependency ranges |
| modules/parser/test/dotparser.spec.ts | Add tests for gzip + URL/file loading + JGF parsing variants |
| modules/parser/src/index.ts | Export parseJGF from public API |
| modules/parser/package.json | Bump version + update dependency ranges |
| modules/drawing/package.json | Bump version + update dependency ranges |
| modules/core/test/utils/svgDebugWriter.ts | Gate debug SVG filesystem writes behind env var |
| modules/core/test/routing/sleeveRouterGot.spec.ts | New GOT sleeve-routing stability/behavior tests |
| modules/core/test/routing/sleeveRouter.spec.ts | New sleeve router unit tests + subgraph routing case |
| modules/core/test/routing/sleeveEdgeLengths.spec.ts | New sleeve vs spline edge-length ratio benchmarks (non-asserting) |
| modules/core/test/routing/SingleSourceSingleTargetShortestPathOnVisibilityGraph.spec.ts | Update tests to new GetPath signature |
| modules/core/test/math/geometry/pointArray.spec.ts | New PointArray tests |
| modules/core/test/math/geometry/point.spec.ts | Add tests for new in-place Point mutation APIs |
| modules/core/test/layout/layoutEditing/incrementalDragger.spec.ts | Remove test-time filesystem output |
| modules/core/test/layout/largeGraphs.spec.ts | Add skipped large-graph loader/layout tests scaffold |
| modules/core/src/structs/graph.ts | Update pagerank to use undirected-degree propagation |
| modules/core/src/routing/visibility/VisibilityGraph.ts | Remove edge-length shrink helper |
| modules/core/src/routing/visibility/VisibilityEdge.ts | Remove LengthMultiplier from visibility edges |
| modules/core/src/routing/splineRouter.ts | Remove unused edge-length multiplier plumbing |
| modules/core/src/routing/spline/pathOptimizer.ts | Add degeneracy handling + triangle lookup fallback |
| modules/core/src/routing/spline/bundling/CostCalculator.ts | Use distPP to reduce Point allocations |
| modules/core/src/routing/SingleSourceSingleTargetShortestPathOnVisibilityGraph.ts | Simplify A* path API (remove shrink/multiplier plumbing) |
| modules/core/src/routing/PreGraph.ts | Avoid concat allocation in AddGraph |
| modules/core/src/routing/passportRouting.ts | Extract passport/obstacle helpers for cluster-aware routing reuse |
| modules/core/src/routing/interactiveEdgeRouter.ts | Remove unused multiplier flag + avoid intermediate array allocations |
| modules/core/src/routing/EdgeRoutingSettings.ts | Add smoothCorners setting |
| modules/core/src/routing/EdgeRoutingMode.ts | Add Sleeve routing mode |
| modules/core/src/math/geometry/smoothedPolyline.ts | Cleanup + fix bezier end-point usage in curve construction |
| modules/core/src/math/geometry/rectangle.ts | Reduce Point allocations + add addCoords + correct containsRect comment/spelling |
| modules/core/src/math/geometry/pointArray.ts | New Float64Array-backed PointArray utility |
| modules/core/src/math/geometry/point.ts | Optimize dist/close ops + make x/y fields mutable + add in-place mutation APIs |
| modules/core/src/math/geometry/index.ts | Export PointArray |
| modules/core/src/math/geometry/debugSvg.ts | New browser-safe DebugCurve SVG serializer/downloader |
| modules/core/src/math/geometry/bezierSeg.ts | Make lengthOnControlPolygon iterative (avoid allocations/recursion) |
| modules/core/src/layout/mds/AllPairsDistances.ts | Optimize stress computation loop and symmetry accounting |
| modules/core/src/layout/layered/ProperLayeredGraph.ts | Replace filter allocations with counted loops |
| modules/core/src/layout/incremental/multipole/minimumEnclosingDisc.ts | Replace recursive Welzl with iterative implementation |
| modules/core/src/layout/driver.ts | Wire Sleeve routing through routeEdges() |
| modules/core/src/layout/core/tile.ts | Bundle CurveClips by endpoint pairs to reduce per-tile edge geometry |
| modules/core/src/index.ts | Export new debug SVG helpers + sleeve routing APIs + CDT + PointArray |
| modules/core/package.json | Bump core version |
| examples/webgl-sleeve/tsconfig.json | New TS config for webgl-sleeve example |
| examples/webgl-sleeve/src/worker.ts | Worker entrypoint for webgl-sleeve example |
| examples/webgl-sleeve/src/settings.ts | Sample graph catalog + layout/routing UI constants |
| examples/webgl-sleeve/src/drag-n-drop.ts | Drag/drop helper for example UI |
| examples/webgl-sleeve/package.json | New webgl-sleeve example package + build pipeline to website/static |
| examples/webgl-sleeve/index.html | Example host page for webgl-sleeve |
| examples/webgl-sleeve/bench/.gitignore | Ignore bench output |
| examples/webgl-renderer/src/settings.ts | Add Sleeve routing option and add GOT sample to list |
| examples/webgl-renderer/package.json | Ensure worker is bundled for deployment build |
| examples/webgl-renderer/index.html | Refresh example UI to match deployed renderer-webgl page |
| examples/svg-renderer/src/settings.ts | Add GOT sample and Sleeve routing option |
| examples/svg-renderer/src/app.ts | Support Sleeve routing option |
| examples/citation-graph/tsconfig.json | New TS config for citation-graph example |
| examples/citation-graph/src/worker.ts | Worker entrypoint for citation-graph example |
| examples/citation-graph/README.md | Document citation-graph demo behavior + tooltip provider |
| examples/citation-graph/package.json | New citation-graph example package + deployment build |
| examples/citation-graph/index.html | Citation-graph example host page |
| examples/citation-graph/.gitignore | Ignore built artifacts + node_modules |
| examples/chrome-routing-bench/server.js | Local benchmark server for paper experiments |
| examples/chrome-routing-bench/README.md | Benchmark usage documentation |
| examples/chrome-routing-bench/loading.html | Loading benchmark page |
| examples/chrome-routing-bench/index.html | Routing-modes benchmark page |
| examples/chrome-routing-bench/.gitignore | Ignore local msagl bundle artifact |
| deploy-webgl-sleeve.sh | Script to build + publish webgl-sleeve demo via surge |
| .gitignore | Ignore top-level graphs directory |
| .github/workflows/test.yml | Expand Node matrix + add agent-skill validation job + refine deploy gating |
| .github/workflows/codeql-analysis.yml | Change CodeQL branch triggers |
| .github/copilot-instructions.md | Add repo-specific Copilot contributor guidance |
Files not reviewed (1)
- skills/msagljs/scripts/package-lock.json: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (layoutInProgress && layoutWorker) { | ||
| // Supersede the in-flight request. Clear handlers first so terminate() | ||
| // does not fire our onerror and reject the (now abandoned) promise — that | ||
| // would surface as "Uncaught (in promise)" in the example code which does | ||
| // not catch rejections from setOptions/setGraph. | ||
| layoutWorker.onmessage = null | ||
| layoutWorker.onerror = null | ||
| layoutWorker.terminate() | ||
| layoutWorker = null | ||
| layoutInProgress = false | ||
| } |
| layoutSettings.edgeRoutingSettings.EdgeRoutingMode = EdgeRoutingMode.SugiyamaSplines | ||
| } else { | ||
| layoutSettings.edgeRoutingSettings.EdgeRoutingMode = EdgeRoutingMode.Spline | ||
| layoutSettings.edgeRoutingSettings.EdgeRoutingMode = EdgeRoutingMode.Sleeve |
| routeEdges(geomGraph, Array.from(geomGraph.deepEdges), null) | ||
| // console.timeEnd('routeEdges') | ||
| } | ||
| console.log(`layout: ${(performance.now() - t0).toFixed(1)}ms`) |
| const ROOT_BENCH = '/Users/levnach/dev/msagljs/examples/chrome-routing-bench'; | ||
| const ROOT_GRAPHS = '/Users/levnach/dev/paper_msagljs/graphs'; |
| @@ -0,0 +1,161 @@ | |||
| <html> | |||
| @@ -0,0 +1,161 @@ | |||
| <html> | |||
| push: | ||
| branches: [ "main" ] | ||
| branches: [ "dev" ] | ||
| pull_request: | ||
| # The branches below must be a subset of the branches above | ||
| branches: [ "main" ] | ||
| branches: [ "dev" ] |
| - Sleeve routing exists on the repository's development branch but is not | ||
| exposed by the npm `@msagl/core` version locked by the portable exporter. | ||
| Check the installed package before using it in application code. |
Summary
devbranch tomainmsagljsAgent Skill for core layout, live SVG/WebGL embedding, and document-ready graph exportmsagljs@devdiscovery and installation for supported agent hostsThis PR contains the full accumulated
devbranch history becausedevwas already substantially ahead ofmainbefore the skill was added.Validation
yarn buildyarn test --runInBand(76 suites, 307 tests passed; 83 skipped)yarn tsc -p tsconfig.skill-examples.jsongh skill publish --dry-run .gh skill install --from-local