Snapshot of main at 9f39a6e1e (2026-11-02)
This commit is contained in:
commit
6ccf1792d7
2580 files changed
+1040202
No files matched your search
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* The trading-network layout, as a pure function of nodes and edges.
|
||||
*
|
||||
* Kept apart from the page so it can run in a Web Worker (see
|
||||
* network-layout-worker.js). fcose runs synchronously: on the ~1200-profile
|
||||
* production graph it blocks ~4.5s on a desktop and several times that on a
|
||||
* phone, where iOS stops a script that runs too long in one go - the graph
|
||||
* then stayed on "Arranging N profiles..." for good. In a worker the page's
|
||||
* own thread never blocks, whatever the device.
|
||||
*
|
||||
* Profiled on the real graph: ~70% of the time is fcose's repulsion and
|
||||
* overlap maths, and numIter makes no difference (250 or 2500, same time,
|
||||
* same quality) - so moving it off the main thread is the fix, not tuning.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Function} cytoscape The cytoscape factory, with fcose registered.
|
||||
* @param {object} input {nodes: [{id, size}], edges: [{id, source, target}], options}
|
||||
* @returns {Promise<Object<string, {x: number, y: number}>>} positions by node id
|
||||
*/
|
||||
export function layoutPositions(cytoscape, { nodes, edges, options }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let cy = null;
|
||||
|
||||
try {
|
||||
cy = cytoscape({
|
||||
headless: true,
|
||||
// Needed headless for node sizes to count: fcose keeps nodes
|
||||
// apart by their real dimensions, so a layout computed on
|
||||
// default-sized nodes would not match the drawing.
|
||||
styleEnabled: true,
|
||||
style: [{ selector: 'node', style: { width: 'data(size)', height: 'data(size)' } }],
|
||||
elements: [
|
||||
...nodes.map((n) => ({ data: { id: n.id, size: n.size || 8 } })),
|
||||
...edges.map((e) => ({ data: { id: e.id, source: e.source, target: e.target } })),
|
||||
],
|
||||
});
|
||||
|
||||
const layout = cy.layout({ ...options, animate: false });
|
||||
|
||||
layout.one('layoutstop', () => {
|
||||
const positions = {};
|
||||
|
||||
cy.nodes().forEach((node) => {
|
||||
const p = node.position();
|
||||
|
||||
positions[node.id()] = { x: p.x, y: p.y };
|
||||
});
|
||||
|
||||
cy.destroy();
|
||||
resolve(positions);
|
||||
});
|
||||
|
||||
layout.run();
|
||||
} catch (error) {
|
||||
if (cy && !cy.destroyed()) {
|
||||
cy.destroy();
|
||||
}
|
||||
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user