flow like the river

This commit is contained in:
root 2025-11-07 00:06:12 +01:00
commit 013fe673f3
42435 changed files with 5764238 additions and 0 deletions

21
VISUALIZACION/node_modules/force-graph/LICENSE generated vendored Executable file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Vasco Asturiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

225
VISUALIZACION/node_modules/force-graph/README.md generated vendored Executable file
View file

@ -0,0 +1,225 @@
force-graph
============================
[![NPM package][npm-img]][npm-url]
[![Build Size][build-size-img]][build-size-url]
[![NPM Downloads][npm-downloads-img]][npm-downloads-url]
Force-directed graph rendered on HTML5 canvas.
<p align="center">
<a href="https://vasturiano.github.io/force-graph/example/medium-graph"><img width="80%" src="https://vasturiano.github.io/force-graph/example/medium-graph/preview.png"></a>
</p>
A web component to represent a graph data structure in a 2-dimensional canvas using a force-directed iterative layout.
Uses HTML5 canvas for rendering and [d3-force](https://github.com/d3/d3-force) for the underlying physics engine.
Supports canvas zooming/panning, node dragging and node/link hover/click interactions.
See also the [3D version](https://github.com/vasturiano/3d-force-graph).
And check out the [React bindings](https://github.com/vasturiano/react-force-graph).
## Examples
* [Basic](https://vasturiano.github.io/force-graph/example/basic/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/basic/index.html))
* [Load JSON](https://vasturiano.github.io/force-graph/example/load-json/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/load-json/index.html))
* [Medium size graph (~4k elements)](https://vasturiano.github.io/force-graph/example/medium-graph/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/medium-graph/index.html))
* [Large size graph (~75k elements)](https://vasturiano.github.io/force-graph/example/large-graph/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/large-graph/index.html))
* [Text as nodes](https://vasturiano.github.io/force-graph/example/text-nodes/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/text-nodes/index.html))
* [Images as nodes](https://vasturiano.github.io/force-graph/example/img-nodes/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/img-nodes/index.html))
* [Directional links (using arrows)](https://vasturiano.github.io/force-graph/example/directional-links-arrows/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/directional-links-arrows/index.html))
* [Directional links (using moving particles)](https://vasturiano.github.io/force-graph/example/directional-links-particles/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/directional-links-particles/index.html))
* [Curved lines and self links](https://vasturiano.github.io/force-graph/example/curved-links/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/curved-links/index.html))
* [Automatic curvature for overlapping links](https://vasturiano.github.io/force-graph/example/curved-links-computed-curvature/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/curved-links-computed-curvature/index.html))
* [Text in links](https://vasturiano.github.io/force-graph/example/text-links/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/text-links/index.html))
* [Dash odd links](https://vasturiano.github.io/force-graph/example/dash-odd-links/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/dash-odd-links/index.html))
* [Highlight nodes/links](https://vasturiano.github.io/force-graph/example/highlight/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/highlight/index.html))
* [Multiple Node Selection](https://vasturiano.github.io/force-graph/example/multi-selection/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/multi-selection/index.html))
* [Auto-colored nodes/links](https://vasturiano.github.io/force-graph/example/auto-colored/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/auto-colored/index.html))
* [Custom node shapes](https://vasturiano.github.io/force-graph/example/custom-node-shape/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/custom-node-shape/index.html))
* [Pre-computed layout (using dagre)](https://vasturiano.github.io/force-graph/example/dagre/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/dagre/index.html))
* [Zoom/pan viewport](https://vasturiano.github.io/force-graph/example/move-viewport/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/move-viewport/index.html))
* [Click to focus on node](https://vasturiano.github.io/force-graph/example/click-to-focus/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/click-to-focus/index.html))
* [Click to expand/collapse nodes](https://vasturiano.github.io/force-graph/example/expandable-nodes/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/expandable-nodes/index.html))
* [Fix nodes after dragging](https://vasturiano.github.io/force-graph/example/fix-dragged-nodes/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/fix-dragged-nodes/index.html))
* [Fit graph to canvas](https://vasturiano.github.io/force-graph/example/fit-to-canvas/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/fit-to-canvas/index.html))
* [Dynamic data changes](https://vasturiano.github.io/force-graph/example/dynamic/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/dynamic/index.html))
* [Node collision detection](https://vasturiano.github.io/force-graph/example/collision-detection/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/collision-detection/index.html))
* [Emit link particles on demand](https://vasturiano.github.io/force-graph/example/emit-particles/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/emit-particles/index.html))
* [Force-directed tree (DAG mode)](https://vasturiano.github.io/force-graph/example/tree/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/tree/index.html))
* [Expandable Tree](https://vasturiano.github.io/force-graph/example/expandable-tree/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/expandable-tree/index.html))
* [yarn.lock dependency graph (DAG mode)](https://vasturiano.github.io/force-graph/example/dag-yarn/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/dag-yarn/index.html))
* [Usage as UI to construct graphs](https://vasturiano.github.io/force-graph/example/build-a-graph/) ([source](https://github.com/vasturiano/force-graph/blob/master/example/build-a-graph/index.html))
## Quick start
```js
import ForceGraph from 'force-graph';
```
or using a *script* tag
```html
<script src="//unpkg.com/force-graph"></script>
```
then
```js
const myGraph = ForceGraph();
myGraph(<myDOMElement>)
.graphData(<myData>);
```
## API reference
### Data input
| Method | Description | Default |
| --- | --- | :--: |
| <b>graphData</b>([<i>data</i>]) | Getter/setter for graph data structure (see below for syntax details). Can also be used to apply [incremental updates](https://vasturiano.github.io/force-graph/example/dynamic/). | `{ nodes: [], links: [] }` |
| <b>nodeId</b>([<i>str</i>]) | Node object accessor attribute for unique node id (used in link objects source/target). | `id` |
| <b>linkSource</b>([<i>str</i>]) | Link object accessor attribute referring to id of source node. | `source` |
| <b>linkTarget</b>([<i>str</i>]) | Link object accessor attribute referring to id of target node. | `target` |
### Container layout
| Method | Description | Default |
| --- | --- | :--: |
| <b>width</b>([<i>px</i>]) | Getter/setter for the canvas width. | *&lt;window width&gt;* |
| <b>height</b>([<i>px</i>]) | Getter/setter for the canvas height. | *&lt;window height&gt;* |
| <b>backgroundColor</b>([<i>str</i>]) | Getter/setter for the chart background color. | *&lt;transparent&gt;* |
### Node styling
| Method | Description | Default |
| --- | --- | :--: |
| <b>nodeRelSize</b>([<i>num</i>]) | Getter/setter for the ratio of node circle area (square px) per value unit. | 4 |
| <b>nodeVal</b>([<i>num</i>, <i>str</i> or <i>fn</i>]) | Node object accessor function, attribute or a numeric constant for the node numeric value (affects circle area). | `val` |
| <b>nodeLabel</b>([<i>str</i> or <i>fn</i>]) | Node object accessor function or attribute for name (shown in label). Supports plain text or HTML content. Note that this method uses `innerHTML` internally, so make sure to pre-sanitize any user-input content to prevent XSS vulnerabilities. | `name` |
| <b>nodeVisibility</b>([<i>boolean</i>, <i>str</i> or <i>fn</i>]) | Node object accessor function, attribute or a boolean constant for whether to display the node. | `true` |
| <b>nodeColor</b>([<i>str</i> or <i>fn</i>]) | Node object accessor function or attribute for node color (affects circle color). | `color` |
| <b>nodeAutoColorBy</b>([<i>str</i> or <i>fn</i>]) | Node object accessor function (`fn(node)`) or attribute (e.g. `'type'`) to automatically group colors by. Only affects nodes without a color attribute. | |
| <b>nodeCanvasObject</b>([<i>fn</i>]) | Callback function for painting a custom canvas object to represent graph nodes. Should use the provided canvas context attribute to perform drawing operations for each node. The callback function will be called for each node at every frame, and has the signature: `.nodeCanvasObject(<node>, <canvas context>, <current global scale>)`. | *default node object is a circle, sized according to `val` and styled according to `color`.* |
| <b>nodeCanvasObjectMode</b>([<i>str</i> or <i>fn</i>]) | Node object accessor function or attribute for the custom drawing mode. Use in combination with `nodeCanvasObject` to specify how to customize nodes painting. Possible values are: <ul><li>`replace`: the node is rendered using just `nodeCanvasObject`.</li><li>`before`: the node is rendered by invoking `nodeCanvasObject` and then proceeding with the default node painting.</li><li>`after`: `nodeCanvasObject` is applied after the default node painting takes place.</li></ul>Any other value will be ignored and the default drawing will be applied. | `() => 'replace'` |
### Link styling
| Method | Description | Default |
| --- | --- | :--: |
| <b>linkLabel</b>([<i>str</i> or <i>fn</i>]) | Link object accessor function or attribute for name (shown in label). Supports plain text or HTML content. Note that this method uses `innerHTML` internally, so make sure to pre-sanitize any user-input content to prevent XSS vulnerabilities. | `name` |
| <b>linkVisibility</b>([<i>boolean</i>, <i>str</i> or <i>fn</i>]) | Link object accessor function, attribute or a boolean constant for whether to display the link line. A value of `false` maintains the link force without rendering it. | `true` |
| <b>linkColor</b>([<i>str</i> or <i>fn</i>]) | Link object accessor function or attribute for line color. | `color` |
| <b>linkAutoColorBy</b>([<i>str</i> or <i>fn</i>]) | Link object accessor function (`fn(link)`) or attribute (e.g. `'type'`) to automatically group colors by. Only affects links without a color attribute. | |
| <b>linkLineDash</b>([<i>num[]</i>, <i>str</i> or <i>fn</i>]) | Link object accessor function, attribute or number array (e.g. `[5, 15]`) to determine if a line dash should be applied to this rendered link. Refer to the [HTML canvas setLineDash API](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash) for example values. Either a falsy value or an empty array will disable dashing. | `null` |
| <b>linkWidth</b>([<i>num</i>, <i>str</i> or <i>fn</i>]) | Link object accessor function, attribute or a numeric constant for the link line width. Keep in mind that link widths remain visually contant through various zoom levels, where as node sizes scale relatively. | 1 |
| <b>linkCurvature</b>([<i>num</i>, <i>str</i> or <i>fn</i>]) | Link object accessor function, attribute or a numeric constant for the curvature radius of the link line. Curved lines are represented as bezier curves, and any numeric value is accepted. A value of `0` renders a straight line. `1` indicates a radius equal to half of the line length, causing the curve to approximate a semi-circle. For self-referencing links (`source` equal to `target`) the curve is represented as a loop around the node, with length proportional to the curvature value. Lines are curved clockwise for positive values, and counter-clockwise for negative values. Note that rendering curved lines is purely a visual effect and does not affect the behavior of the underlying forces. | 0 |
| <b>linkCanvasObject</b>([<i>fn</i>]) | Callback function for painting a custom canvas object to represent graph links. Should use the provided canvas context attribute to perform drawing operations for each link. The callback function will be called for each link at every frame, and has the signature: `.linkCanvasObject(<link>, <canvas context>, <current global scale>)`. | *default link object is a line, styled according to `width` and `color`.* |
| <b>linkCanvasObjectMode</b>([<i>str</i> or <i>fn</i>]) | Link object accessor function or attribute for the custom drawing mode. Use in combination with `linkCanvasObject` to specify how to customize links painting. Possible values are: <ul><li>`replace`: the link is rendered using just `linkCanvasObject`.</li><li>`before`: the link is rendered by invoking `linkCanvasObject` and then proceeding with the default link painting.</li><li>`after`: `linkCanvasObject` is applied after the default link painting takes place.</li></ul>Any other value will be ignored and the default drawing will be applied. | `() => 'replace'` |
| <b>linkDirectionalArrowLength</b>([<i>num</i>, <i>str</i> or <i>fn</i>]) | Link object accessor function, attribute or a numeric constant for the length (in `px`) of the arrow head indicating the link directionality. The arrow is displayed directly over the link line, and points in the direction of `source` > `target`. A value of `0` hides the arrow. | 0 |
| <b>linkDirectionalArrowColor</b>([<i>str</i> or <i>fn</i>]) | Link object accessor function or attribute for the color of the arrow head. | `color` |
| <b>linkDirectionalArrowRelPos</b>([<i>num</i>, <i>str</i> or <i>fn</i>]) | Link object accessor function, attribute or a numeric constant for the longitudinal position of the arrow head along the link line, expressed as a ratio between `0` and `1`, where `0` indicates immediately next to the `source` node, `1` next to the `target` node, and `0.5` right in the middle. | 0.5 |
| <b>linkDirectionalParticles</b>([<i>num</i>, <i>str</i> or <i>fn</i>]) | Link object accessor function, attribute or a numeric constant for the number of particles (small circles) to display over the link line. The particles are distributed equi-spaced along the line, travel in the direction `source` > `target`, and can be used to indicate link directionality. | 0 |
| <b>linkDirectionalParticleSpeed</b>([<i>num</i>, <i>str</i> or <i>fn</i>]) | Link object accessor function, attribute or a numeric constant for the directional particles speed, expressed as the ratio of the link length to travel per frame. Values above `0.5` are discouraged. | 0.01 |
| <b>linkDirectionalParticleWidth</b>([<i>num</i>, <i>str</i> or <i>fn</i>]) | Link object accessor function, attribute or a numeric constant for the directional particles width (diameter). | 4 |
| <b>linkDirectionalParticleColor</b>([<i>str</i> or <i>fn</i>]) | Link object accessor function or attribute for the directional particles color. | `color` |
| <b>emitParticle</b>(<i>link</i>) | An alternative mechanism for generating particles, this method emits a non-cyclical single particle within a specific link. The emitted particle shares the styling (speed, width, color) of the regular particle props. A valid `link` object that is included in `graphData` should be passed as a single parameter. ||
### Render control
| Method | Description | Default |
| --- | --- | :--: |
| <b>autoPauseRedraw</b>([<i>boolean</i>]) | Getter/setter for performance optimization to automatically pause redrawing the canvas at every frame whenever the simulation engine is halted. If you have custom dynamic objects that rely on a constant redraw of the canvas, it's recommended to switch this option off. | `true` |
| <b>pauseAnimation</b>() <br/><sub>(alias: <i>stopAnimation</i>)</sub> | Pauses the rendering cycle of the component, effectively freezing the current view and cancelling all user interaction. This method can be used to save performance in circumstances when a static image is sufficient. | |
| <b>resumeAnimation</b>() | Resumes the rendering cycle of the component, and re-enables the user interaction. This method can be used together with `pauseAnimation` for performance optimization purposes. | |
| <b>centerAt</b>([<i>x</i>], [<i>y</i>], [<i>ms</i>]) | Getter/setter for the coordinates of the center of the viewport. This method can be used to perform panning on the canvas programmatically. Each of the `x, y` coordinates is optional, allowing for motion in just one dimension. An optional 3rd argument defines the duration of the transition (in ms) to animate the canvas motion. A value of 0 (default) centers immediately in the final position. | 0,0 |
| <b>zoom</b>([<i>num</i>], [<i>ms</i>]) | Getter/setter for the canvas zoom amount. The zoom is defined in terms of the scale transform of each px. A value of `1` indicates unity, larger values zoom in and smaller values zoom out. An optional 2nd argument defines the duration of the transition (in ms) to animate the canvas motion. A value of 0 (default) jumps immediately to the final position. | By default the zoom is set to a value inversely proportional to the amount of nodes in the system. |
| <b>zoomToFit</b>([<i>ms</i>], [<i>px</i>], [<i>nodeFilterFn</i>]) | Automatically zooms/pans the canvas so that all of the nodes fit inside it. If no nodes are found no action is taken. It accepts three optional arguments: the first defines the duration of the transition (in ms) to animate the canvas motion (default: 0ms). The second argument is the amount of padding (in px) between the edge of the canvas and the outermost node (default: 10px). The third argument specifies a custom node filter: `node => <boolean>`, which should return a truthy value if the node is to be included. This can be useful for focusing on a portion of the graph. | `(0, 10, node => true)` |
| <b>minZoom</b>([<i>num</i>]) | Getter/setter for the lowest zoom out level permitted. | 0.01 |
| <b>maxZoom</b>([<i>num</i>]) | Getter/setter for the highest zoom in level permitted. | 1000 |
| <b>onRenderFramePre</b>(<i>fn</i>) | Callback function to invoke at every frame, immediately before any node/link is rendered to the canvas. This can be used to draw additional external items on the canvas. The canvas context and the current global scale are included as parameters: `.onRenderFramePre(<canvas context>, <global scale>)`. | - |
| <b>onRenderFramePost</b>(<i>fn</i>) | Callback function to invoke at every frame, immediately after the last node/link is rendered to the canvas. This can be used to draw additional external items on the canvas. The canvas context and the current global scale are included as parameters: `.onRenderFramePost(<canvas context>, <global scale>)`. | - |
### Force engine (d3-force) configuration
| Method | Description | Default |
| --- | --- | :--: |
| <b>dagMode</b>([<i>str</i>]) | Apply layout constraints based on the graph directionality. Only works correctly for [DAG](https://en.wikipedia.org/wiki/Directed_acyclic_graph) graph structures (without cycles). Choice between `td` (top-down), `bu` (bottom-up), `lr` (left-to-right), `rl` (right-to-left), `radialout` (outwards-radially) or `radialin` (inwards-radially). | |
| <b>dagLevelDistance</b>([<i>num</i>]) | If `dagMode` is engaged, this specifies the distance between the different graph depths. | *auto-derived from the number of nodes* |
| <b>dagNodeFilter</b>([<i>fn</i>]) | Node accessor function to specify nodes to ignore during the DAG layout processing. This accessor method receives a node object and should return a `boolean` value indicating whether the node is to be included. Excluded nodes will be left unconstrained and free to move in any direction. | `node => true` |
| <b>onDagError</b>([<i>fn</i>]) | Callback to invoke if a cycle is encountered while processing the data structure for a DAG layout. The loop segment of the graph is included for information, as an array of node ids. By default an exception will be thrown whenever a loop is encountered. You can override this method to handle this case externally and allow the graph to continue the DAG processing. Strict graph directionality is not guaranteed if a loop is encountered and the result is a best effort to establish a hierarchy. | *throws exception* |
| <b>d3AlphaMin</b>([<i>num</i>]) | Getter/setter for the [simulation alpha min](https://github.com/d3/d3-force#simulation_alphaMin) parameter. | `0` |
| <b>d3AlphaDecay</b>([<i>num</i>]) | Getter/setter for the [simulation intensity decay](https://github.com/d3/d3-force#simulation_alphaDecay) parameter. | `0.0228` |
| <b>d3VelocityDecay</b>([<i>num</i>]) | Getter/setter for the nodes' [velocity decay](https://github.com/d3/d3-force#simulation_velocityDecay) that simulates the medium resistance. | `0.4` |
| <b>d3Force</b>(<i>str</i>, [<i>fn</i>]) | Getter/setter for the internal forces that control the d3 simulation engine. Follows the same interface as `d3-force`'s [simulation.force](https://github.com/d3/d3-force#simulation_force). Three forces are included by default: `'link'` (based on [forceLink](https://github.com/d3/d3-force#forceLink)), `'charge'` (based on [forceManyBody](https://github.com/d3/d3-force#forceManyBody)) and `'center'` (based on [forceCenter](https://github.com/d3/d3-force#forceCenter)). Each of these forces can be reconfigured, or new forces can be added to the system. | |
| <b>d3ReheatSimulation</b>() | Reheats the force simulation engine, by setting the `alpha` value to `1`. Only applicable if using the d3 simulation engine. | |
| <b>warmupTicks</b>([<i>int</i>]) | Getter/setter for number of layout engine cycles to dry-run at ignition before starting to render. | 0 |
| <b>cooldownTicks</b>([<i>int</i>]) | Getter/setter for how many build-in frames to render before stopping and freezing the layout engine. | Infinity |
| <b>cooldownTime</b>([<i>num</i>]) | Getter/setter for how long (ms) to render for before stopping and freezing the layout engine. | 15000 |
| <b>onEngineTick</b>(<i>fn</i>) | Callback function invoked at every tick of the simulation engine. | - |
| <b>onEngineStop</b>(<i>fn</i>) | Callback function invoked when the simulation engine stops and the layout is frozen. | - |
### Interaction
| Method | Description | Default |
| --- | --- | :--: |
| <b>onNodeClick</b>(<i>fn</i>) | Callback function for node (left-button) clicks. The node object and the event object are included as arguments `onNodeClick(node, event)`. | - |
| <b>onNodeRightClick</b>(<i>fn</i>) | Callback function for node right-clicks. The node object and the event object are included as arguments `onNodeRightClick(node, event)`. | - |
| <b>onNodeHover</b>(<i>fn</i>) | Callback function for node mouse over events. The node object (or `null` if there's no node under the mouse line of sight) is included as the first argument, and the previous node object (or null) as second argument: `onNodeHover(node, prevNode)`. | - |
| <b>onNodeDrag</b>(<i>fn</i>) | Callback function for node drag interactions. This function is invoked repeatedly while dragging a node, every time its position is updated. The node object is included as the first argument, and the change in coordinates since the last iteration of this function are included as the second argument in format {x,y,z}: `onNodeDrag(node, translate)`. | - |
| <b>onNodeDragEnd</b>(<i>fn</i>) | Callback function for the end of node drag interactions. This function is invoked when the node is released. The node object is included as the first argument, and the change in coordinates from the node's initial postion are included as the second argument in format {x,y,z}: `onNodeDragEnd(node, translate)`. | - |
| <b>onLinkClick</b>(<i>fn</i>) | Callback function for link (left-button) clicks. The link object and the event object are included as arguments `onLinkClick(link, event)`. | - |
| <b>onLinkRightClick</b>(<i>fn</i>) | Callback function for link right-clicks. The link object and the event object are included as arguments `onLinkRightClick(link, event)`. | - |
| <b>onLinkHover</b>(<i>fn</i>) | Callback function for link mouse over events. The link object (or `null` if there's no link under the mouse line of sight) is included as the first argument, and the previous link object (or null) as second argument: `onLinkHover(link, prevLink)`. | - |
| <b>linkHoverPrecision</b>([<i>int</i>]) | Whether to display the link label when hovering the link closely (low value) or from far away (high value). | 4 |
| <b>onBackgroundClick</b>(<i>fn</i>) | Callback function for click events on the empty space between the nodes and links. The event object is included as single argument `onBackgroundClick(event)`. | - |
| <b>onBackgroundRightClick</b>(<i>fn</i>) | Callback function for right-click events on the empty space between the nodes and links. The event object is included as single argument `onBackgroundRightClick(event)`. | - |
| <b>onZoom</b>(<i>fn</i>) | Callback function for zoom/pan events. The current zoom transform is included as single argument `onZoom({ k, x, y })`. Note that `onZoom` is triggered by user interaction as well as programmatic zooming/panning with `zoom()` and `centerAt()`. | - |
| <b>onZoomEnd</b>(<i>fn</i>) | Callback function for on 'end' of zoom/pan events. The current zoom transform is included as single argument `onZoomEnd({ k, x, y })`. Note that `onZoomEnd` is triggered by user interaction as well as programmatic zooming/panning with `zoom()` and `centerAt()`. | - |
| <b>nodePointerAreaPaint</b>([<i>fn</i>]) | Callback function for painting a canvas area used to detect node pointer interactions. The provided paint color uniquely identifies the node and should be used to perform drawing operations on the provided canvas context. This painted area will not be visible, but instead be used to detect pointer interactions with the node. The callback function has the signature: `.nodePointerAreaPaint(<node>, <color>, <canvas context>, <current global scale>)`. | *default interaction area is a circle centered on the node and sized according to `val`.* |
| <b>linkPointerAreaPaint</b>([<i>fn</i>]) | Callback function for painting a canvas area used to detect link pointer interactions. The provided paint color uniquely identifies the link and should be used to perform drawing operations on the provided canvas context. This painted area will not be visible, but instead be used to detect pointer interactions with the link. The callback function has the signature: `.linkPointerAreaPaint(<link>, <color>, <canvas context>, <current global scale>)`. | *default interaction area is a straight line between the source and target nodes.* |
| <b>enableNodeDrag</b>([<i>boolean</i>]) | Getter/setter for whether to enable the user interaction to drag nodes by click-dragging. If enabled, every time a node is dragged the simulation is re-heated so the other nodes react to the changes. Only applicable if enablePointerInteraction is `true`. | `true` |
| <b>enableZoomInteraction</b>([<i>boolean</i>]) | Getter/setter for whether to enable zooming user interactions. | `true` |
| <b>enablePanInteraction</b>([<i>boolean</i>]) | Getter/setter for whether to enable panning user interactions. | `true` |
| <b>enablePointerInteraction</b>([<i>boolean</i>]) | Getter/setter for whether to enable the mouse tracking events. This activates an internal tracker of the canvas mouse position and enables the functionality of object hover/click/drag and tooltip labels, at the cost of performance. If you're looking for maximum gain in your graph performance it's recommended to switch off this property. | `true` |
### Utility
| Method | Description |
| --- | --- |
| <b>getGraphBbox</b>([<i>nodeFilterFn</i>]) | Returns the current bounding box of the nodes in the graph, formatted as `{ x: [<num>, <num>], y: [<num>, <num>] }`. If no nodes are found, returns `null`. Accepts an optional argument to define a custom node filter: `node => <boolean>`, which should return a truthy value if the node is to be included. This can be useful to calculate the bounding box of a portion of the graph. |
| <b>screen2GraphCoords</b>(<i>x</i>, <i>y</i>) | Utility method to translate viewport coordinates to the graph domain. Given a pair of `x`,`y` screen coordinates, returns the current equivalent `{x, y}` in the domain of graph node coordinates. |
| <b>graph2ScreenCoords</b>(<i>x</i>, <i>y</i>) | Utility method to translate node coordinates to the viewport domain. Given a pair of `x`,`y` graph coordinates, returns the current equivalent `{x, y}` in viewport coordinates. |
### Input JSON syntax
```json
{
"nodes": [
{
"id": "id1",
"name": "name1",
"val": 1
},
{
"id": "id2",
"name": "name2",
"val": 10
},
...
],
"links": [
{
"source": "id1",
"target": "id2"
},
...
]
}
```
## Giving Back
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=L398E7PKP47E8&currency_code=USD&source=url) If this project has helped you and you'd like to contribute back, you can always [buy me a ☕](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=L398E7PKP47E8&currency_code=USD&source=url)!
[npm-img]: https://img.shields.io/npm/v/force-graph
[npm-url]: https://npmjs.org/package/force-graph
[build-size-img]: https://img.shields.io/bundlephobia/minzip/force-graph
[build-size-url]: https://bundlephobia.com/result?p=force-graph
[npm-downloads-img]: https://img.shields.io/npm/dt/force-graph
[npm-downloads-url]: https://www.npmtrends.com/force-graph

196
VISUALIZACION/node_modules/force-graph/dist/force-graph.d.ts generated vendored Executable file
View file

@ -0,0 +1,196 @@
interface GraphData {
nodes: NodeObject[];
links: LinkObject[];
}
interface NodeObject {
id?: string | number;
x?: number;
y?: number;
vx?: number;
vy?: number;
fx?: number;
fy?: number;
}
interface LinkObject {
source?: string | number | NodeObject;
target?: string | number | NodeObject;
}
type Accessor<In, Out> = Out | string | ((obj: In) => Out);
type NodeAccessor<T> = Accessor<NodeObject, T>;
type LinkAccessor<T> = Accessor<LinkObject, T>;
type CanvasCustomRenderMode = 'replace' | 'before' | 'after';
type CanvasCustomRenderModeFn<T> = (obj: T) => CanvasCustomRenderMode | any;
type CanvasCustomRenderFn<T> = (obj: T, canvasContext: CanvasRenderingContext2D, globalScale: number) => void;
type CanvasPointerAreaPaintFn<T> = (obj: T, paintColor: string, canvasContext: CanvasRenderingContext2D, globalScale: number) => void;
type DagMode = 'td' | 'bu' | 'lr' | 'rl' | 'radialout' | 'radialin';
interface ForceFn {
(alpha: number): void;
initialize?: (nodes: NodeObject[], ...args: any[]) => void;
[key: string]: any;
}
interface ForceGraphGenericInstance<ChainableInstance> {
(element: HTMLElement): ChainableInstance;
resetProps(): ChainableInstance;
_destructor(): void;
// Data input
graphData(): GraphData;
graphData(data: GraphData): ChainableInstance;
nodeId(): string;
nodeId(id: string): ChainableInstance;
linkSource(): string;
linkSource(source: string): ChainableInstance;
linkTarget(): string;
linkTarget(target: string): ChainableInstance;
// Container layout
width(): number;
width(width: number): ChainableInstance;
height(): number;
height(height: number): ChainableInstance;
backgroundColor(): string;
backgroundColor(color?: string): ChainableInstance;
// Node styling
nodeRelSize(): number;
nodeRelSize(size: number): ChainableInstance;
nodeVal(): NodeAccessor<number>;
nodeVal(valAccessor: NodeAccessor<number>): ChainableInstance;
nodeLabel(): NodeAccessor<string>;
nodeLabel(labelAccessor: NodeAccessor<string>): ChainableInstance;
nodeVisibility(): NodeAccessor<boolean>;
nodeVisibility(visibilityAccessor: NodeAccessor<boolean>): ChainableInstance;
nodeColor(): NodeAccessor<string>;
nodeColor(colorAccessor: NodeAccessor<string>): ChainableInstance;
nodeAutoColorBy(): NodeAccessor<string | null>;
nodeAutoColorBy(colorByAccessor: NodeAccessor<string | null>): ChainableInstance;
nodeCanvasObject(): CanvasCustomRenderFn<NodeObject>;
nodeCanvasObject(renderFn: CanvasCustomRenderFn<NodeObject>): ChainableInstance;
nodeCanvasObjectMode(): string | CanvasCustomRenderModeFn<NodeObject>;
nodeCanvasObjectMode(modeAccessor: string | CanvasCustomRenderModeFn<NodeObject>): ChainableInstance;
nodePointerAreaPaint(): CanvasPointerAreaPaintFn<NodeObject>;
nodePointerAreaPaint(renderFn: CanvasPointerAreaPaintFn<NodeObject>): ChainableInstance;
// Link styling
linkLabel(): LinkAccessor<string>;
linkLabel(labelAccessor: LinkAccessor<string>): ChainableInstance;
linkVisibility(): LinkAccessor<boolean>;
linkVisibility(visibilityAccessor: LinkAccessor<boolean>): ChainableInstance;
linkColor(): LinkAccessor<string>;
linkColor(colorAccessor: LinkAccessor<string>): ChainableInstance;
linkAutoColorBy(): LinkAccessor<string | null>;
linkAutoColorBy(colorByAccessor: LinkAccessor<string | null>): ChainableInstance;
linkLineDash(): LinkAccessor<number[] | null>;
linkLineDash(linkLineDashAccessor: LinkAccessor<number[] | null>): ChainableInstance;
linkWidth(): LinkAccessor<number>;
linkWidth(widthAccessor: LinkAccessor<number>): ChainableInstance;
linkCurvature(): LinkAccessor<number>;
linkCurvature(curvatureAccessor: LinkAccessor<number>): ChainableInstance;
linkCanvasObject(): CanvasCustomRenderFn<LinkObject>;
linkCanvasObject(renderFn: CanvasCustomRenderFn<LinkObject>): ChainableInstance;
linkCanvasObjectMode(): string | CanvasCustomRenderModeFn<LinkObject>;
linkCanvasObjectMode(modeAccessor: string | CanvasCustomRenderModeFn<LinkObject>): ChainableInstance;
linkDirectionalArrowLength(): LinkAccessor<number>;
linkDirectionalArrowLength(lengthAccessor: LinkAccessor<number>): ChainableInstance;
linkDirectionalArrowColor(): LinkAccessor<string>;
linkDirectionalArrowColor(colorAccessor: LinkAccessor<string>): ChainableInstance;
linkDirectionalArrowRelPos(): LinkAccessor<number>;
linkDirectionalArrowRelPos(fractionAccessor: LinkAccessor<number>): ChainableInstance;
linkDirectionalParticles(): LinkAccessor<number>;
linkDirectionalParticles(numParticlesAccessor: LinkAccessor<number>): ChainableInstance;
linkDirectionalParticleSpeed(): LinkAccessor<number>;
linkDirectionalParticleSpeed(relDistancePerFrameAccessor: LinkAccessor<number>): ChainableInstance;
linkDirectionalParticleWidth(): LinkAccessor<number>;
linkDirectionalParticleWidth(widthAccessor: LinkAccessor<number>): ChainableInstance;
linkDirectionalParticleColor(): LinkAccessor<string>;
linkDirectionalParticleColor(colorAccessor: LinkAccessor<string>): ChainableInstance;
emitParticle(link: LinkObject): ChainableInstance;
linkPointerAreaPaint(): CanvasPointerAreaPaintFn<LinkObject>;
linkPointerAreaPaint(renderFn: CanvasPointerAreaPaintFn<LinkObject>): ChainableInstance;
// Render control
autoPauseRedraw(): boolean;
autoPauseRedraw(enable?: boolean): ChainableInstance;
pauseAnimation(): ChainableInstance;
resumeAnimation(): ChainableInstance;
centerAt(): {x: number, y: number};
centerAt(x?: number, y?: number, durationMs?: number): ChainableInstance;
zoom(): number;
zoom(scale: number, durationMs?: number): ChainableInstance;
zoomToFit(durationMs?: number, padding?: number, nodeFilter?: (node: NodeObject) => boolean): ChainableInstance;
minZoom(): number;
minZoom(scale: number): ChainableInstance;
maxZoom(): number;
maxZoom(scale: number): ChainableInstance;
onRenderFramePre(callback: (canvasContext: CanvasRenderingContext2D, globalScale: number) => void): ChainableInstance;
onRenderFramePost(callback: (canvasContext: CanvasRenderingContext2D, globalScale: number) => void): ChainableInstance;
// Force engine (d3-force) configuration
dagMode(): DagMode | null;
dagMode(mode: DagMode | null): ChainableInstance;
dagLevelDistance(): number | null;
dagLevelDistance(distance: number): ChainableInstance;
dagNodeFilter(): (node: NodeObject) => boolean;
dagNodeFilter(filterFn: (node: NodeObject) => boolean): ChainableInstance;
onDagError(): (loopNodeIds: (string | number)[]) => void;
onDagError(errorHandleFn: (loopNodeIds: (string | number)[]) => void): ChainableInstance;
d3AlphaMin(): number;
d3AlphaMin(alphaMin: number): ChainableInstance;
d3AlphaDecay(): number;
d3AlphaDecay(alphaDecay: number): ChainableInstance;
d3VelocityDecay(): number;
d3VelocityDecay(velocityDecay: number): ChainableInstance;
d3Force(forceName: 'link' | 'charge' | 'center' | string): ForceFn | undefined;
d3Force(forceName: 'link' | 'charge' | 'center' | string, forceFn: ForceFn | null): ChainableInstance;
d3ReheatSimulation(): ChainableInstance;
warmupTicks(): number;
warmupTicks(ticks: number): ChainableInstance;
cooldownTicks(): number;
cooldownTicks(ticks: number): ChainableInstance;
cooldownTime(): number;
cooldownTime(milliseconds: number): ChainableInstance;
onEngineTick(callback: () => void): ChainableInstance;
onEngineStop(callback: () => void): ChainableInstance;
// Interaction
onNodeClick(callback: (node: NodeObject, event: MouseEvent) => void): ChainableInstance;
onNodeRightClick(callback: (node: NodeObject, event: MouseEvent) => void): ChainableInstance;
onNodeHover(callback: (node: NodeObject | null, previousNode: NodeObject | null) => void): ChainableInstance;
onNodeDrag(callback: (node: NodeObject, translate: { x: number, y: number }) => void): ChainableInstance;
onNodeDragEnd(callback: (node: NodeObject, translate: { x: number, y: number }) => void): ChainableInstance;
onLinkClick(callback: (link: LinkObject, event: MouseEvent) => void): ChainableInstance;
onLinkRightClick(callback: (link: LinkObject, event: MouseEvent) => void): ChainableInstance;
onLinkHover(callback: (link: LinkObject | null, previousLink: LinkObject | null) => void): ChainableInstance;
linkHoverPrecision(): number;
linkHoverPrecision(precision: number): ChainableInstance;
onBackgroundClick(callback: (event: MouseEvent) => void): ChainableInstance;
onBackgroundRightClick(callback: (event: MouseEvent) => void): ChainableInstance;
onZoom(callback: (transform: {k: number, x: number, y: number}) => void): ChainableInstance;
onZoomEnd(callback: (transform: {k: number, x: number, y: number}) => void): ChainableInstance;
enableNodeDrag(): boolean;
enableNodeDrag(enable: boolean): ChainableInstance;
enableZoomInteraction(): boolean;
enableZoomInteraction(enable: boolean): ChainableInstance;
enablePanInteraction(): boolean;
enablePanInteraction(enable: boolean): ChainableInstance;
enablePointerInteraction(): boolean;
enablePointerInteraction(enable?: boolean): ChainableInstance;
// Utility
getGraphBbox(nodeFilter?: (node: NodeObject) => boolean): { x: [number, number], y: [number, number] };
screen2GraphCoords(x: number, y: number): { x: number, y: number };
graph2ScreenCoords(x: number, y: number): { x: number, y: number };
}
type ForceGraphInstance = ForceGraphGenericInstance<ForceGraphInstance>;
declare function ForceGraph(): ForceGraphInstance;
export { CanvasCustomRenderFn, CanvasCustomRenderModeFn, CanvasPointerAreaPaintFn, ForceGraphGenericInstance, ForceGraphInstance, GraphData, LinkObject, NodeObject, ForceGraph as default };

12202
VISUALIZACION/node_modules/force-graph/dist/force-graph.js generated vendored Executable file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1674
VISUALIZACION/node_modules/force-graph/dist/force-graph.mjs generated vendored Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,34 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
// Random tree
const NODES = 300;
const GROUPS = 12;
const gData = {
nodes: [...Array(NODES).keys()].map(i => ({
id: i,
group: Math.ceil(Math.random() * GROUPS)
})),
links: [...Array(NODES).keys()]
.filter(id => id)
.map(id => ({
source: id,
target: Math.round(Math.random() * (id-1))
}))
}
const Graph = ForceGraph()
(document.getElementById('graph'))
.nodeAutoColorBy('group')
.linkAutoColorBy(d => gData.nodes[d.source].group)
.graphData(gData);
</script>
</body>

View file

@ -0,0 +1,29 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
// Random tree
const N = 300;
const gData = {
nodes: [...Array(N).keys()].map(i => ({ id: i })),
links: [...Array(N).keys()]
.filter(id => id)
.map(id => ({
source: id,
target: Math.round(Math.random() * (id-1))
}))
};
const Graph = ForceGraph()
(document.getElementById('graph'))
.linkDirectionalParticles(2)
.graphData(gData);
</script>
</body>

View file

@ -0,0 +1,108 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<br/>
<div style="text-align: center; color: silver">
<b>New node:</b> click on the canvas, <b>New link:</b> drag one node close enough to another one,
<b>Rename</b> node or link by clicking on it, <b>Remove</b> node or link by right-clicking on it
</div>
<div id="graph"></div>
<script>
let nodeIdCounter = 0, linkIdCounter = 0;
let nodes = [], links = [];
let dragSourceNode = null, interimLink = null;
const snapInDistance = 15;
const snapOutDistance = 40;
const updateGraphData = () => {
Graph.graphData({ nodes: nodes, links: links });
};
const distance = (node1, node2) => {
return Math.sqrt(Math.pow(node1.x - node2.x, 2) + Math.pow(node1.y - node2.y, 2));
};
const rename = (nodeOrLink, type) => {
let value = prompt('Name this ' + type + ':', nodeOrLink.name);
if (!value) {
return;
}
nodeOrLink.name = value;
updateGraphData();
};
const setInterimLink = (source, target) => {
let linkId = linkIdCounter ++;
interimLink = { id: linkId, source: source, target: target, name: 'link_' + linkId };
links.push(interimLink);
updateGraphData();
};
const removeLink = link => {
links.splice(links.indexOf(link), 1);
};
const removeInterimLinkWithoutAddingIt = () => {
removeLink(interimLink);
interimLink = null;
updateGraphData();
};
const removeNode = node => {
links.filter(link => link.source === node || link.target === node).forEach(link => removeLink(link));
nodes.splice(nodes.indexOf(node), 1);
};
const Graph = ForceGraph()
(document.getElementById('graph'))
.linkDirectionalArrowLength(6)
.linkDirectionalArrowRelPos(1)
.onNodeDrag(dragNode => {
dragSourceNode = dragNode;
for (let node of nodes) {
if (dragNode === node) {
continue;
}
// close enough: snap onto node as target for suggested link
if (!interimLink && distance(dragNode, node) < snapInDistance) {
setInterimLink(dragSourceNode, node);
}
// close enough to other node: snap over to other node as target for suggested link
if (interimLink && node !== interimLink.target && distance(dragNode, node) < snapInDistance) {
removeLink(interimLink);
setInterimLink(dragSourceNode, node);
}
}
// far away enough: snap out of the current target node
if (interimLink && distance(dragNode, interimLink.target) > snapOutDistance) {
removeInterimLinkWithoutAddingIt();
}
})
.onNodeDragEnd(() => {
dragSourceNode = null;
interimLink = null;
updateGraphData();
})
.nodeColor(node => node === dragSourceNode || (interimLink &&
(node === interimLink.source || node === interimLink.target)) ? 'orange' : null)
.linkColor(link => link === interimLink ? 'orange' : '#bbbbbb')
.linkLineDash(link => link === interimLink ? [2, 2] : [])
.onNodeClick((node, event) => rename(node, 'node'))
.onNodeRightClick((node, event) => removeNode(node))
.onLinkClick((link, event) => rename(link, 'link'))
.onLinkRightClick((link, event) => removeLink(link))
.onBackgroundClick(event => {
let coords = Graph.screen2GraphCoords(event.layerX, event.layerY);
let nodeId = nodeIdCounter ++;
nodes.push({ id: nodeId, x: coords.x, y: coords.y, name: 'node_' + nodeId });
updateGraphData();
});
updateGraphData();
</script>
</body>

View file

@ -0,0 +1,28 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
fetch('../datasets/miserables.json').then(res => res.json()).then(data => {
const elem = document.getElementById('graph');
const Graph = ForceGraph()(elem)
.graphData(data)
.nodeLabel('id')
.nodeAutoColorBy('group')
.linkDirectionalParticles(2)
.linkDirectionalParticleWidth(1.4)
.onNodeClick(node => {
// Center/zoom on node
Graph.centerAt(node.x, node.y, 1000);
Graph.zoom(8, 2000);
});
});
</script>
</body>

View file

@ -0,0 +1,50 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
<script src="//unpkg.com/d3-quadtree"></script>
<script src="//unpkg.com/d3-force"></script>
</head>
<body>
<div id="graph"></div>
<script>
const N = 80;
const nodes = [...Array(N).keys()].map(() => ({
// Initial velocity in random direction
vx: (Math.random() * 2) - 1,
vy: (Math.random() * 2) - 1
}));
const Graph = ForceGraph()
(document.getElementById('graph'));
Graph.cooldownTime(Infinity)
.d3AlphaDecay(0)
.d3VelocityDecay(0)
// Deactivate existing forces
.d3Force('center', null)
.d3Force('charge', null)
// Add collision and bounding box forces
.d3Force('collide', d3.forceCollide(Graph.nodeRelSize()))
.d3Force('box', () => {
const SQUARE_HALF_SIDE = Graph.nodeRelSize() * N * 0.5;
nodes.forEach(node => {
const x = node.x || 0, y = node.y || 0;
// bounce on box walls
if (Math.abs(x) > SQUARE_HALF_SIDE) { node.vx *= -1; }
if (Math.abs(y) > SQUARE_HALF_SIDE) { node.vy *= -1; }
});
})
// Add nodes
.graphData({ nodes, links: [] });
</script>
</body>

View file

@ -0,0 +1,76 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
const gData = {
nodes: [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }],
links: [
{ source: 0, target: 1 },
{ source: 0, target: 1 },
{ source: 1, target: 0 },
{ source: 1, target: 2 },
{ source: 2, target: 2 },
{ source: 2, target: 2 },
{ source: 2, target: 2 },
{ source: 2, target: 3 },
{ source: 3, target: 4 },
{ source: 4, target: 3 }
]
};
let selfLoopLinks = {};
let sameNodesLinks = {};
const curvatureMinMax = 0.5;
// 1. assign each link a nodePairId that combines their source and target independent of the links direction
// 2. group links together that share the same two nodes or are self-loops
gData.links.forEach(link => {
link.nodePairId = link.source <= link.target ? (link.source + "_" + link.target) : (link.target + "_" + link.source);
let map = link.source === link.target ? selfLoopLinks : sameNodesLinks;
if (!map[link.nodePairId]) {
map[link.nodePairId] = [];
}
map[link.nodePairId].push(link);
});
// Compute the curvature for self-loop links to avoid overlaps
Object.keys(selfLoopLinks).forEach(id => {
let links = selfLoopLinks[id];
let lastIndex = links.length - 1;
links[lastIndex].curvature = 1;
let delta = (1 - curvatureMinMax) / lastIndex;
for (let i = 0; i < lastIndex; i++) {
links[i].curvature = curvatureMinMax + i * delta;
}
});
// Compute the curvature for links sharing the same two nodes to avoid overlaps
Object.keys(sameNodesLinks).filter(nodePairId => sameNodesLinks[nodePairId].length > 1).forEach(nodePairId => {
let links = sameNodesLinks[nodePairId];
let lastIndex = links.length - 1;
let lastLink = links[lastIndex];
lastLink.curvature = curvatureMinMax;
let delta = 2 * curvatureMinMax / lastIndex;
for (let i = 0; i < lastIndex; i++) {
links[i].curvature = - curvatureMinMax + i * delta;
if (lastLink.source !== links[i].source) {
links[i].curvature *= -1; // flip it around, otherwise they overlap
}
}
});
const Graph = ForceGraph()
(document.getElementById('graph'))
.linkCurvature('curvature')
.linkDirectionalArrowLength(6)
.linkDirectionalArrowRelPos(1)
.graphData(gData);
</script>
</body>

View file

@ -0,0 +1,37 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
const gData = {
nodes: [...Array(9).keys()].map(i => ({ id: i })),
links: [
{ source: 1, target: 4, curvature: 0 },
{ source: 1, target: 4, curvature: 0.5 },
{ source: 1, target: 4, curvature: -0.5 },
{ source: 5, target: 2, curvature: 0.3 },
{ source: 2, target: 5, curvature: 0.3 },
{ source: 0, target: 3, curvature: 0 },
{ source: 3, target: 3, curvature: 0.5 },
{ source: 0, target: 4, curvature: 0.2 },
{ source: 4, target: 5, curvature: 0.5 },
{ source: 5, target: 6, curvature: 0.7 },
{ source: 6, target: 7, curvature: 1 },
{ source: 7, target: 8, curvature: 2 },
{ source: 8, target: 0, curvature: 0.5 }
]
};
const Graph = ForceGraph()
(document.getElementById('graph'))
.linkDirectionalParticles(2)
.linkCurvature('curvature')
.graphData(gData);
</script>
</body>

View file

@ -0,0 +1,44 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
// Random tree
const N = 20;
const gData = {
nodes: [...Array(N).keys()].map(i => ({ id: i })),
links: [...Array(N).keys()]
.filter(id => id)
.map(id => ({
source: id,
target: Math.round(Math.random() * (id-1))
}))
};
// gen a number persistent color from around the palette
const getColor = n => '#' + ((n * 1234567) % Math.pow(2, 24)).toString(16).padStart(6, '0');
const Graph = ForceGraph()
(document.getElementById('graph'))
.nodeCanvasObject((node, ctx) => nodePaint(node, getColor(node.id), ctx))
.nodePointerAreaPaint(nodePaint)
.nodeLabel('id')
.graphData(gData);
function nodePaint({ id, x, y }, color, ctx) {
ctx.fillStyle = color;
[
() => { ctx.fillRect(x - 6, y - 4, 12, 8); }, // rectangle
() => { ctx.beginPath(); ctx.moveTo(x, y - 5); ctx.lineTo(x - 5, y + 5); ctx.lineTo(x + 5, y + 5); ctx.fill(); }, // triangle
() => { ctx.beginPath(); ctx.arc(x, y, 5, 0, 2 * Math.PI, false); ctx.fill(); }, // circle
() => { ctx.font = '10px Sans-Serif'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText('Text', x, y); } // text
][id%4]();
}
</script>
</body>

View file

@ -0,0 +1,96 @@
<head>
<style> body { margin: 0; } </style>
<script src="//bundle.run/@yarnpkg/lockfile@1.1.0"></script>
<script src="//unpkg.com/dat.gui"></script>
<script src="//unpkg.com/d3-quadtree"></script>
<script src="//unpkg.com/d3-force"></script>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
// controls
const controls = { 'DAG Orientation': 'lr'};
const gui = new dat.GUI();
gui.add(controls, 'DAG Orientation', ['lr', 'td', 'radialout', null])
.onChange(orientation => graph && graph.dagMode(orientation));
// graph config
const graph = ForceGraph()
.backgroundColor('#101020')
.linkColor(() => 'rgba(255,255,255,0.2)')
.dagMode('lr')
.dagLevelDistance(300)
.nodeId('package')
.linkCurvature(d =>
0.07 * // max curvature
// curve outwards from source, using gradual straightening within a margin of a few px
(['td', 'bu'].includes(graph.dagMode())
? Math.max(-1, Math.min(1, (d.source.x - d.target.x) / 25)) :
['lr', 'rl'].includes(graph.dagMode())
? Math.max(-1, Math.min(1, (d.target.y - d.source.y) / 25))
: ['radialout', 'radialin'].includes(graph.dagMode()) ? 0 : 1
)
)
.linkDirectionalParticles(2)
.linkDirectionalParticleWidth(3)
.nodeCanvasObject((node, ctx) => {
const label = node.package;
const fontSize = 15;
ctx.font = `${fontSize}px Sans-Serif`;
const textWidth = ctx.measureText(label).width;
const bckgDimensions = [textWidth, fontSize].map(n => n + fontSize * 0.2); // some padding
ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
ctx.fillRect(node.x - bckgDimensions[0] / 2, node.y - bckgDimensions[1] / 2, ...bckgDimensions);
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = 'lightsteelblue';
ctx.fillText(label, node.x, node.y);
node.__bckgDimensions = bckgDimensions; // to re-use in nodePointerAreaPaint
})
.nodePointerAreaPaint((node, color, ctx) => {
ctx.fillStyle = color;
const bckgDimensions = node.__bckgDimensions;
bckgDimensions && ctx.fillRect(node.x - bckgDimensions[0] / 2, node.y - bckgDimensions[1] / 2, ...bckgDimensions);
})
.d3Force('collide', d3.forceCollide(13))
.d3AlphaDecay(0.02)
.d3VelocityDecay(0.3);
fetch('//unpkg.com/d3@5.9.7/yarn.lock')
.then(r => r.text())
.then(text => {
const yarnlock = _yarnpkg_lockfile.parse(text);
if (yarnlock.type !== 'success') throw new Error('invalid yarn.lock');
return yarnlock.object;
})
.then(yarnlock => {
const nodes = [];
const links = [];
Object.entries(yarnlock).forEach(([package, details]) => {
nodes.push({
package,
version: details.version
});
if (details.dependencies) {
Object.entries(details.dependencies).forEach(([dep, version]) => {
links.push({source: package, target: `${dep}@${version}`});
});
}
});
graph(document.getElementById('graph'))
.graphData({ nodes, links });
});
</script>
</body>

View file

@ -0,0 +1,119 @@
<head>
<style> body { margin: 0; } </style>
<script src="//bundle.run/@yarnpkg/lockfile@1.1.0"></script>
<script src="//unpkg.com/dagre/dist/dagre.min.js"></script>
<script src="//unpkg.com/accessor-fn"></script>
<script src="//unpkg.com/force-graph"></script>
<!-- <script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
const Graph = ForceGraph()(document.getElementById('graph'))
.nodeId('id')
.nodeLabel('id')
.cooldownTicks(0) // pre-defined layout, cancel force engine iterations
.linkDirectionalArrowLength(3)
.linkDirectionalArrowRelPos(1)
.linkCurvature(d =>
0.07 * // max curvature
// curve outwards from source, using gradual straightening within a margin of a few px
Math.max(-1, Math.min(1, (d.source.x - d.target.x) / 5)) *
Math.max(-1, Math.min(1, (d.target.y - d.source.y) / 5))
);
fetch('../../yarn.lock')
.then(r => r.text())
.then(text => {
const yarnlock = _yarnpkg_lockfile.parse(text);
if (yarnlock.type !== 'success') throw new Error('invalid yarn.lock');
return yarnlock.object;
})
.then(yarnlock => {
const nodes = [];
const links = [];
Object.entries(yarnlock).forEach(([package, details]) => {
nodes.push({ id: package });
if (details.dependencies) {
Object.entries(details.dependencies).forEach(([dep, version]) => {
links.push({source: package, target: `${dep}@${version}`});
});
}
});
return { nodes, links };
}).then(data => {
const nodeDiameter = Graph.nodeRelSize() * 2;
const layoutData = getLayout(data.nodes, data.links, {
nodeWidth: nodeDiameter,
nodeHeight: nodeDiameter,
nodesep: nodeDiameter * 0.5,
ranksep: nodeDiameter * Math.sqrt(data.nodes.length) * 0.6,
// root nodes aligned on top
rankDir: 'BT',
ranker: 'longest-path',
linkSource: 'target',
linkTarget: 'source'
});
layoutData.nodes.forEach(node => { node.fx = node.x; node.fy = node.y; }); // fix nodes
Graph.graphData(layoutData);
Graph.zoomToFit();
});
//
function getLayout(nodes, links, {
nodeId = 'id',
linkSource = 'source',
linkTarget = 'target',
nodeWidth = 0,
nodeHeight = 0,
...graphCfg
} = {}) {
const getNodeWidth = accessorFn(nodeWidth);
const getNodeHeight = accessorFn(nodeHeight);
const g = new dagre.graphlib.Graph();
g.setGraph({
// rankDir: 'LR',
// ranker: 'network-simplex' // 'tight-tree', 'longest-path'
// acyclicer: 'greedy'
nodesep: 5,
edgesep: 1,
ranksep: 20,
...graphCfg
});
nodes.forEach(node =>
g.setNode(
node[nodeId],
Object.assign({}, node, {
width: getNodeWidth(node),
height: getNodeHeight(node)
})
)
);
links.forEach(link =>
g.setEdge(link[linkSource], link[linkTarget], Object.assign({}, link))
);
dagre.layout(g);
return {
nodes: g.nodes().map(n => {
const node = g.node(n);
delete node.width;
delete node.height;
return node;
}),
links: g.edges().map(e => g.edge(e))
};
}
</script>
</body>

View file

@ -0,0 +1,47 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!-- <script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
// Random tree
const N = 100;
const gData = {
nodes: [...Array(N).keys()].map(i => ({ id: i })),
links: [...Array(N).keys()]
.filter(id => id)
.map(id => ({
source: id,
target: Math.round(Math.random() * (id-1)),
dashed: (id % 2 === 0)
}))
};
const elem = document.getElementById('graph');
const dashLen = 6;
const gapLen = 8;
const Graph = ForceGraph()(elem)
.graphData(gData)
.nodeRelSize(8)
.linkWidth(3)
.linkLineDash(link => link.dashed && [dashLen, gapLen]);
// Dash animation
const st = +new Date();
const dashAnimateTime = 300; // time to animate a single dash
(function animate() {
const t = ((+new Date() - st) % dashAnimateTime) / dashAnimateTime;
const lineDash = t < 0.5 ? [0, gapLen * t * 2, dashLen, gapLen * (1 - t * 2)] : [dashLen * (t - 0.5) * 2, gapLen, dashLen * (1 - (t - 0.5) * 2), 0];
Graph.linkLineDash(link => link.dashed && lineDash);
requestAnimationFrame(animate);
})(); // IIFE
</script>
</body>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,464 @@
size,path
,d3
,d3/d3-array
,d3/d3-array/threshold
,d3/d3-axis
,d3/d3-brush
,d3/d3-chord
,d3/d3-collection
,d3/d3-color
,d3/d3-dispatch
,d3/d3-drag
,d3/d3-dsv
,d3/d3-ease
,d3/d3-force
,d3/d3-format
,d3/d3-geo
,d3/d3-geo/clip
,d3/d3-geo/path
,d3/d3-geo/projection
,d3/d3-hierarchy
,d3/d3-hierarchy/hierarchy
,d3/d3-hierarchy/pack
,d3/d3-hierarchy/treemap
,d3/d3-interpolate
,d3/d3-interpolate/transform
,d3/d3-path
,d3/d3-polygon
,d3/d3-quadtree
,d3/d3-queue
,d3/d3-random
,d3/d3-request
,d3/d3-scale
,d3/d3-selection
,d3/d3-selection/selection
,d3/d3-shape
,d3/d3-shape/curve
,d3/d3-shape/offset
,d3/d3-shape/order
,d3/d3-shape/symbol
,d3/d3-time-format
,d3/d3-time
,d3/d3-timer
,d3/d3-transition
,d3/d3-transition/selection
,d3/d3-transition/transition
,d3/d3-voronoi
,d3/d3-zoom
90,d3/d3-array/array.js
86,d3/d3-array/ascending.js
238,d3/d3-array/bisect.js
786,d3/d3-array/bisector.js
72,d3/d3-array/constant.js
86,d3/d3-array/descending.js
135,d3/d3-array/deviation.js
553,d3/d3-array/extent.js
1876,d3/d3-array/histogram.js
43,d3/d3-array/identity.js
451,d3/d3-array/max.js
362,d3/d3-array/mean.js
452,d3/d3-array/median.js
339,d3/d3-array/merge.js
451,d3/d3-array/min.js
63,d3/d3-array/number.js
182,d3/d3-array/pairs.js
161,d3/d3-array/permute.js
416,d3/d3-array/quantile.js
344,d3/d3-array/range.js
357,d3/d3-array/scan.js
285,d3/d3-array/shuffle.js
295,d3/d3-array/sum.js
361,d3/d3-array/threshold/freedmanDiaconis.js
180,d3/d3-array/threshold/scott.js
96,d3/d3-array/threshold/sturges.js
672,d3/d3-array/ticks.js
356,d3/d3-array/transpose.js
540,d3/d3-array/variance.js
99,d3/d3-array/zip.js
42,d3/d3-axis/array.js
5239,d3/d3-axis/axis.js
43,d3/d3-axis/identity.js
15778,d3/d3-brush/brush.js
72,d3/d3-brush/constant.js
127,d3/d3-brush/event.js
202,d3/d3-brush/noevent.js
42,d3/d3-chord/array.js
3178,d3/d3-chord/chord.js
72,d3/d3-chord/constant.js
159,d3/d3-chord/math.js
2340,d3/d3-chord/ribbon.js
137,d3/d3-collection/entries.js
104,d3/d3-collection/keys.js
1988,d3/d3-collection/map.js
2021,d3/d3-collection/nest.js
800,d3/d3-collection/set.js
115,d3/d3-collection/values.js
9276,d3/d3-color/color.js
1855,d3/d3-color/cubehelix.js
340,d3/d3-color/define.js
3167,d3/d3-color/lab.js
72,d3/d3-color/math.js
2729,d3/d3-dispatch/dispatch.js
72,d3/d3-drag/constant.js
4297,d3/d3-drag/drag.js
430,d3/d3-drag/event.js
857,d3/d3-drag/nodrag.js
202,d3/d3-drag/noevent.js
199,d3/d3-dsv/csv.js
3582,d3/d3-dsv/dsv.js
200,d3/d3-dsv/tsv.js
653,d3/d3-ease/back.js
521,d3/d3-ease/bounce.js
261,d3/d3-ease/circle.js
210,d3/d3-ease/cubic.js
1309,d3/d3-ease/elastic.js
251,d3/d3-ease/exp.js
43,d3/d3-ease/linear.js
596,d3/d3-ease/poly.js
192,d3/d3-ease/quad.js
236,d3/d3-ease/sin.js
654,d3/d3-force/center.js
2447,d3/d3-force/collide.js
72,d3/d3-force/constant.js
69,d3/d3-force/jiggle.js
3213,d3/d3-force/link.js
3181,d3/d3-force/manyBody.js
3444,d3/d3-force/simulation.js
1030,d3/d3-force/x.js
1030,d3/d3-force/y.js
361,d3/d3-format/defaultLocale.js
134,d3/d3-format/exponent.js
656,d3/d3-format/formatDecimal.js
368,d3/d3-format/formatDefault.js
475,d3/d3-format/formatGroup.js
611,d3/d3-format/formatPrefixAuto.js
458,d3/d3-format/formatRounded.js
1589,d3/d3-format/formatSpecifier.js
846,d3/d3-format/formatTypes.js
5247,d3/d3-format/locale.js
119,d3/d3-format/precisionFixed.js
190,d3/d3-format/precisionPrefix.js
186,d3/d3-format/precisionRound.js
906,d3/d3-geo/adder.js
1958,d3/d3-geo/area.js
5361,d3/d3-geo/bounds.js
929,d3/d3-geo/cartesian.js
3816,d3/d3-geo/centroid.js
2373,d3/d3-geo/circle.js
2897,d3/d3-geo/clip/antimeridian.js
470,d3/d3-geo/clip/buffer.js
5956,d3/d3-geo/clip/circle.js
5527,d3/d3-geo/clip/extent.js
3813,d3/d3-geo/clip/index.js
1099,d3/d3-geo/clip/line.js
2802,d3/d3-geo/clip/polygon.js
250,d3/d3-geo/compose.js
72,d3/d3-geo/constant.js
229,d3/d3-geo/distance.js
3034,d3/d3-geo/graticule.js
43,d3/d3-geo/identity.js
911,d3/d3-geo/interpolate.js
1309,d3/d3-geo/length.js
880,d3/d3-geo/math.js
34,d3/d3-geo/noop.js
945,d3/d3-geo/path/area.js
485,d3/d3-geo/path/bounds.js
2033,d3/d3-geo/path/centroid.js
914,d3/d3-geo/path/context.js
1690,d3/d3-geo/path/index.js
1149,d3/d3-geo/path/string.js
139,d3/d3-geo/pointEqual.js
2491,d3/d3-geo/polygonContains.js
235,d3/d3-geo/projection/albers.js
3986,d3/d3-geo/projection/albersUsa.js
502,d3/d3-geo/projection/azimuthal.js
447,d3/d3-geo/projection/azimuthalEqualArea.js
443,d3/d3-geo/projection/azimuthalEquidistant.js
402,d3/d3-geo/projection/conic.js
1017,d3/d3-geo/projection/conicConformal.js
871,d3/d3-geo/projection/conicEqualArea.js
771,d3/d3-geo/projection/conicEquidistant.js
314,d3/d3-geo/projection/cylindricalEqualArea.js
253,d3/d3-geo/projection/equirectangular.js
910,d3/d3-geo/projection/fit.js
387,d3/d3-geo/projection/gnomonic.js
1922,d3/d3-geo/projection/identity.js
3752,d3/d3-geo/projection/index.js
1119,d3/d3-geo/projection/mercator.js
376,d3/d3-geo/projection/orthographic.js
3275,d3/d3-geo/projection/resample.js
436,d3/d3-geo/projection/stereographic.js
762,d3/d3-geo/projection/transverseMercator.js
2509,d3/d3-geo/rotation.js
2305,d3/d3-geo/stream.js
701,d3/d3-geo/transform.js
166,d3/d3-hierarchy/accessors.js
2093,d3/d3-hierarchy/cluster.js
120,d3/d3-hierarchy/constant.js
138,d3/d3-hierarchy/hierarchy/ancestors.js
121,d3/d3-hierarchy/hierarchy/descendants.js
381,d3/d3-hierarchy/hierarchy/each.js
353,d3/d3-hierarchy/hierarchy/eachAfter.js
282,d3/d3-hierarchy/hierarchy/eachBefore.js
1819,d3/d3-hierarchy/hierarchy/index.js
164,d3/d3-hierarchy/hierarchy/leaves.js
246,d3/d3-hierarchy/hierarchy/links.js
606,d3/d3-hierarchy/hierarchy/path.js
151,d3/d3-hierarchy/hierarchy/sort.js
264,d3/d3-hierarchy/hierarchy/sum.js
2452,d3/d3-hierarchy/pack/enclose.js
1917,d3/d3-hierarchy/pack/index.js
389,d3/d3-hierarchy/pack/shuffle.js
3497,d3/d3-hierarchy/pack/siblings.js
1266,d3/d3-hierarchy/partition.js
1934,d3/d3-hierarchy/stratify.js
7060,d3/d3-hierarchy/tree.js
1184,d3/d3-hierarchy/treemap/binary.js
309,d3/d3-hierarchy/treemap/dice.js
2810,d3/d3-hierarchy/treemap/index.js
1029,d3/d3-hierarchy/treemap/resquarify.js
166,d3/d3-hierarchy/treemap/round.js
309,d3/d3-hierarchy/treemap/slice.js
170,d3/d3-hierarchy/treemap/sliceDice.js
1868,d3/d3-hierarchy/treemap/squarify.js
372,d3/d3-interpolate/array.js
600,d3/d3-interpolate/basis.js
360,d3/d3-interpolate/basisClosed.js
697,d3/d3-interpolate/color.js
72,d3/d3-interpolate/constant.js
760,d3/d3-interpolate/cubehelix.js
134,d3/d3-interpolate/date.js
547,d3/d3-interpolate/hcl.js
547,d3/d3-interpolate/hsl.js
447,d3/d3-interpolate/lab.js
100,d3/d3-interpolate/number.js
390,d3/d3-interpolate/object.js
163,d3/d3-interpolate/quantize.js
1277,d3/d3-interpolate/rgb.js
112,d3/d3-interpolate/round.js
1758,d3/d3-interpolate/string.js
672,d3/d3-interpolate/transform/decompose.js
2064,d3/d3-interpolate/transform/index.js
980,d3/d3-interpolate/transform/parse.js
598,d3/d3-interpolate/value.js
1387,d3/d3-interpolate/zoom.js
4089,d3/d3-path/path.js
243,d3/d3-polygon/area.js
346,d3/d3-polygon/centroid.js
411,d3/d3-polygon/contains.js
402,d3/d3-polygon/cross.js
1710,d3/d3-polygon/hull.js
375,d3/d3-polygon/length.js
2441,d3/d3-quadtree/add.js
1667,d3/d3-quadtree/cover.js
170,d3/d3-quadtree/data.js
206,d3/d3-quadtree/extent.js
1696,d3/d3-quadtree/find.js
134,d3/d3-quadtree/quad.js
2077,d3/d3-quadtree/quadtree.js
1898,d3/d3-quadtree/remove.js
51,d3/d3-quadtree/root.js
155,d3/d3-quadtree/size.js
695,d3/d3-quadtree/visit.js
773,d3/d3-quadtree/visitAfter.js
138,d3/d3-quadtree/x.js
138,d3/d3-quadtree/y.js
29,d3/d3-queue/array.js
2870,d3/d3-queue/queue.js
168,d3/d3-random/bates.js
113,d3/d3-random/exponential.js
137,d3/d3-random/irwinHall.js
178,d3/d3-random/logNormal.js
503,d3/d3-random/normal.js
236,d3/d3-random/uniform.js
101,d3/d3-request/csv.js
517,d3/d3-request/dsv.js
157,d3/d3-request/html.js
127,d3/d3-request/json.js
4593,d3/d3-request/request.js
109,d3/d3-request/text.js
118,d3/d3-request/tsv.js
370,d3/d3-request/type.js
174,d3/d3-request/xml.js
90,d3/d3-scale/array.js
2637,d3/d3-scale/band.js
119,d3/d3-scale/category10.js
179,d3/d3-scale/category20.js
179,d3/d3-scale/category20b.js
179,d3/d3-scale/category20c.js
101,d3/d3-scale/colors.js
72,d3/d3-scale/constant.js
3328,d3/d3-scale/continuous.js
188,d3/d3-scale/cubehelix.js
463,d3/d3-scale/identity.js
1206,d3/d3-scale/linear.js
3273,d3/d3-scale/log.js
340,d3/d3-scale/nice.js
44,d3/d3-scale/number.js
1116,d3/d3-scale/ordinal.js
1000,d3/d3-scale/pow.js
1280,d3/d3-scale/quantile.js
1066,d3/d3-scale/quantize.js
536,d3/d3-scale/rainbow.js
717,d3/d3-scale/sequential.js
802,d3/d3-scale/threshold.js
1203,d3/d3-scale/tickFormat.js
4565,d3/d3-scale/time.js
379,d3/d3-scale/utcTime.js
6471,d3/d3-scale/viridis.js
72,d3/d3-selection/constant.js
662,d3/d3-selection/creator.js
536,d3/d3-selection/local.js
533,d3/d3-selection/matcher.js
224,d3/d3-selection/mouse.js
303,d3/d3-selection/namespace.js
254,d3/d3-selection/namespaces.js
448,d3/d3-selection/point.js
259,d3/d3-selection/select.js
282,d3/d3-selection/selectAll.js
235,d3/d3-selection/selection/append.js
1460,d3/d3-selection/selection/attr.js
134,d3/d3-selection/selection/call.js
1740,d3/d3-selection/selection/classed.js
3597,d3/d3-selection/selection/data.js
132,d3/d3-selection/selection/datum.js
869,d3/d3-selection/selection/dispatch.js
289,d3/d3-selection/selection/each.js
53,d3/d3-selection/selection/empty.js
792,d3/d3-selection/selection/enter.js
176,d3/d3-selection/selection/exit.js
546,d3/d3-selection/selection/filter.js
520,d3/d3-selection/selection/html.js
2216,d3/d3-selection/selection/index.js
468,d3/d3-selection/selection/insert.js
171,d3/d3-selection/selection/lower.js
575,d3/d3-selection/selection/merge.js
258,d3/d3-selection/selection/node.js
140,d3/d3-selection/selection/nodes.js
3119,d3/d3-selection/selection/on.js
367,d3/d3-selection/selection/order.js
617,d3/d3-selection/selection/property.js
138,d3/d3-selection/selection/raise.js
153,d3/d3-selection/selection/remove.js
653,d3/d3-selection/selection/select.js
550,d3/d3-selection/selection/selectAll.js
98,d3/d3-selection/selection/size.js
681,d3/d3-selection/selection/sort.js
71,d3/d3-selection/selection/sparse.js
889,d3/d3-selection/selection/style.js
528,d3/d3-selection/selection/text.js
152,d3/d3-selection/selector.js
171,d3/d3-selection/selectorAll.js
175,d3/d3-selection/sourceEvent.js
407,d3/d3-selection/touch.js
323,d3/d3-selection/touches.js
218,d3/d3-selection/window.js
8831,d3/d3-shape/arc.js
2917,d3/d3-shape/area.js
42,d3/d3-shape/array.js
81,d3/d3-shape/constant.js
1436,d3/d3-shape/curve/basis.js
1530,d3/d3-shape/curve/basisClosed.js
1069,d3/d3-shape/curve/basisOpen.js
1081,d3/d3-shape/curve/bundle.js
1633,d3/d3-shape/curve/cardinal.js
1605,d3/d3-shape/curve/cardinalClosed.js
1288,d3/d3-shape/curve/cardinalOpen.js
2637,d3/d3-shape/curve/catmullRom.js
2083,d3/d3-shape/curve/catmullRomClosed.js
1760,d3/d3-shape/curve/catmullRomOpen.js
738,d3/d3-shape/curve/linear.js
514,d3/d3-shape/curve/linearClosed.js
3203,d3/d3-shape/curve/monotone.js
1761,d3/d3-shape/curve/natural.js
655,d3/d3-shape/curve/radial.js
1367,d3/d3-shape/curve/step.js
86,d3/d3-shape/descending.js
43,d3/d3-shape/identity.js
1516,d3/d3-shape/line.js
106,d3/d3-shape/math.js
29,d3/d3-shape/noop.js
319,d3/d3-shape/offset/expand.js
310,d3/d3-shape/offset/none.js
314,d3/d3-shape/offset/silhouette.js
740,d3/d3-shape/offset/wiggle.js
305,d3/d3-shape/order/ascending.js
112,d3/d3-shape/order/descending.js
545,d3/d3-shape/order/insideOut.js
120,d3/d3-shape/order/none.js
97,d3/d3-shape/order/reverse.js
2336,d3/d3-shape/pie.js
81,d3/d3-shape/point.js
934,d3/d3-shape/radialArea.js
396,d3/d3-shape/radialLine.js
1432,d3/d3-shape/stack.js
186,d3/d3-shape/symbol/circle.js
476,d3/d3-shape/symbol/cross.js
307,d3/d3-shape/symbol/diamond.js
137,d3/d3-shape/symbol/square.js
609,d3/d3-shape/symbol/star.js
255,d3/d3-shape/symbol/triangle.js
733,d3/d3-shape/symbol/wye.js
1160,d3/d3-shape/symbol.js
867,d3/d3-time-format/defaultLocale.js
284,d3/d3-time-format/isoFormat.js
319,d3/d3-time-format/isoParse.js
13876,d3/d3-time-format/locale.js
462,d3/d3-time/day.js
164,d3/d3-time/duration.js
569,d3/d3-time/hour.js
1845,d3/d3-time/interval.js
668,d3/d3-time/millisecond.js
437,d3/d3-time/minute.js
414,d3/d3-time/month.js
440,d3/d3-time/second.js
397,d3/d3-time/utcDay.js
399,d3/d3-time/utcHour.js
412,d3/d3-time/utcMinute.js
453,d3/d3-time/utcMonth.js
979,d3/d3-time/utcWeek.js
808,d3/d3-time/utcYear.js
963,d3/d3-time/week.js
754,d3/d3-time/year.js
400,d3/d3-timer/interval.js
250,d3/d3-timer/timeout.js
2771,d3/d3-timer/timer.js
484,d3/d3-transition/active.js
665,d3/d3-transition/interrupt.js
245,d3/d3-transition/selection/index.js
138,d3/d3-transition/selection/interrupt.js
1090,d3/d3-transition/selection/transition.js
2473,d3/d3-transition/transition/attr.js
904,d3/d3-transition/transition/attrTween.js
510,d3/d3-transition/transition/delay.js
528,d3/d3-transition/transition/duration.js
348,d3/d3-transition/transition/ease.js
574,d3/d3-transition/transition/filter.js
1892,d3/d3-transition/transition/index.js
340,d3/d3-transition/transition/interpolate.js
653,d3/d3-transition/transition/merge.js
853,d3/d3-transition/transition/on.js
284,d3/d3-transition/transition/remove.js
4792,d3/d3-transition/transition/schedule.js
826,d3/d3-transition/transition/select.js
883,d3/d3-transition/transition/selectAll.js
174,d3/d3-transition/transition/selection.js
2119,d3/d3-transition/transition/style.js
607,d3/d3-transition/transition/styleTween.js
473,d3/d3-transition/transition/text.js
691,d3/d3-transition/transition/transition.js
2026,d3/d3-transition/transition/tween.js
4381,d3/d3-voronoi/Beach.js
4087,d3/d3-voronoi/Cell.js
1632,d3/d3-voronoi/Circle.js
72,d3/d3-voronoi/constant.js
3415,d3/d3-voronoi/Diagram.js
3634,d3/d3-voronoi/Edge.js
81,d3/d3-voronoi/point.js
5302,d3/d3-voronoi/RedBlackTree.js
1420,d3/d3-voronoi/voronoi.js
72,d3/d3-zoom/constant.js
137,d3/d3-zoom/event.js
202,d3/d3-zoom/noevent.js
1336,d3/d3-zoom/transform.js
12133,d3/d3-zoom/zoom.js
1 size path
2 d3
3 d3/d3-array
4 d3/d3-array/threshold
5 d3/d3-axis
6 d3/d3-brush
7 d3/d3-chord
8 d3/d3-collection
9 d3/d3-color
10 d3/d3-dispatch
11 d3/d3-drag
12 d3/d3-dsv
13 d3/d3-ease
14 d3/d3-force
15 d3/d3-format
16 d3/d3-geo
17 d3/d3-geo/clip
18 d3/d3-geo/path
19 d3/d3-geo/projection
20 d3/d3-hierarchy
21 d3/d3-hierarchy/hierarchy
22 d3/d3-hierarchy/pack
23 d3/d3-hierarchy/treemap
24 d3/d3-interpolate
25 d3/d3-interpolate/transform
26 d3/d3-path
27 d3/d3-polygon
28 d3/d3-quadtree
29 d3/d3-queue
30 d3/d3-random
31 d3/d3-request
32 d3/d3-scale
33 d3/d3-selection
34 d3/d3-selection/selection
35 d3/d3-shape
36 d3/d3-shape/curve
37 d3/d3-shape/offset
38 d3/d3-shape/order
39 d3/d3-shape/symbol
40 d3/d3-time-format
41 d3/d3-time
42 d3/d3-timer
43 d3/d3-transition
44 d3/d3-transition/selection
45 d3/d3-transition/transition
46 d3/d3-voronoi
47 d3/d3-zoom
48 90 d3/d3-array/array.js
49 86 d3/d3-array/ascending.js
50 238 d3/d3-array/bisect.js
51 786 d3/d3-array/bisector.js
52 72 d3/d3-array/constant.js
53 86 d3/d3-array/descending.js
54 135 d3/d3-array/deviation.js
55 553 d3/d3-array/extent.js
56 1876 d3/d3-array/histogram.js
57 43 d3/d3-array/identity.js
58 451 d3/d3-array/max.js
59 362 d3/d3-array/mean.js
60 452 d3/d3-array/median.js
61 339 d3/d3-array/merge.js
62 451 d3/d3-array/min.js
63 63 d3/d3-array/number.js
64 182 d3/d3-array/pairs.js
65 161 d3/d3-array/permute.js
66 416 d3/d3-array/quantile.js
67 344 d3/d3-array/range.js
68 357 d3/d3-array/scan.js
69 285 d3/d3-array/shuffle.js
70 295 d3/d3-array/sum.js
71 361 d3/d3-array/threshold/freedmanDiaconis.js
72 180 d3/d3-array/threshold/scott.js
73 96 d3/d3-array/threshold/sturges.js
74 672 d3/d3-array/ticks.js
75 356 d3/d3-array/transpose.js
76 540 d3/d3-array/variance.js
77 99 d3/d3-array/zip.js
78 42 d3/d3-axis/array.js
79 5239 d3/d3-axis/axis.js
80 43 d3/d3-axis/identity.js
81 15778 d3/d3-brush/brush.js
82 72 d3/d3-brush/constant.js
83 127 d3/d3-brush/event.js
84 202 d3/d3-brush/noevent.js
85 42 d3/d3-chord/array.js
86 3178 d3/d3-chord/chord.js
87 72 d3/d3-chord/constant.js
88 159 d3/d3-chord/math.js
89 2340 d3/d3-chord/ribbon.js
90 137 d3/d3-collection/entries.js
91 104 d3/d3-collection/keys.js
92 1988 d3/d3-collection/map.js
93 2021 d3/d3-collection/nest.js
94 800 d3/d3-collection/set.js
95 115 d3/d3-collection/values.js
96 9276 d3/d3-color/color.js
97 1855 d3/d3-color/cubehelix.js
98 340 d3/d3-color/define.js
99 3167 d3/d3-color/lab.js
100 72 d3/d3-color/math.js
101 2729 d3/d3-dispatch/dispatch.js
102 72 d3/d3-drag/constant.js
103 4297 d3/d3-drag/drag.js
104 430 d3/d3-drag/event.js
105 857 d3/d3-drag/nodrag.js
106 202 d3/d3-drag/noevent.js
107 199 d3/d3-dsv/csv.js
108 3582 d3/d3-dsv/dsv.js
109 200 d3/d3-dsv/tsv.js
110 653 d3/d3-ease/back.js
111 521 d3/d3-ease/bounce.js
112 261 d3/d3-ease/circle.js
113 210 d3/d3-ease/cubic.js
114 1309 d3/d3-ease/elastic.js
115 251 d3/d3-ease/exp.js
116 43 d3/d3-ease/linear.js
117 596 d3/d3-ease/poly.js
118 192 d3/d3-ease/quad.js
119 236 d3/d3-ease/sin.js
120 654 d3/d3-force/center.js
121 2447 d3/d3-force/collide.js
122 72 d3/d3-force/constant.js
123 69 d3/d3-force/jiggle.js
124 3213 d3/d3-force/link.js
125 3181 d3/d3-force/manyBody.js
126 3444 d3/d3-force/simulation.js
127 1030 d3/d3-force/x.js
128 1030 d3/d3-force/y.js
129 361 d3/d3-format/defaultLocale.js
130 134 d3/d3-format/exponent.js
131 656 d3/d3-format/formatDecimal.js
132 368 d3/d3-format/formatDefault.js
133 475 d3/d3-format/formatGroup.js
134 611 d3/d3-format/formatPrefixAuto.js
135 458 d3/d3-format/formatRounded.js
136 1589 d3/d3-format/formatSpecifier.js
137 846 d3/d3-format/formatTypes.js
138 5247 d3/d3-format/locale.js
139 119 d3/d3-format/precisionFixed.js
140 190 d3/d3-format/precisionPrefix.js
141 186 d3/d3-format/precisionRound.js
142 906 d3/d3-geo/adder.js
143 1958 d3/d3-geo/area.js
144 5361 d3/d3-geo/bounds.js
145 929 d3/d3-geo/cartesian.js
146 3816 d3/d3-geo/centroid.js
147 2373 d3/d3-geo/circle.js
148 2897 d3/d3-geo/clip/antimeridian.js
149 470 d3/d3-geo/clip/buffer.js
150 5956 d3/d3-geo/clip/circle.js
151 5527 d3/d3-geo/clip/extent.js
152 3813 d3/d3-geo/clip/index.js
153 1099 d3/d3-geo/clip/line.js
154 2802 d3/d3-geo/clip/polygon.js
155 250 d3/d3-geo/compose.js
156 72 d3/d3-geo/constant.js
157 229 d3/d3-geo/distance.js
158 3034 d3/d3-geo/graticule.js
159 43 d3/d3-geo/identity.js
160 911 d3/d3-geo/interpolate.js
161 1309 d3/d3-geo/length.js
162 880 d3/d3-geo/math.js
163 34 d3/d3-geo/noop.js
164 945 d3/d3-geo/path/area.js
165 485 d3/d3-geo/path/bounds.js
166 2033 d3/d3-geo/path/centroid.js
167 914 d3/d3-geo/path/context.js
168 1690 d3/d3-geo/path/index.js
169 1149 d3/d3-geo/path/string.js
170 139 d3/d3-geo/pointEqual.js
171 2491 d3/d3-geo/polygonContains.js
172 235 d3/d3-geo/projection/albers.js
173 3986 d3/d3-geo/projection/albersUsa.js
174 502 d3/d3-geo/projection/azimuthal.js
175 447 d3/d3-geo/projection/azimuthalEqualArea.js
176 443 d3/d3-geo/projection/azimuthalEquidistant.js
177 402 d3/d3-geo/projection/conic.js
178 1017 d3/d3-geo/projection/conicConformal.js
179 871 d3/d3-geo/projection/conicEqualArea.js
180 771 d3/d3-geo/projection/conicEquidistant.js
181 314 d3/d3-geo/projection/cylindricalEqualArea.js
182 253 d3/d3-geo/projection/equirectangular.js
183 910 d3/d3-geo/projection/fit.js
184 387 d3/d3-geo/projection/gnomonic.js
185 1922 d3/d3-geo/projection/identity.js
186 3752 d3/d3-geo/projection/index.js
187 1119 d3/d3-geo/projection/mercator.js
188 376 d3/d3-geo/projection/orthographic.js
189 3275 d3/d3-geo/projection/resample.js
190 436 d3/d3-geo/projection/stereographic.js
191 762 d3/d3-geo/projection/transverseMercator.js
192 2509 d3/d3-geo/rotation.js
193 2305 d3/d3-geo/stream.js
194 701 d3/d3-geo/transform.js
195 166 d3/d3-hierarchy/accessors.js
196 2093 d3/d3-hierarchy/cluster.js
197 120 d3/d3-hierarchy/constant.js
198 138 d3/d3-hierarchy/hierarchy/ancestors.js
199 121 d3/d3-hierarchy/hierarchy/descendants.js
200 381 d3/d3-hierarchy/hierarchy/each.js
201 353 d3/d3-hierarchy/hierarchy/eachAfter.js
202 282 d3/d3-hierarchy/hierarchy/eachBefore.js
203 1819 d3/d3-hierarchy/hierarchy/index.js
204 164 d3/d3-hierarchy/hierarchy/leaves.js
205 246 d3/d3-hierarchy/hierarchy/links.js
206 606 d3/d3-hierarchy/hierarchy/path.js
207 151 d3/d3-hierarchy/hierarchy/sort.js
208 264 d3/d3-hierarchy/hierarchy/sum.js
209 2452 d3/d3-hierarchy/pack/enclose.js
210 1917 d3/d3-hierarchy/pack/index.js
211 389 d3/d3-hierarchy/pack/shuffle.js
212 3497 d3/d3-hierarchy/pack/siblings.js
213 1266 d3/d3-hierarchy/partition.js
214 1934 d3/d3-hierarchy/stratify.js
215 7060 d3/d3-hierarchy/tree.js
216 1184 d3/d3-hierarchy/treemap/binary.js
217 309 d3/d3-hierarchy/treemap/dice.js
218 2810 d3/d3-hierarchy/treemap/index.js
219 1029 d3/d3-hierarchy/treemap/resquarify.js
220 166 d3/d3-hierarchy/treemap/round.js
221 309 d3/d3-hierarchy/treemap/slice.js
222 170 d3/d3-hierarchy/treemap/sliceDice.js
223 1868 d3/d3-hierarchy/treemap/squarify.js
224 372 d3/d3-interpolate/array.js
225 600 d3/d3-interpolate/basis.js
226 360 d3/d3-interpolate/basisClosed.js
227 697 d3/d3-interpolate/color.js
228 72 d3/d3-interpolate/constant.js
229 760 d3/d3-interpolate/cubehelix.js
230 134 d3/d3-interpolate/date.js
231 547 d3/d3-interpolate/hcl.js
232 547 d3/d3-interpolate/hsl.js
233 447 d3/d3-interpolate/lab.js
234 100 d3/d3-interpolate/number.js
235 390 d3/d3-interpolate/object.js
236 163 d3/d3-interpolate/quantize.js
237 1277 d3/d3-interpolate/rgb.js
238 112 d3/d3-interpolate/round.js
239 1758 d3/d3-interpolate/string.js
240 672 d3/d3-interpolate/transform/decompose.js
241 2064 d3/d3-interpolate/transform/index.js
242 980 d3/d3-interpolate/transform/parse.js
243 598 d3/d3-interpolate/value.js
244 1387 d3/d3-interpolate/zoom.js
245 4089 d3/d3-path/path.js
246 243 d3/d3-polygon/area.js
247 346 d3/d3-polygon/centroid.js
248 411 d3/d3-polygon/contains.js
249 402 d3/d3-polygon/cross.js
250 1710 d3/d3-polygon/hull.js
251 375 d3/d3-polygon/length.js
252 2441 d3/d3-quadtree/add.js
253 1667 d3/d3-quadtree/cover.js
254 170 d3/d3-quadtree/data.js
255 206 d3/d3-quadtree/extent.js
256 1696 d3/d3-quadtree/find.js
257 134 d3/d3-quadtree/quad.js
258 2077 d3/d3-quadtree/quadtree.js
259 1898 d3/d3-quadtree/remove.js
260 51 d3/d3-quadtree/root.js
261 155 d3/d3-quadtree/size.js
262 695 d3/d3-quadtree/visit.js
263 773 d3/d3-quadtree/visitAfter.js
264 138 d3/d3-quadtree/x.js
265 138 d3/d3-quadtree/y.js
266 29 d3/d3-queue/array.js
267 2870 d3/d3-queue/queue.js
268 168 d3/d3-random/bates.js
269 113 d3/d3-random/exponential.js
270 137 d3/d3-random/irwinHall.js
271 178 d3/d3-random/logNormal.js
272 503 d3/d3-random/normal.js
273 236 d3/d3-random/uniform.js
274 101 d3/d3-request/csv.js
275 517 d3/d3-request/dsv.js
276 157 d3/d3-request/html.js
277 127 d3/d3-request/json.js
278 4593 d3/d3-request/request.js
279 109 d3/d3-request/text.js
280 118 d3/d3-request/tsv.js
281 370 d3/d3-request/type.js
282 174 d3/d3-request/xml.js
283 90 d3/d3-scale/array.js
284 2637 d3/d3-scale/band.js
285 119 d3/d3-scale/category10.js
286 179 d3/d3-scale/category20.js
287 179 d3/d3-scale/category20b.js
288 179 d3/d3-scale/category20c.js
289 101 d3/d3-scale/colors.js
290 72 d3/d3-scale/constant.js
291 3328 d3/d3-scale/continuous.js
292 188 d3/d3-scale/cubehelix.js
293 463 d3/d3-scale/identity.js
294 1206 d3/d3-scale/linear.js
295 3273 d3/d3-scale/log.js
296 340 d3/d3-scale/nice.js
297 44 d3/d3-scale/number.js
298 1116 d3/d3-scale/ordinal.js
299 1000 d3/d3-scale/pow.js
300 1280 d3/d3-scale/quantile.js
301 1066 d3/d3-scale/quantize.js
302 536 d3/d3-scale/rainbow.js
303 717 d3/d3-scale/sequential.js
304 802 d3/d3-scale/threshold.js
305 1203 d3/d3-scale/tickFormat.js
306 4565 d3/d3-scale/time.js
307 379 d3/d3-scale/utcTime.js
308 6471 d3/d3-scale/viridis.js
309 72 d3/d3-selection/constant.js
310 662 d3/d3-selection/creator.js
311 536 d3/d3-selection/local.js
312 533 d3/d3-selection/matcher.js
313 224 d3/d3-selection/mouse.js
314 303 d3/d3-selection/namespace.js
315 254 d3/d3-selection/namespaces.js
316 448 d3/d3-selection/point.js
317 259 d3/d3-selection/select.js
318 282 d3/d3-selection/selectAll.js
319 235 d3/d3-selection/selection/append.js
320 1460 d3/d3-selection/selection/attr.js
321 134 d3/d3-selection/selection/call.js
322 1740 d3/d3-selection/selection/classed.js
323 3597 d3/d3-selection/selection/data.js
324 132 d3/d3-selection/selection/datum.js
325 869 d3/d3-selection/selection/dispatch.js
326 289 d3/d3-selection/selection/each.js
327 53 d3/d3-selection/selection/empty.js
328 792 d3/d3-selection/selection/enter.js
329 176 d3/d3-selection/selection/exit.js
330 546 d3/d3-selection/selection/filter.js
331 520 d3/d3-selection/selection/html.js
332 2216 d3/d3-selection/selection/index.js
333 468 d3/d3-selection/selection/insert.js
334 171 d3/d3-selection/selection/lower.js
335 575 d3/d3-selection/selection/merge.js
336 258 d3/d3-selection/selection/node.js
337 140 d3/d3-selection/selection/nodes.js
338 3119 d3/d3-selection/selection/on.js
339 367 d3/d3-selection/selection/order.js
340 617 d3/d3-selection/selection/property.js
341 138 d3/d3-selection/selection/raise.js
342 153 d3/d3-selection/selection/remove.js
343 653 d3/d3-selection/selection/select.js
344 550 d3/d3-selection/selection/selectAll.js
345 98 d3/d3-selection/selection/size.js
346 681 d3/d3-selection/selection/sort.js
347 71 d3/d3-selection/selection/sparse.js
348 889 d3/d3-selection/selection/style.js
349 528 d3/d3-selection/selection/text.js
350 152 d3/d3-selection/selector.js
351 171 d3/d3-selection/selectorAll.js
352 175 d3/d3-selection/sourceEvent.js
353 407 d3/d3-selection/touch.js
354 323 d3/d3-selection/touches.js
355 218 d3/d3-selection/window.js
356 8831 d3/d3-shape/arc.js
357 2917 d3/d3-shape/area.js
358 42 d3/d3-shape/array.js
359 81 d3/d3-shape/constant.js
360 1436 d3/d3-shape/curve/basis.js
361 1530 d3/d3-shape/curve/basisClosed.js
362 1069 d3/d3-shape/curve/basisOpen.js
363 1081 d3/d3-shape/curve/bundle.js
364 1633 d3/d3-shape/curve/cardinal.js
365 1605 d3/d3-shape/curve/cardinalClosed.js
366 1288 d3/d3-shape/curve/cardinalOpen.js
367 2637 d3/d3-shape/curve/catmullRom.js
368 2083 d3/d3-shape/curve/catmullRomClosed.js
369 1760 d3/d3-shape/curve/catmullRomOpen.js
370 738 d3/d3-shape/curve/linear.js
371 514 d3/d3-shape/curve/linearClosed.js
372 3203 d3/d3-shape/curve/monotone.js
373 1761 d3/d3-shape/curve/natural.js
374 655 d3/d3-shape/curve/radial.js
375 1367 d3/d3-shape/curve/step.js
376 86 d3/d3-shape/descending.js
377 43 d3/d3-shape/identity.js
378 1516 d3/d3-shape/line.js
379 106 d3/d3-shape/math.js
380 29 d3/d3-shape/noop.js
381 319 d3/d3-shape/offset/expand.js
382 310 d3/d3-shape/offset/none.js
383 314 d3/d3-shape/offset/silhouette.js
384 740 d3/d3-shape/offset/wiggle.js
385 305 d3/d3-shape/order/ascending.js
386 112 d3/d3-shape/order/descending.js
387 545 d3/d3-shape/order/insideOut.js
388 120 d3/d3-shape/order/none.js
389 97 d3/d3-shape/order/reverse.js
390 2336 d3/d3-shape/pie.js
391 81 d3/d3-shape/point.js
392 934 d3/d3-shape/radialArea.js
393 396 d3/d3-shape/radialLine.js
394 1432 d3/d3-shape/stack.js
395 186 d3/d3-shape/symbol/circle.js
396 476 d3/d3-shape/symbol/cross.js
397 307 d3/d3-shape/symbol/diamond.js
398 137 d3/d3-shape/symbol/square.js
399 609 d3/d3-shape/symbol/star.js
400 255 d3/d3-shape/symbol/triangle.js
401 733 d3/d3-shape/symbol/wye.js
402 1160 d3/d3-shape/symbol.js
403 867 d3/d3-time-format/defaultLocale.js
404 284 d3/d3-time-format/isoFormat.js
405 319 d3/d3-time-format/isoParse.js
406 13876 d3/d3-time-format/locale.js
407 462 d3/d3-time/day.js
408 164 d3/d3-time/duration.js
409 569 d3/d3-time/hour.js
410 1845 d3/d3-time/interval.js
411 668 d3/d3-time/millisecond.js
412 437 d3/d3-time/minute.js
413 414 d3/d3-time/month.js
414 440 d3/d3-time/second.js
415 397 d3/d3-time/utcDay.js
416 399 d3/d3-time/utcHour.js
417 412 d3/d3-time/utcMinute.js
418 453 d3/d3-time/utcMonth.js
419 979 d3/d3-time/utcWeek.js
420 808 d3/d3-time/utcYear.js
421 963 d3/d3-time/week.js
422 754 d3/d3-time/year.js
423 400 d3/d3-timer/interval.js
424 250 d3/d3-timer/timeout.js
425 2771 d3/d3-timer/timer.js
426 484 d3/d3-transition/active.js
427 665 d3/d3-transition/interrupt.js
428 245 d3/d3-transition/selection/index.js
429 138 d3/d3-transition/selection/interrupt.js
430 1090 d3/d3-transition/selection/transition.js
431 2473 d3/d3-transition/transition/attr.js
432 904 d3/d3-transition/transition/attrTween.js
433 510 d3/d3-transition/transition/delay.js
434 528 d3/d3-transition/transition/duration.js
435 348 d3/d3-transition/transition/ease.js
436 574 d3/d3-transition/transition/filter.js
437 1892 d3/d3-transition/transition/index.js
438 340 d3/d3-transition/transition/interpolate.js
439 653 d3/d3-transition/transition/merge.js
440 853 d3/d3-transition/transition/on.js
441 284 d3/d3-transition/transition/remove.js
442 4792 d3/d3-transition/transition/schedule.js
443 826 d3/d3-transition/transition/select.js
444 883 d3/d3-transition/transition/selectAll.js
445 174 d3/d3-transition/transition/selection.js
446 2119 d3/d3-transition/transition/style.js
447 607 d3/d3-transition/transition/styleTween.js
448 473 d3/d3-transition/transition/text.js
449 691 d3/d3-transition/transition/transition.js
450 2026 d3/d3-transition/transition/tween.js
451 4381 d3/d3-voronoi/Beach.js
452 4087 d3/d3-voronoi/Cell.js
453 1632 d3/d3-voronoi/Circle.js
454 72 d3/d3-voronoi/constant.js
455 3415 d3/d3-voronoi/Diagram.js
456 3634 d3/d3-voronoi/Edge.js
457 81 d3/d3-voronoi/point.js
458 5302 d3/d3-voronoi/RedBlackTree.js
459 1420 d3/d3-voronoi/voronoi.js
460 72 d3/d3-zoom/constant.js
461 137 d3/d3-zoom/event.js
462 202 d3/d3-zoom/noevent.js
463 1336 d3/d3-zoom/transform.js
464 12133 d3/d3-zoom/zoom.js

View file

@ -0,0 +1,337 @@
{
"nodes": [
{"id": "Myriel", "group": 1},
{"id": "Napoleon", "group": 1},
{"id": "Mlle.Baptistine", "group": 1},
{"id": "Mme.Magloire", "group": 1},
{"id": "CountessdeLo", "group": 1},
{"id": "Geborand", "group": 1},
{"id": "Champtercier", "group": 1},
{"id": "Cravatte", "group": 1},
{"id": "Count", "group": 1},
{"id": "OldMan", "group": 1},
{"id": "Labarre", "group": 2},
{"id": "Valjean", "group": 2},
{"id": "Marguerite", "group": 3},
{"id": "Mme.deR", "group": 2},
{"id": "Isabeau", "group": 2},
{"id": "Gervais", "group": 2},
{"id": "Tholomyes", "group": 3},
{"id": "Listolier", "group": 3},
{"id": "Fameuil", "group": 3},
{"id": "Blacheville", "group": 3},
{"id": "Favourite", "group": 3},
{"id": "Dahlia", "group": 3},
{"id": "Zephine", "group": 3},
{"id": "Fantine", "group": 3},
{"id": "Mme.Thenardier", "group": 4},
{"id": "Thenardier", "group": 4},
{"id": "Cosette", "group": 5},
{"id": "Javert", "group": 4},
{"id": "Fauchelevent", "group": 0},
{"id": "Bamatabois", "group": 2},
{"id": "Perpetue", "group": 3},
{"id": "Simplice", "group": 2},
{"id": "Scaufflaire", "group": 2},
{"id": "Woman1", "group": 2},
{"id": "Judge", "group": 2},
{"id": "Champmathieu", "group": 2},
{"id": "Brevet", "group": 2},
{"id": "Chenildieu", "group": 2},
{"id": "Cochepaille", "group": 2},
{"id": "Pontmercy", "group": 4},
{"id": "Boulatruelle", "group": 6},
{"id": "Eponine", "group": 4},
{"id": "Anzelma", "group": 4},
{"id": "Woman2", "group": 5},
{"id": "MotherInnocent", "group": 0},
{"id": "Gribier", "group": 0},
{"id": "Jondrette", "group": 7},
{"id": "Mme.Burgon", "group": 7},
{"id": "Gavroche", "group": 8},
{"id": "Gillenormand", "group": 5},
{"id": "Magnon", "group": 5},
{"id": "Mlle.Gillenormand", "group": 5},
{"id": "Mme.Pontmercy", "group": 5},
{"id": "Mlle.Vaubois", "group": 5},
{"id": "Lt.Gillenormand", "group": 5},
{"id": "Marius", "group": 8},
{"id": "BaronessT", "group": 5},
{"id": "Mabeuf", "group": 8},
{"id": "Enjolras", "group": 8},
{"id": "Combeferre", "group": 8},
{"id": "Prouvaire", "group": 8},
{"id": "Feuilly", "group": 8},
{"id": "Courfeyrac", "group": 8},
{"id": "Bahorel", "group": 8},
{"id": "Bossuet", "group": 8},
{"id": "Joly", "group": 8},
{"id": "Grantaire", "group": 8},
{"id": "MotherPlutarch", "group": 9},
{"id": "Gueulemer", "group": 4},
{"id": "Babet", "group": 4},
{"id": "Claquesous", "group": 4},
{"id": "Montparnasse", "group": 4},
{"id": "Toussaint", "group": 5},
{"id": "Child1", "group": 10},
{"id": "Child2", "group": 10},
{"id": "Brujon", "group": 4},
{"id": "Mme.Hucheloup", "group": 8}
],
"links": [
{"source": "Napoleon", "target": "Myriel", "value": 1},
{"source": "Mlle.Baptistine", "target": "Myriel", "value": 8},
{"source": "Mme.Magloire", "target": "Myriel", "value": 10},
{"source": "Mme.Magloire", "target": "Mlle.Baptistine", "value": 6},
{"source": "CountessdeLo", "target": "Myriel", "value": 1},
{"source": "Geborand", "target": "Myriel", "value": 1},
{"source": "Champtercier", "target": "Myriel", "value": 1},
{"source": "Cravatte", "target": "Myriel", "value": 1},
{"source": "Count", "target": "Myriel", "value": 2},
{"source": "OldMan", "target": "Myriel", "value": 1},
{"source": "Valjean", "target": "Labarre", "value": 1},
{"source": "Valjean", "target": "Mme.Magloire", "value": 3},
{"source": "Valjean", "target": "Mlle.Baptistine", "value": 3},
{"source": "Valjean", "target": "Myriel", "value": 5},
{"source": "Marguerite", "target": "Valjean", "value": 1},
{"source": "Mme.deR", "target": "Valjean", "value": 1},
{"source": "Isabeau", "target": "Valjean", "value": 1},
{"source": "Gervais", "target": "Valjean", "value": 1},
{"source": "Listolier", "target": "Tholomyes", "value": 4},
{"source": "Fameuil", "target": "Tholomyes", "value": 4},
{"source": "Fameuil", "target": "Listolier", "value": 4},
{"source": "Blacheville", "target": "Tholomyes", "value": 4},
{"source": "Blacheville", "target": "Listolier", "value": 4},
{"source": "Blacheville", "target": "Fameuil", "value": 4},
{"source": "Favourite", "target": "Tholomyes", "value": 3},
{"source": "Favourite", "target": "Listolier", "value": 3},
{"source": "Favourite", "target": "Fameuil", "value": 3},
{"source": "Favourite", "target": "Blacheville", "value": 4},
{"source": "Dahlia", "target": "Tholomyes", "value": 3},
{"source": "Dahlia", "target": "Listolier", "value": 3},
{"source": "Dahlia", "target": "Fameuil", "value": 3},
{"source": "Dahlia", "target": "Blacheville", "value": 3},
{"source": "Dahlia", "target": "Favourite", "value": 5},
{"source": "Zephine", "target": "Tholomyes", "value": 3},
{"source": "Zephine", "target": "Listolier", "value": 3},
{"source": "Zephine", "target": "Fameuil", "value": 3},
{"source": "Zephine", "target": "Blacheville", "value": 3},
{"source": "Zephine", "target": "Favourite", "value": 4},
{"source": "Zephine", "target": "Dahlia", "value": 4},
{"source": "Fantine", "target": "Tholomyes", "value": 3},
{"source": "Fantine", "target": "Listolier", "value": 3},
{"source": "Fantine", "target": "Fameuil", "value": 3},
{"source": "Fantine", "target": "Blacheville", "value": 3},
{"source": "Fantine", "target": "Favourite", "value": 4},
{"source": "Fantine", "target": "Dahlia", "value": 4},
{"source": "Fantine", "target": "Zephine", "value": 4},
{"source": "Fantine", "target": "Marguerite", "value": 2},
{"source": "Fantine", "target": "Valjean", "value": 9},
{"source": "Mme.Thenardier", "target": "Fantine", "value": 2},
{"source": "Mme.Thenardier", "target": "Valjean", "value": 7},
{"source": "Thenardier", "target": "Mme.Thenardier", "value": 13},
{"source": "Thenardier", "target": "Fantine", "value": 1},
{"source": "Thenardier", "target": "Valjean", "value": 12},
{"source": "Cosette", "target": "Mme.Thenardier", "value": 4},
{"source": "Cosette", "target": "Valjean", "value": 31},
{"source": "Cosette", "target": "Tholomyes", "value": 1},
{"source": "Cosette", "target": "Thenardier", "value": 1},
{"source": "Javert", "target": "Valjean", "value": 17},
{"source": "Javert", "target": "Fantine", "value": 5},
{"source": "Javert", "target": "Thenardier", "value": 5},
{"source": "Javert", "target": "Mme.Thenardier", "value": 1},
{"source": "Javert", "target": "Cosette", "value": 1},
{"source": "Fauchelevent", "target": "Valjean", "value": 8},
{"source": "Fauchelevent", "target": "Javert", "value": 1},
{"source": "Bamatabois", "target": "Fantine", "value": 1},
{"source": "Bamatabois", "target": "Javert", "value": 1},
{"source": "Bamatabois", "target": "Valjean", "value": 2},
{"source": "Perpetue", "target": "Fantine", "value": 1},
{"source": "Simplice", "target": "Perpetue", "value": 2},
{"source": "Simplice", "target": "Valjean", "value": 3},
{"source": "Simplice", "target": "Fantine", "value": 2},
{"source": "Simplice", "target": "Javert", "value": 1},
{"source": "Scaufflaire", "target": "Valjean", "value": 1},
{"source": "Woman1", "target": "Valjean", "value": 2},
{"source": "Woman1", "target": "Javert", "value": 1},
{"source": "Judge", "target": "Valjean", "value": 3},
{"source": "Judge", "target": "Bamatabois", "value": 2},
{"source": "Champmathieu", "target": "Valjean", "value": 3},
{"source": "Champmathieu", "target": "Judge", "value": 3},
{"source": "Champmathieu", "target": "Bamatabois", "value": 2},
{"source": "Brevet", "target": "Judge", "value": 2},
{"source": "Brevet", "target": "Champmathieu", "value": 2},
{"source": "Brevet", "target": "Valjean", "value": 2},
{"source": "Brevet", "target": "Bamatabois", "value": 1},
{"source": "Chenildieu", "target": "Judge", "value": 2},
{"source": "Chenildieu", "target": "Champmathieu", "value": 2},
{"source": "Chenildieu", "target": "Brevet", "value": 2},
{"source": "Chenildieu", "target": "Valjean", "value": 2},
{"source": "Chenildieu", "target": "Bamatabois", "value": 1},
{"source": "Cochepaille", "target": "Judge", "value": 2},
{"source": "Cochepaille", "target": "Champmathieu", "value": 2},
{"source": "Cochepaille", "target": "Brevet", "value": 2},
{"source": "Cochepaille", "target": "Chenildieu", "value": 2},
{"source": "Cochepaille", "target": "Valjean", "value": 2},
{"source": "Cochepaille", "target": "Bamatabois", "value": 1},
{"source": "Pontmercy", "target": "Thenardier", "value": 1},
{"source": "Boulatruelle", "target": "Thenardier", "value": 1},
{"source": "Eponine", "target": "Mme.Thenardier", "value": 2},
{"source": "Eponine", "target": "Thenardier", "value": 3},
{"source": "Anzelma", "target": "Eponine", "value": 2},
{"source": "Anzelma", "target": "Thenardier", "value": 2},
{"source": "Anzelma", "target": "Mme.Thenardier", "value": 1},
{"source": "Woman2", "target": "Valjean", "value": 3},
{"source": "Woman2", "target": "Cosette", "value": 1},
{"source": "Woman2", "target": "Javert", "value": 1},
{"source": "MotherInnocent", "target": "Fauchelevent", "value": 3},
{"source": "MotherInnocent", "target": "Valjean", "value": 1},
{"source": "Gribier", "target": "Fauchelevent", "value": 2},
{"source": "Mme.Burgon", "target": "Jondrette", "value": 1},
{"source": "Gavroche", "target": "Mme.Burgon", "value": 2},
{"source": "Gavroche", "target": "Thenardier", "value": 1},
{"source": "Gavroche", "target": "Javert", "value": 1},
{"source": "Gavroche", "target": "Valjean", "value": 1},
{"source": "Gillenormand", "target": "Cosette", "value": 3},
{"source": "Gillenormand", "target": "Valjean", "value": 2},
{"source": "Magnon", "target": "Gillenormand", "value": 1},
{"source": "Magnon", "target": "Mme.Thenardier", "value": 1},
{"source": "Mlle.Gillenormand", "target": "Gillenormand", "value": 9},
{"source": "Mlle.Gillenormand", "target": "Cosette", "value": 2},
{"source": "Mlle.Gillenormand", "target": "Valjean", "value": 2},
{"source": "Mme.Pontmercy", "target": "Mlle.Gillenormand", "value": 1},
{"source": "Mme.Pontmercy", "target": "Pontmercy", "value": 1},
{"source": "Mlle.Vaubois", "target": "Mlle.Gillenormand", "value": 1},
{"source": "Lt.Gillenormand", "target": "Mlle.Gillenormand", "value": 2},
{"source": "Lt.Gillenormand", "target": "Gillenormand", "value": 1},
{"source": "Lt.Gillenormand", "target": "Cosette", "value": 1},
{"source": "Marius", "target": "Mlle.Gillenormand", "value": 6},
{"source": "Marius", "target": "Gillenormand", "value": 12},
{"source": "Marius", "target": "Pontmercy", "value": 1},
{"source": "Marius", "target": "Lt.Gillenormand", "value": 1},
{"source": "Marius", "target": "Cosette", "value": 21},
{"source": "Marius", "target": "Valjean", "value": 19},
{"source": "Marius", "target": "Tholomyes", "value": 1},
{"source": "Marius", "target": "Thenardier", "value": 2},
{"source": "Marius", "target": "Eponine", "value": 5},
{"source": "Marius", "target": "Gavroche", "value": 4},
{"source": "BaronessT", "target": "Gillenormand", "value": 1},
{"source": "BaronessT", "target": "Marius", "value": 1},
{"source": "Mabeuf", "target": "Marius", "value": 1},
{"source": "Mabeuf", "target": "Eponine", "value": 1},
{"source": "Mabeuf", "target": "Gavroche", "value": 1},
{"source": "Enjolras", "target": "Marius", "value": 7},
{"source": "Enjolras", "target": "Gavroche", "value": 7},
{"source": "Enjolras", "target": "Javert", "value": 6},
{"source": "Enjolras", "target": "Mabeuf", "value": 1},
{"source": "Enjolras", "target": "Valjean", "value": 4},
{"source": "Combeferre", "target": "Enjolras", "value": 15},
{"source": "Combeferre", "target": "Marius", "value": 5},
{"source": "Combeferre", "target": "Gavroche", "value": 6},
{"source": "Combeferre", "target": "Mabeuf", "value": 2},
{"source": "Prouvaire", "target": "Gavroche", "value": 1},
{"source": "Prouvaire", "target": "Enjolras", "value": 4},
{"source": "Prouvaire", "target": "Combeferre", "value": 2},
{"source": "Feuilly", "target": "Gavroche", "value": 2},
{"source": "Feuilly", "target": "Enjolras", "value": 6},
{"source": "Feuilly", "target": "Prouvaire", "value": 2},
{"source": "Feuilly", "target": "Combeferre", "value": 5},
{"source": "Feuilly", "target": "Mabeuf", "value": 1},
{"source": "Feuilly", "target": "Marius", "value": 1},
{"source": "Courfeyrac", "target": "Marius", "value": 9},
{"source": "Courfeyrac", "target": "Enjolras", "value": 17},
{"source": "Courfeyrac", "target": "Combeferre", "value": 13},
{"source": "Courfeyrac", "target": "Gavroche", "value": 7},
{"source": "Courfeyrac", "target": "Mabeuf", "value": 2},
{"source": "Courfeyrac", "target": "Eponine", "value": 1},
{"source": "Courfeyrac", "target": "Feuilly", "value": 6},
{"source": "Courfeyrac", "target": "Prouvaire", "value": 3},
{"source": "Bahorel", "target": "Combeferre", "value": 5},
{"source": "Bahorel", "target": "Gavroche", "value": 5},
{"source": "Bahorel", "target": "Courfeyrac", "value": 6},
{"source": "Bahorel", "target": "Mabeuf", "value": 2},
{"source": "Bahorel", "target": "Enjolras", "value": 4},
{"source": "Bahorel", "target": "Feuilly", "value": 3},
{"source": "Bahorel", "target": "Prouvaire", "value": 2},
{"source": "Bahorel", "target": "Marius", "value": 1},
{"source": "Bossuet", "target": "Marius", "value": 5},
{"source": "Bossuet", "target": "Courfeyrac", "value": 12},
{"source": "Bossuet", "target": "Gavroche", "value": 5},
{"source": "Bossuet", "target": "Bahorel", "value": 4},
{"source": "Bossuet", "target": "Enjolras", "value": 10},
{"source": "Bossuet", "target": "Feuilly", "value": 6},
{"source": "Bossuet", "target": "Prouvaire", "value": 2},
{"source": "Bossuet", "target": "Combeferre", "value": 9},
{"source": "Bossuet", "target": "Mabeuf", "value": 1},
{"source": "Bossuet", "target": "Valjean", "value": 1},
{"source": "Joly", "target": "Bahorel", "value": 5},
{"source": "Joly", "target": "Bossuet", "value": 7},
{"source": "Joly", "target": "Gavroche", "value": 3},
{"source": "Joly", "target": "Courfeyrac", "value": 5},
{"source": "Joly", "target": "Enjolras", "value": 5},
{"source": "Joly", "target": "Feuilly", "value": 5},
{"source": "Joly", "target": "Prouvaire", "value": 2},
{"source": "Joly", "target": "Combeferre", "value": 5},
{"source": "Joly", "target": "Mabeuf", "value": 1},
{"source": "Joly", "target": "Marius", "value": 2},
{"source": "Grantaire", "target": "Bossuet", "value": 3},
{"source": "Grantaire", "target": "Enjolras", "value": 3},
{"source": "Grantaire", "target": "Combeferre", "value": 1},
{"source": "Grantaire", "target": "Courfeyrac", "value": 2},
{"source": "Grantaire", "target": "Joly", "value": 2},
{"source": "Grantaire", "target": "Gavroche", "value": 1},
{"source": "Grantaire", "target": "Bahorel", "value": 1},
{"source": "Grantaire", "target": "Feuilly", "value": 1},
{"source": "Grantaire", "target": "Prouvaire", "value": 1},
{"source": "MotherPlutarch", "target": "Mabeuf", "value": 3},
{"source": "Gueulemer", "target": "Thenardier", "value": 5},
{"source": "Gueulemer", "target": "Valjean", "value": 1},
{"source": "Gueulemer", "target": "Mme.Thenardier", "value": 1},
{"source": "Gueulemer", "target": "Javert", "value": 1},
{"source": "Gueulemer", "target": "Gavroche", "value": 1},
{"source": "Gueulemer", "target": "Eponine", "value": 1},
{"source": "Babet", "target": "Thenardier", "value": 6},
{"source": "Babet", "target": "Gueulemer", "value": 6},
{"source": "Babet", "target": "Valjean", "value": 1},
{"source": "Babet", "target": "Mme.Thenardier", "value": 1},
{"source": "Babet", "target": "Javert", "value": 2},
{"source": "Babet", "target": "Gavroche", "value": 1},
{"source": "Babet", "target": "Eponine", "value": 1},
{"source": "Claquesous", "target": "Thenardier", "value": 4},
{"source": "Claquesous", "target": "Babet", "value": 4},
{"source": "Claquesous", "target": "Gueulemer", "value": 4},
{"source": "Claquesous", "target": "Valjean", "value": 1},
{"source": "Claquesous", "target": "Mme.Thenardier", "value": 1},
{"source": "Claquesous", "target": "Javert", "value": 1},
{"source": "Claquesous", "target": "Eponine", "value": 1},
{"source": "Claquesous", "target": "Enjolras", "value": 1},
{"source": "Montparnasse", "target": "Javert", "value": 1},
{"source": "Montparnasse", "target": "Babet", "value": 2},
{"source": "Montparnasse", "target": "Gueulemer", "value": 2},
{"source": "Montparnasse", "target": "Claquesous", "value": 2},
{"source": "Montparnasse", "target": "Valjean", "value": 1},
{"source": "Montparnasse", "target": "Gavroche", "value": 1},
{"source": "Montparnasse", "target": "Eponine", "value": 1},
{"source": "Montparnasse", "target": "Thenardier", "value": 1},
{"source": "Toussaint", "target": "Cosette", "value": 2},
{"source": "Toussaint", "target": "Javert", "value": 1},
{"source": "Toussaint", "target": "Valjean", "value": 1},
{"source": "Child1", "target": "Gavroche", "value": 2},
{"source": "Child2", "target": "Gavroche", "value": 2},
{"source": "Child2", "target": "Child1", "value": 3},
{"source": "Brujon", "target": "Babet", "value": 3},
{"source": "Brujon", "target": "Gueulemer", "value": 3},
{"source": "Brujon", "target": "Thenardier", "value": 3},
{"source": "Brujon", "target": "Gavroche", "value": 1},
{"source": "Brujon", "target": "Eponine", "value": 1},
{"source": "Brujon", "target": "Claquesous", "value": 1},
{"source": "Brujon", "target": "Montparnasse", "value": 1},
{"source": "Mme.Hucheloup", "target": "Bossuet", "value": 1},
{"source": "Mme.Hucheloup", "target": "Joly", "value": 1},
{"source": "Mme.Hucheloup", "target": "Grantaire", "value": 1},
{"source": "Mme.Hucheloup", "target": "Bahorel", "value": 1},
{"source": "Mme.Hucheloup", "target": "Courfeyrac", "value": 1},
{"source": "Mme.Hucheloup", "target": "Gavroche", "value": 1},
{"source": "Mme.Hucheloup", "target": "Enjolras", "value": 1}
]
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
// Random tree
const N = 40;
const gData = {
nodes: [...Array(N).keys()].map(i => ({ id: i })),
links: [...Array(N).keys()]
.filter(id => id)
.map(id => ({
source: id,
target: Math.round(Math.random() * (id-1))
}))
};
const Graph = ForceGraph()
(document.getElementById('graph'))
.graphData(gData)
.linkDirectionalArrowLength(6);
</script>
</body>

View file

@ -0,0 +1,22 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
fetch('../datasets/miserables.json').then(res => res.json()).then(data => {
const Graph = ForceGraph()
(document.getElementById('graph'))
.graphData(data)
.nodeLabel('id')
.nodeAutoColorBy('group')
.linkDirectionalParticles("value")
.linkDirectionalParticleSpeed(d => d.value * 0.001);
});
</script>
</body>

View file

@ -0,0 +1,42 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
const initData = {
nodes: [ {id: 0 } ],
links: []
};
const elem = document.getElementById("graph");
const Graph = ForceGraph()(elem)
.onNodeClick(removeNode)
.graphData(initData);
setInterval(() => {
const { nodes, links } = Graph.graphData();
const id = nodes.length;
Graph.graphData({
nodes: [...nodes, { id }],
links: [...links, { source: id, target: Math.round(Math.random() * (id-1)) }]
});
}, 1000);
//
function removeNode(node) {
let { nodes, links } = Graph.graphData();
links = links.filter(l => l.source !== node && l.target !== node); // Remove links attached to node
nodes.splice(node.id, 1); // Remove node
nodes.forEach((n, idx) => { n.id = idx; }); // Reset node ids to array index
Graph.graphData({ nodes, links });
}
</script>
</body>

View file

@ -0,0 +1,50 @@
<head>
<style>
body { margin: 0; }
#emit-particles-btn {
position: absolute;
top: 10px;
right: 10px;
font-size: 13px;
}
</style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<button id="emit-particles-btn">Emit 10 Random Particles</button>
<script>
// Random tree
const N = 50;
const links = [...Array(N).keys()]
.filter(id => id)
.map(id => ({
source: id,
target: Math.round(Math.random() * (id-1))
}));
const gData = {
nodes: [...Array(N).keys()].map(i => ({ id: i })),
links
};
const Graph = ForceGraph()
(document.getElementById('graph'))
.linkDirectionalParticleColor(() => 'red')
.linkHoverPrecision(10)
.graphData(gData);
Graph.onLinkClick(Graph.emitParticle); // emit particles on link click
document.getElementById('emit-particles-btn').addEventListener('click', () => {
[...Array(10).keys()].forEach(() => {
const link = links[Math.floor(Math.random() * links.length)];
Graph.emitParticle(link);
});
});
</script>
</body>

View file

@ -0,0 +1,66 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!-- <script src="../../dist/force-graph.js"></script>-->
<style>
.clickable { cursor: unset !important }
</style>
</head>
<body>
<div id="graph"></div>
<script>
const rootId = 0;
// Random tree
const N = 300;
const gData = {
nodes: [...Array(N).keys()].map(i => ({ id: i, collapsed: i !== rootId, childLinks: [] })),
links: [...Array(N).keys()]
.filter(id => id)
.map(id => ({
source: Math.round(Math.random() * (id - 1)),
target: id
}))
};
// link parent/children
const nodesById = Object.fromEntries(gData.nodes.map(node => [node.id, node]));
gData.links.forEach(link => {
nodesById[link.source].childLinks.push(link);
});
const getPrunedTree = () => {
const visibleNodes = [];
const visibleLinks = [];
(function traverseTree(node = nodesById[rootId]) {
visibleNodes.push(node);
if (node.collapsed) return;
visibleLinks.push(...node.childLinks);
node.childLinks
.map(link => ((typeof link.target) === 'object') ? link.target : nodesById[link.target]) // get child node
.forEach(traverseTree);
})(); // IIFE
return { nodes: visibleNodes, links: visibleLinks };
};
const elem = document.getElementById('graph');
const Graph = ForceGraph()(elem)
.graphData(getPrunedTree())
.onNodeHover(node => elem.style.cursor = node && node.childLinks.length ? 'pointer' : null)
.onNodeClick(node => {
if (node.childLinks.length) {
node.collapsed = !node.collapsed; // toggle collapse state
Graph.graphData(getPrunedTree());
}
})
.linkDirectionalParticles(1)
.linkDirectionalParticleWidth(2.5)
.nodeColor(node => !node.childLinks.length ? 'green' : node.collapsed ? 'red' : 'yellow');
</script>
</body>

View file

@ -0,0 +1,85 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/d3-dsv"></script>
<script src="//unpkg.com/d3-quadtree"></script>
<script src="//unpkg.com/d3-force"></script>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
<style>
.clickable { cursor: unset !important }
</style>
</head>
<body>
<div id="graph"></div>
<script>
fetch('../datasets/d3-dependencies.csv').then(r => r.text()).then(d3.csvParse).then(data => {
const nodes = [], links = [];
data.forEach(({ size, path }) => {
const levels = path.split('/'),
level = levels.length - 1,
module = level > 0 ? levels[1] : null,
leaf = levels.pop(),
parent = levels.join('/');
const node = {
id: path,
leaf,
module,
collapsed: level > 0,
childLinks: []
};
nodes.push(node);
if (parent) {
links.push({ source: parent, target: path });
}
});
const rootId = 'd3';
// link parent/children
const nodesById = Object.fromEntries(nodes.map(node => [node.id, node]));
links.forEach(link => {
nodesById[link.source].childLinks.push(link);
});
const getPrunedTree = () => {
const visibleNodes = [];
const visibleLinks = [];
(function traverseTree(node = nodesById[rootId]) {
visibleNodes.push(node);
if (node.collapsed) return;
visibleLinks.push(...node.childLinks);
node.childLinks
.map(link => ((typeof link.target) === 'object') ? link.target : nodesById[link.target]) // get child node
.forEach(traverseTree);
})(); // IIFE
return { nodes: visibleNodes, links: visibleLinks };
};
const elem = document.getElementById('graph');
const Graph = ForceGraph()
(elem)
.graphData(getPrunedTree())
.nodeLabel('id')
.nodeColor(node => !node.childLinks.length ? 'green' : node.collapsed ? 'red' : 'yellow')
.onNodeHover(node => elem.style.cursor = node && node.childLinks.length ? 'pointer' : null)
.onNodeClick(node => {
node.collapsed = !node.collapsed; // toggle collapse state
Graph.graphData(getPrunedTree());
})
.dagMode('td')
.dagLevelDistance(90)
.d3Force('collision', d3.forceCollide(node => Graph.nodeRelSize()))
.warmupTicks(250);
});
</script>
</body>

View file

@ -0,0 +1,34 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!-- <script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
// Random tree
const N = 100;
const gData = {
nodes: [...Array(N).keys()].map(i => ({ id: i })),
links: [...Array(N).keys()]
.filter(id => id)
.map(id => ({
source: id,
target: Math.round(Math.random() * (id-1))
}))
};
const Graph = ForceGraph()
(document.getElementById('graph'))
.cooldownTicks(100)
.graphData(gData);
Graph.d3Force('center', null);
// fit to canvas when engine stops
Graph.onEngineStop(() => Graph.zoomToFit(400));
</script>
</body>

View file

@ -0,0 +1,24 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
fetch('../datasets/miserables.json').then(res => res.json()).then(data => {
const Graph = ForceGraph()
(document.getElementById('graph'))
.graphData(data)
.nodeLabel('id')
.nodeAutoColorBy('group')
.onNodeDragEnd(node => {
node.fx = node.x;
node.fy = node.y;
});
});
</script>
</body>

View file

@ -0,0 +1,84 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
// Random tree
const N = 80;
const gData = {
nodes: [...Array(N).keys()].map(i => ({ id: i })),
links: [...Array(N).keys()]
.filter(id => id)
.map(id => ({
source: id,
target: Math.round(Math.random() * (id-1))
}))
};
// cross-link node objects
gData.links.forEach(link => {
const a = gData.nodes[link.source];
const b = gData.nodes[link.target];
!a.neighbors && (a.neighbors = []);
!b.neighbors && (b.neighbors = []);
a.neighbors.push(b);
b.neighbors.push(a);
!a.links && (a.links = []);
!b.links && (b.links = []);
a.links.push(link);
b.links.push(link);
});
const NODE_R = 8;
const highlightNodes = new Set();
const highlightLinks = new Set();
let hoverNode = null;
const elem = document.getElementById('graph');
ForceGraph()(elem)
.graphData(gData)
.nodeRelSize(NODE_R)
.onNodeHover(node => {
highlightNodes.clear();
highlightLinks.clear();
if (node) {
highlightNodes.add(node);
node.neighbors.forEach(neighbor => highlightNodes.add(neighbor));
node.links.forEach(link => highlightLinks.add(link));
}
hoverNode = node || null;
})
.onLinkHover(link => {
highlightNodes.clear();
highlightLinks.clear();
if (link) {
highlightLinks.add(link);
highlightNodes.add(link.source);
highlightNodes.add(link.target);
}
})
.autoPauseRedraw(false) // keep redrawing after engine has stopped
.linkWidth(link => highlightLinks.has(link) ? 5 : 1)
.linkDirectionalParticles(4)
.linkDirectionalParticleWidth(link => highlightLinks.has(link) ? 4 : 0)
.nodeCanvasObjectMode(node => highlightNodes.has(node) ? 'before' : undefined)
.nodeCanvasObject((node, ctx) => {
// add ring just for highlighted nodes
ctx.beginPath();
ctx.arc(node.x, node.y, NODE_R * 1.4, 0, 2 * Math.PI, false);
ctx.fillStyle = node === hoverNode ? 'red' : 'orange';
ctx.fill();
});
</script>
</body>

View file

@ -0,0 +1,37 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
window.devicePixelRatio = 1; // use standard resolution in retina displays
// Random tree
const N = 500000;
const gData = {
nodes: [...Array(N).keys()].map(i => ({ id: i })),
links: [...Array(N).keys()]
.filter(id => id)
.map(id => ({
source: id,
target: Math.round(Math.random() * (id-1))
}))
};
const Graph = ForceGraph()
(document.getElementById('graph'))
.linkColor(() => 'rgba(0,0,0,0.04)')
.enablePointerInteraction(false)
.d3AlphaDecay(0)
.d3VelocityDecay(0.08)
.warmupTicks(20)
.cooldownTicks(0)
.zoom(0.01)
.graphData(gData);
</script>
</body>

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

View file

@ -0,0 +1,43 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
const imgs = ['cat.jpg', 'dog.jpg', 'eagle.jpg', 'elephant.jpg', 'grasshopper.jpg', 'octopus.jpg', 'owl.jpg', 'panda.jpg', 'squirrel.jpg', 'tiger.jpg', 'whale.jpg']
.map(src => {
const img = new Image();
img.src = `./imgs/${src}`;
return img;
});
// Random connected graph
const gData = {
nodes: imgs.map((img, id) => ({ id, img })),
links: [...Array(imgs.length).keys()]
.filter(id => id)
.map(id => ({
source: id,
target: Math.round(Math.random() * (id-1))
}))
};
const Graph = ForceGraph()
(document.getElementById('graph'))
.nodeCanvasObject(({ img, x, y }, ctx) => {
const size = 12;
ctx.drawImage(img, x - size / 2, y - size / 2, size, size);
})
.nodePointerAreaPaint((node, color, ctx) => {
const size = 12;
ctx.fillStyle = color;
ctx.fillRect(node.x - size / 2, node.y - size / 2, size, size); // draw square as pointer trap
})
.graphData(gData);
</script>
</body>

View file

@ -0,0 +1,41 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
window.devicePixelRatio = 1; // use standard resolution in retina displays
fetch('../datasets/mplate.mtx').then(res => res.text()).then(mtxData => {
let nodeSet = new Set();
const pairs = mtxData.split('\n')
.slice(14, -1)
.map(d => d.split(' '));
pairs.forEach(d => {
nodeSet.add(d[0]);
nodeSet.add(d[1]);
});
const nodes = Array.from(nodeSet).map(d => ({ id: d }));
const links = pairs.filter(d => d[0] !== d[1])
.map(d => ({ source: d[0], target: d[1] }));
const Graph = ForceGraph()
(document.getElementById('graph'))
.graphData({ nodes, links })
.d3AlphaDecay(0)
.d3VelocityDecay(0.08)
.cooldownTime(60000)
.linkColor(() => 'rgba(0,0,0,0.05)')
.zoom(0.05)
.enablePointerInteraction(false);
});
</script>
</body>

View file

@ -0,0 +1,24 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
fetch('../datasets/miserables.json').then(res => res.json()).then(data => {
const Graph = ForceGraph()
(document.getElementById('graph'))
.graphData(data)
.nodeId('id')
.nodeVal('val')
.nodeLabel('id')
.nodeAutoColorBy('group')
.linkSource('source')
.linkTarget('target')
});
</script>
</body>

View file

@ -0,0 +1,26 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
fetch('../datasets/blocks.json').then(res => res.json()).then(data => {
const elem = document.getElementById('graph');
const Graph = ForceGraph()(elem)
.backgroundColor('#101020')
.nodeRelSize(6)
.nodeAutoColorBy('user')
.nodeLabel(node => `${node.user}: ${node.description}`)
.linkColor(() => 'rgba(255,255,255,0.2)')
.linkDirectionalParticles(1)
.onNodeClick(node => window.open(`https://bl.ocks.org/${node.user}/${node.id}`, '_blank'))
.graphData(data);
});
</script>
</body>

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

View file

@ -0,0 +1,42 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
// Random tree
const N = 300;
const gData = {
nodes: [...Array(N).keys()].map(i => ({ id: i })),
links: [...Array(N).keys()]
.filter(id => id)
.map(id => ({
source: id,
target: Math.round(Math.random() * (id-1))
}))
};
const Graph = ForceGraph()
(document.getElementById('graph'))
.graphData(gData);
let k = 0, angle = 0, radius = 300;
setInterval(() => {
// zoom in
Graph.zoom(k);
k += 0.001;
// pan around
Graph.centerAt(
radius * Math.cos(angle),
radius * Math.sin(angle)
);
angle += Math.PI / 300;
}, 10);
</script>
</body>

View file

@ -0,0 +1,57 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!-- <script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
// Random tree
const N = 30;
const gData = {
nodes: [...Array(N).keys()].map(i => ({ id: i })),
links: [...Array(N).keys()]
.filter(id => id)
.map(id => ({
source: id,
target: Math.round(Math.random() * (id-1))
}))
};
let selectedNodes = new Set();
const Graph = ForceGraph()
(document.getElementById('graph'))
.graphData(gData)
.nodeRelSize(7)
.nodeColor(node => selectedNodes.has(node) ? 'darkorange' : 'grey')
.onNodeClick((node, event) => {
if (event.ctrlKey || event.shiftKey || event.altKey) { // multi-selection
selectedNodes.has(node) ? selectedNodes.delete(node) : selectedNodes.add(node);
} else { // single-selection
const untoggle = selectedNodes.has(node) && selectedNodes.size === 1;
selectedNodes.clear();
!untoggle && selectedNodes.add(node);
}
Graph.nodeColor(Graph.nodeColor()); // update color of selected nodes
})
.onNodeDrag((node, translate) => {
if (selectedNodes.has(node)) { // moving a selected node
[...selectedNodes]
.filter(selNode => selNode !== node) // don't touch node being dragged
.forEach(node => ['x', 'y'].forEach(coord => node[`f${coord}`] = node[coord] + translate[coord])); // translate other nodes by same amount
}
})
.onNodeDragEnd(node => {
if (selectedNodes.has(node)) { // finished moving a selected node
[...selectedNodes]
.filter(selNode => selNode !== node) // don't touch node being dragged
.forEach(node => ['x', 'y'].forEach(coord => node[`f${coord}`] = undefined)); // unfix controlled nodes
}
});
</script>
</body>

View file

@ -0,0 +1,37 @@
<head>
<style> body { margin: 30px; } </style>
<script src="//unpkg.com/element-resize-detector/dist/element-resize-detector.min.js"></script>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
// Random tree
const N = 200;
const gData = {
nodes: [...Array(N).keys()].map(i => ({ id: i })),
links: [...Array(N).keys()]
.filter(id => id)
.map(id => ({
source: id,
target: Math.round(Math.random() * (id-1))
}))
};
const Graph = ForceGraph()
(document.getElementById('graph'))
.backgroundColor('#F5F5FF')
.height(window.innerHeight - 60)
.graphData(gData);
elementResizeDetectorMaker().listenTo(
document.getElementById('graph'),
el => Graph.width(el.offsetWidth)
);
</script>
</body>

View file

@ -0,0 +1,69 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
fetch('../datasets/miserables.json').then(res => res.json()).then(data => {
const Graph = ForceGraph()
(document.getElementById('graph'))
.graphData(data)
.nodeId('id')
.nodeLabel('id')
.nodeAutoColorBy('group')
.linkCanvasObjectMode(() => 'after')
.linkCanvasObject((link, ctx) => {
const MAX_FONT_SIZE = 4;
const LABEL_NODE_MARGIN = Graph.nodeRelSize() * 1.5;
const start = link.source;
const end = link.target;
// ignore unbound links
if (typeof start !== 'object' || typeof end !== 'object') return;
// calculate label positioning
const textPos = Object.assign(...['x', 'y'].map(c => ({
[c]: start[c] + (end[c] - start[c]) / 2 // calc middle point
})));
const relLink = { x: end.x - start.x, y: end.y - start.y };
const maxTextLength = Math.sqrt(Math.pow(relLink.x, 2) + Math.pow(relLink.y, 2)) - LABEL_NODE_MARGIN * 2;
let textAngle = Math.atan2(relLink.y, relLink.x);
// maintain label vertical orientation for legibility
if (textAngle > Math.PI / 2) textAngle = -(Math.PI - textAngle);
if (textAngle < -Math.PI / 2) textAngle = -(-Math.PI - textAngle);
const label = `${link.source.id} > ${link.target.id}`;
// estimate fontSize to fit in link length
ctx.font = '1px Sans-Serif';
const fontSize = Math.min(MAX_FONT_SIZE, maxTextLength / ctx.measureText(label).width);
ctx.font = `${fontSize}px Sans-Serif`;
const textWidth = ctx.measureText(label).width;
const bckgDimensions = [textWidth, fontSize].map(n => n + fontSize * 0.2); // some padding
// draw text label (with background rect)
ctx.save();
ctx.translate(textPos.x, textPos.y);
ctx.rotate(textAngle);
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.fillRect(- bckgDimensions[0] / 2, - bckgDimensions[1] / 2, ...bckgDimensions);
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = 'darkgrey';
ctx.fillText(label, 0, 0);
ctx.restore();
});
});
</script>
</body>

View file

@ -0,0 +1,42 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
fetch('../datasets/miserables.json').then(res => res.json()).then(data => {
const Graph = ForceGraph()
(document.getElementById('graph'))
.graphData(data)
.nodeId('id')
.nodeAutoColorBy('group')
.nodeCanvasObject((node, ctx, globalScale) => {
const label = node.id;
const fontSize = 12/globalScale;
ctx.font = `${fontSize}px Sans-Serif`;
const textWidth = ctx.measureText(label).width;
const bckgDimensions = [textWidth, fontSize].map(n => n + fontSize * 0.2); // some padding
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.fillRect(node.x - bckgDimensions[0] / 2, node.y - bckgDimensions[1] / 2, ...bckgDimensions);
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = node.color;
ctx.fillText(label, node.x, node.y);
node.__bckgDimensions = bckgDimensions; // to re-use in nodePointerAreaPaint
})
.nodePointerAreaPaint((node, color, ctx) => {
ctx.fillStyle = color;
const bckgDimensions = node.__bckgDimensions;
bckgDimensions && ctx.fillRect(node.x - bckgDimensions[0] / 2, node.y - bckgDimensions[1] / 2, ...bckgDimensions);
});
});
</script>
</body>

View file

@ -0,0 +1,71 @@
<head>
<style> body { margin: 0; } </style>
<script src="//unpkg.com/d3-dsv"></script>
<script src="//unpkg.com/dat.gui"></script>
<script src="//unpkg.com/d3-quadtree"></script>
<script src="//unpkg.com/d3-force"></script>
<script src="//unpkg.com/force-graph"></script>
<!--<script src="../../dist/force-graph.js"></script>-->
</head>
<body>
<div id="graph"></div>
<script>
// controls
const controls = { 'DAG Orientation': 'td'};
const gui = new dat.GUI();
gui.add(controls, 'DAG Orientation', ['td', 'bu', 'lr', 'rl', 'radialout', 'radialin', null])
.onChange(orientation => graph && graph.dagMode(orientation));
// graph config
const NODE_REL_SIZE = 1;
const graph = ForceGraph()
.dagMode('td')
.dagLevelDistance(300)
.backgroundColor('#101020')
.linkColor(() => 'rgba(255,255,255,0.2)')
.nodeRelSize(NODE_REL_SIZE)
.nodeId('path')
.nodeVal(node => 100 / (node.level + 1))
.nodeLabel('path')
.nodeAutoColorBy('module')
.linkDirectionalParticles(2)
.linkDirectionalParticleWidth(2)
.d3Force('collision', d3.forceCollide(node => Math.sqrt(100 / (node.level + 1)) * NODE_REL_SIZE))
.d3VelocityDecay(0.3);
fetch('../datasets/d3-dependencies.csv')
.then(r => r.text())
.then(d3.csvParse)
.then(data => {
const nodes = [], links = [];
data.forEach(({ size, path }) => {
const levels = path.split('/'),
level = levels.length - 1,
module = level > 0 ? levels[1] : null,
leaf = levels.pop(),
parent = levels.join('/');
const node = {
path,
leaf,
module,
size: +size || 20,
level
};
nodes.push(node);
if (parent) {
links.push({source: parent, target: path, targetNode: node});
}
});
graph(document.getElementById('graph'))
.graphData({ nodes, links });
});
</script>
</body>

82
VISUALIZACION/node_modules/force-graph/package.json generated vendored Executable file
View file

@ -0,0 +1,82 @@
{
"name": "force-graph",
"version": "1.43.2",
"description": "2D force-directed graph rendered on HTML5 canvas",
"type": "module",
"unpkg": "dist/force-graph.min.js",
"main": "dist/force-graph.mjs",
"module": "dist/force-graph.mjs",
"types": "src/index.d.ts",
"exports": {
"types": "./dist/force-graph.d.ts",
"umd": "./dist/force-graph.min.js",
"default": "./dist/force-graph.mjs"
},
"sideEffects": [
"./src/*.css"
],
"repository": {
"type": "git",
"url": "git+https://github.com/vasturiano/force-graph.git"
},
"homepage": "https://github.com/vasturiano/force-graph",
"keywords": [
"2d",
"force",
"simulation",
"graph",
"canvas",
"d3"
],
"author": {
"name": "Vasco Asturiano",
"url": "https://github.com/vasturiano"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/vasturiano/force-graph/issues"
},
"scripts": {
"build": "rimraf dist && rollup -c",
"dev": "rollup -w -c rollup.config.dev.js",
"prepare": "npm run build"
},
"files": [
"dist/**/*",
"example/**/*",
"src/**/*"
],
"dependencies": {
"@tweenjs/tween.js": "18 - 21",
"accessor-fn": "1",
"bezier-js": "3 - 6",
"canvas-color-tracker": "1",
"d3-array": "1 - 3",
"d3-drag": "2 - 3",
"d3-force-3d": "2 - 3",
"d3-scale": "1 - 4",
"d3-scale-chromatic": "1 - 3",
"d3-selection": "2 - 3",
"d3-zoom": "2 - 3",
"index-array-by": "1",
"kapsule": "^1.14",
"lodash-es": "4"
},
"devDependencies": {
"@babel/core": "^7.22.8",
"@babel/preset-env": "^7.22.7",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-commonjs": "^25.0.2",
"@rollup/plugin-node-resolve": "^15.1.0",
"@rollup/plugin-terser": "^0.4.3",
"postcss": "^8.4.25",
"rimraf": "^5.0.1",
"rollup": "^3.26.2",
"rollup-plugin-dts": "^5.3.0",
"rollup-plugin-postcss": "^4.0.2",
"typescript": "^5.1.6"
},
"engines": {
"node": ">=12"
}
}

View file

@ -0,0 +1,546 @@
import {
forceSimulation as d3ForceSimulation,
forceLink as d3ForceLink,
forceManyBody as d3ForceManyBody,
forceCenter as d3ForceCenter,
forceRadial as d3ForceRadial
} from 'd3-force-3d';
import { Bezier } from 'bezier-js';
import Kapsule from 'kapsule';
import accessorFn from 'accessor-fn';
import indexBy from 'index-array-by';
import { autoColorObjects } from './color-utils';
import getDagDepths from './dagDepths';
//
const DAG_LEVEL_NODE_RATIO = 2;
// whenever styling props are changed that require a canvas redraw
const notifyRedraw = (_, state) => state.onNeedsRedraw && state.onNeedsRedraw();
const updDataPhotons = (_, state) => {
if (!state.isShadow) {
// Add photon particles
const linkParticlesAccessor = accessorFn(state.linkDirectionalParticles);
state.graphData.links.forEach(link => {
const numPhotons = Math.round(Math.abs(linkParticlesAccessor(link)));
if (numPhotons) {
link.__photons = [...Array(numPhotons)].map(() => ({}));
} else {
delete link.__photons;
}
});
}
};
export default Kapsule({
props: {
graphData: {
default: {
nodes: [],
links: []
},
onChange(_, state) {
state.engineRunning = false; // Pause simulation
updDataPhotons(_, state);
}
},
dagMode: { onChange(dagMode, state) { // td, bu, lr, rl, radialin, radialout
!dagMode && (state.graphData.nodes || []).forEach(n => n.fx = n.fy = undefined); // unfix nodes when disabling dag mode
}},
dagLevelDistance: {},
dagNodeFilter: { default: node => true },
onDagError: { triggerUpdate: false },
nodeRelSize: { default: 4, triggerUpdate: false, onChange: notifyRedraw }, // area per val unit
nodeId: { default: 'id' },
nodeVal: { default: 'val', triggerUpdate: false, onChange: notifyRedraw },
nodeColor: { default: 'color', triggerUpdate: false, onChange: notifyRedraw },
nodeAutoColorBy: {},
nodeCanvasObject: { triggerUpdate: false, onChange: notifyRedraw },
nodeCanvasObjectMode: { default: () => 'replace', triggerUpdate: false, onChange: notifyRedraw },
nodeVisibility: { default: true, triggerUpdate: false, onChange: notifyRedraw },
linkSource: { default: 'source' },
linkTarget: { default: 'target' },
linkVisibility: { default: true, triggerUpdate: false, onChange: notifyRedraw },
linkColor: { default: 'color', triggerUpdate: false, onChange: notifyRedraw },
linkAutoColorBy: {},
linkLineDash: { triggerUpdate: false, onChange: notifyRedraw },
linkWidth: { default: 1, triggerUpdate: false, onChange: notifyRedraw },
linkCurvature: { default: 0, triggerUpdate: false, onChange: notifyRedraw },
linkCanvasObject: { triggerUpdate: false, onChange: notifyRedraw },
linkCanvasObjectMode: { default: () => 'replace', triggerUpdate: false, onChange: notifyRedraw },
linkDirectionalArrowLength: { default: 0, triggerUpdate: false, onChange: notifyRedraw },
linkDirectionalArrowColor: { triggerUpdate: false, onChange: notifyRedraw },
linkDirectionalArrowRelPos: { default: 0.5, triggerUpdate: false, onChange: notifyRedraw }, // value between 0<>1 indicating the relative pos along the (exposed) line
linkDirectionalParticles: { default: 0, triggerUpdate: false, onChange: updDataPhotons }, // animate photons travelling in the link direction
linkDirectionalParticleSpeed: { default: 0.01, triggerUpdate: false }, // in link length ratio per frame
linkDirectionalParticleWidth: { default: 4, triggerUpdate: false },
linkDirectionalParticleColor: { triggerUpdate: false },
globalScale: { default: 1, triggerUpdate: false },
d3AlphaMin: { default: 0, triggerUpdate: false},
d3AlphaDecay: { default: 0.0228, triggerUpdate: false, onChange(alphaDecay, state) { state.forceLayout.alphaDecay(alphaDecay) }},
d3AlphaTarget: { default: 0, triggerUpdate: false, onChange(alphaTarget, state) { state.forceLayout.alphaTarget(alphaTarget) }},
d3VelocityDecay: { default: 0.4, triggerUpdate: false, onChange(velocityDecay, state) { state.forceLayout.velocityDecay(velocityDecay) } },
warmupTicks: { default: 0, triggerUpdate: false }, // how many times to tick the force engine at init before starting to render
cooldownTicks: { default: Infinity, triggerUpdate: false },
cooldownTime: { default: 15000, triggerUpdate: false }, // ms
onUpdate: { default: () => {}, triggerUpdate: false },
onFinishUpdate: { default: () => {}, triggerUpdate: false },
onEngineTick: { default: () => {}, triggerUpdate: false },
onEngineStop: { default: () => {}, triggerUpdate: false },
onNeedsRedraw: { triggerUpdate: false },
isShadow: { default: false, triggerUpdate: false }
},
methods: {
// Expose d3 forces for external manipulation
d3Force: function(state, forceName, forceFn) {
if (forceFn === undefined) {
return state.forceLayout.force(forceName); // Force getter
}
state.forceLayout.force(forceName, forceFn); // Force setter
return this;
},
d3ReheatSimulation: function(state) {
state.forceLayout.alpha(1);
this.resetCountdown();
return this;
},
// reset cooldown state
resetCountdown: function(state) {
state.cntTicks = 0;
state.startTickTime = new Date();
state.engineRunning = true;
return this;
},
isEngineRunning: state => !!state.engineRunning,
tickFrame: function(state) {
!state.isShadow && layoutTick();
paintLinks();
!state.isShadow && paintArrows();
!state.isShadow && paintPhotons();
paintNodes();
return this;
//
function layoutTick() {
if (state.engineRunning) {
if (
++state.cntTicks > state.cooldownTicks ||
(new Date()) - state.startTickTime > state.cooldownTime ||
(state.d3AlphaMin > 0 && state.forceLayout.alpha() < state.d3AlphaMin)
) {
state.engineRunning = false; // Stop ticking graph
state.onEngineStop();
} else {
state.forceLayout.tick(); // Tick it
state.onEngineTick();
}
}
}
function paintNodes() {
const getVisibility = accessorFn(state.nodeVisibility);
const getVal = accessorFn(state.nodeVal);
const getColor = accessorFn(state.nodeColor);
const getNodeCanvasObjectMode = accessorFn(state.nodeCanvasObjectMode);
const ctx = state.ctx;
// Draw wider nodes by 1px on shadow canvas for more precise hovering (due to boundary anti-aliasing)
const padAmount = state.isShadow / state.globalScale;
const visibleNodes = state.graphData.nodes.filter(getVisibility);
ctx.save();
visibleNodes.forEach(node => {
const nodeCanvasObjectMode = getNodeCanvasObjectMode(node);
if (state.nodeCanvasObject && (nodeCanvasObjectMode === 'before' || nodeCanvasObjectMode === 'replace')) {
// Custom node before/replace paint
state.nodeCanvasObject(node, ctx, state.globalScale);
if (nodeCanvasObjectMode === 'replace') {
ctx.restore();
return;
}
}
// Draw wider nodes by 1px on shadow canvas for more precise hovering (due to boundary anti-aliasing)
const r = Math.sqrt(Math.max(0, getVal(node) || 1)) * state.nodeRelSize + padAmount;
ctx.beginPath();
ctx.arc(node.x, node.y, r, 0, 2 * Math.PI, false);
ctx.fillStyle = getColor(node) || 'rgba(31, 120, 180, 0.92)';
ctx.fill();
if (state.nodeCanvasObject && nodeCanvasObjectMode === 'after') {
// Custom node after paint
state.nodeCanvasObject(node, state.ctx, state.globalScale);
}
});
ctx.restore();
}
function paintLinks() {
const getVisibility = accessorFn(state.linkVisibility);
const getColor = accessorFn(state.linkColor);
const getWidth = accessorFn(state.linkWidth);
const getLineDash = accessorFn(state.linkLineDash);
const getCurvature = accessorFn(state.linkCurvature);
const getLinkCanvasObjectMode = accessorFn(state.linkCanvasObjectMode);
const ctx = state.ctx;
// Draw wider lines by 2px on shadow canvas for more precise hovering (due to boundary anti-aliasing)
const padAmount = state.isShadow * 2;
const visibleLinks = state.graphData.links.filter(getVisibility);
visibleLinks.forEach(calcLinkControlPoints); // calculate curvature control points for all visible links
let beforeCustomLinks = [], afterCustomLinks = [], defaultPaintLinks = visibleLinks;
if (state.linkCanvasObject) {
const replaceCustomLinks = [], otherCustomLinks = [];
visibleLinks.forEach(d =>
({
before: beforeCustomLinks,
after: afterCustomLinks,
replace: replaceCustomLinks
}[getLinkCanvasObjectMode(d)] || otherCustomLinks).push(d)
);
defaultPaintLinks = [...beforeCustomLinks, ...afterCustomLinks, ...otherCustomLinks];
beforeCustomLinks = beforeCustomLinks.concat(replaceCustomLinks);
}
// Custom link before paints
ctx.save();
beforeCustomLinks.forEach(link => state.linkCanvasObject(link, ctx, state.globalScale));
ctx.restore();
// Bundle strokes per unique color/width/dash for performance optimization
const linksPerColor = indexBy(defaultPaintLinks, [getColor, getWidth, getLineDash]);
ctx.save();
Object.entries(linksPerColor).forEach(([color, linksPerWidth]) => {
const lineColor = !color || color === 'undefined' ? 'rgba(0,0,0,0.15)' : color;
Object.entries(linksPerWidth).forEach(([width, linesPerLineDash]) => {
const lineWidth = (width || 1) / state.globalScale + padAmount;
Object.entries(linesPerLineDash).forEach(([dashSegments, links]) => {
const lineDashSegments = getLineDash(links[0]);
ctx.beginPath();
links.forEach(link => {
const start = link.source;
const end = link.target;
if (!start || !end || !start.hasOwnProperty('x') || !end.hasOwnProperty('x')) return; // skip invalid link
ctx.moveTo(start.x, start.y);
const controlPoints = link.__controlPoints;
if (!controlPoints) { // Straight line
ctx.lineTo(end.x, end.y);
} else {
// Use quadratic curves for regular lines and bezier for loops
ctx[controlPoints.length === 2 ? 'quadraticCurveTo' : 'bezierCurveTo'](...controlPoints, end.x, end.y);
}
});
ctx.strokeStyle = lineColor;
ctx.lineWidth = lineWidth;
ctx.setLineDash(lineDashSegments || []);
ctx.stroke();
});
});
});
ctx.restore();
// Custom link after paints
ctx.save();
afterCustomLinks.forEach(link => state.linkCanvasObject(link, ctx, state.globalScale));
ctx.restore();
//
function calcLinkControlPoints(link) {
const curvature = getCurvature(link);
if (!curvature) { // straight line
link.__controlPoints = null;
return;
}
const start = link.source;
const end = link.target;
if (!start || !end || !start.hasOwnProperty('x') || !end.hasOwnProperty('x')) return; // skip invalid link
const l = Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2)); // line length
if (l > 0) {
const a = Math.atan2(end.y - start.y, end.x - start.x); // line angle
const d = l * curvature; // control point distance
const cp = { // control point
x: (start.x + end.x) / 2 + d * Math.cos(a - Math.PI / 2),
y: (start.y + end.y) / 2 + d * Math.sin(a - Math.PI / 2)
};
link.__controlPoints = [cp.x, cp.y];
} else { // Same point, draw a loop
const d = curvature * 70;
link.__controlPoints = [end.x, end.y - d, end.x + d, end.y];
}
}
}
function paintArrows() {
const ARROW_WH_RATIO = 1.6;
const ARROW_VLEN_RATIO = 0.2;
const getLength = accessorFn(state.linkDirectionalArrowLength);
const getRelPos = accessorFn(state.linkDirectionalArrowRelPos);
const getVisibility = accessorFn(state.linkVisibility);
const getColor = accessorFn(state.linkDirectionalArrowColor || state.linkColor);
const getNodeVal = accessorFn(state.nodeVal);
const ctx = state.ctx;
ctx.save();
state.graphData.links.filter(getVisibility).forEach(link => {
const arrowLength = getLength(link);
if (!arrowLength || arrowLength < 0) return;
const start = link.source;
const end = link.target;
if (!start || !end || !start.hasOwnProperty('x') || !end.hasOwnProperty('x')) return; // skip invalid link
const startR = Math.sqrt(Math.max(0, getNodeVal(start) || 1)) * state.nodeRelSize;
const endR = Math.sqrt(Math.max(0, getNodeVal(end) || 1)) * state.nodeRelSize;
const arrowRelPos = Math.min(1, Math.max(0, getRelPos(link)));
const arrowColor = getColor(link) || 'rgba(0,0,0,0.28)';
const arrowHalfWidth = arrowLength / ARROW_WH_RATIO / 2;
// Construct bezier for curved lines
const bzLine = link.__controlPoints && new Bezier(start.x, start.y, ...link.__controlPoints, end.x, end.y);
const getCoordsAlongLine = bzLine
? t => bzLine.get(t) // get position along bezier line
: t => ({ // straight line: interpolate linearly
x: start.x + (end.x - start.x) * t || 0,
y: start.y + (end.y - start.y) * t || 0
});
const lineLen = bzLine
? bzLine.length()
: Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2));
const posAlongLine = startR + arrowLength + (lineLen - startR - endR - arrowLength) * arrowRelPos;
const arrowHead = getCoordsAlongLine(posAlongLine / lineLen);
const arrowTail = getCoordsAlongLine((posAlongLine - arrowLength) / lineLen);
const arrowTailVertex = getCoordsAlongLine((posAlongLine - arrowLength * (1 - ARROW_VLEN_RATIO)) / lineLen);
const arrowTailAngle = Math.atan2(arrowHead.y - arrowTail.y, arrowHead.x - arrowTail.x) - Math.PI / 2;
ctx.beginPath();
ctx.moveTo(arrowHead.x, arrowHead.y);
ctx.lineTo(arrowTail.x + arrowHalfWidth * Math.cos(arrowTailAngle), arrowTail.y + arrowHalfWidth * Math.sin(arrowTailAngle));
ctx.lineTo(arrowTailVertex.x, arrowTailVertex.y);
ctx.lineTo(arrowTail.x - arrowHalfWidth * Math.cos(arrowTailAngle), arrowTail.y - arrowHalfWidth * Math.sin(arrowTailAngle));
ctx.fillStyle = arrowColor;
ctx.fill();
});
ctx.restore();
}
function paintPhotons() {
const getNumPhotons = accessorFn(state.linkDirectionalParticles);
const getSpeed = accessorFn(state.linkDirectionalParticleSpeed);
const getDiameter = accessorFn(state.linkDirectionalParticleWidth);
const getVisibility = accessorFn(state.linkVisibility);
const getColor = accessorFn(state.linkDirectionalParticleColor || state.linkColor);
const ctx = state.ctx;
ctx.save();
state.graphData.links.filter(getVisibility).forEach(link => {
const numCyclePhotons = getNumPhotons(link);
if (!link.hasOwnProperty('__photons') || !link.__photons.length) return;
const start = link.source;
const end = link.target;
if (!start || !end || !start.hasOwnProperty('x') || !end.hasOwnProperty('x')) return; // skip invalid link
const particleSpeed = getSpeed(link);
const photons = link.__photons || [];
const photonR = Math.max(0, getDiameter(link) / 2) / Math.sqrt(state.globalScale);
const photonColor = getColor(link) || 'rgba(0,0,0,0.28)';
ctx.fillStyle = photonColor;
// Construct bezier for curved lines
const bzLine = link.__controlPoints
? new Bezier(start.x, start.y, ...link.__controlPoints, end.x, end.y)
: null;
let cyclePhotonIdx = 0;
let needsCleanup = false; // whether some photons need to be removed from list
photons.forEach(photon => {
const singleHop = !!photon.__singleHop;
if (!photon.hasOwnProperty('__progressRatio')) {
photon.__progressRatio = singleHop ? 0 : cyclePhotonIdx / numCyclePhotons;
}
!singleHop && cyclePhotonIdx++; // increase regular photon index
photon.__progressRatio += particleSpeed;
if (photon.__progressRatio >=1) {
if (!singleHop) {
photon.__progressRatio = photon.__progressRatio % 1;
} else {
needsCleanup = true;
return;
}
}
const photonPosRatio = photon.__progressRatio;
const coords = bzLine
? bzLine.get(photonPosRatio) // get position along bezier line
: { // straight line: interpolate linearly
x: start.x + (end.x - start.x) * photonPosRatio || 0,
y: start.y + (end.y - start.y) * photonPosRatio || 0
};
ctx.beginPath();
ctx.arc(coords.x, coords.y, photonR, 0, 2 * Math.PI, false);
ctx.fill();
});
if (needsCleanup) {
// remove expired single hop photons
link.__photons = link.__photons.filter(photon => !photon.__singleHop || photon.__progressRatio <= 1);
}
});
ctx.restore();
}
},
emitParticle: function(state, link) {
if (link) {
!link.__photons && (link.__photons = []);
link.__photons.push({ __singleHop: true }); // add a single hop particle
}
return this;
}
},
stateInit: () => ({
forceLayout: d3ForceSimulation()
.force('link', d3ForceLink())
.force('charge', d3ForceManyBody())
.force('center', d3ForceCenter())
.force('dagRadial', null)
.stop(),
engineRunning: false
}),
init(canvasCtx, state) {
// Main canvas object to manipulate
state.ctx = canvasCtx;
},
update(state) {
state.engineRunning = false; // Pause simulation
state.onUpdate();
if (state.nodeAutoColorBy !== null) {
// Auto add color to uncolored nodes
autoColorObjects(state.graphData.nodes, accessorFn(state.nodeAutoColorBy), state.nodeColor);
}
if (state.linkAutoColorBy !== null) {
// Auto add color to uncolored links
autoColorObjects(state.graphData.links, accessorFn(state.linkAutoColorBy), state.linkColor);
}
// parse links
state.graphData.links.forEach(link => {
link.source = link[state.linkSource];
link.target = link[state.linkTarget];
});
// Feed data to force-directed layout
state.forceLayout
.stop()
.alpha(1) // re-heat the simulation
.nodes(state.graphData.nodes);
// add links (if link force is still active)
const linkForce = state.forceLayout.force('link');
if (linkForce) {
linkForce
.id(d => d[state.nodeId])
.links(state.graphData.links);
}
// setup dag force constraints
const nodeDepths = state.dagMode && getDagDepths(
state.graphData,
node => node[state.nodeId],
{
nodeFilter: state.dagNodeFilter,
onLoopError: state.onDagError || undefined
}
);
const maxDepth = Math.max(...Object.values(nodeDepths || []));
const dagLevelDistance = state.dagLevelDistance || (
state.graphData.nodes.length / (maxDepth || 1) * DAG_LEVEL_NODE_RATIO
* (['radialin', 'radialout'].indexOf(state.dagMode) !== -1 ? 0.7 : 1)
);
// Fix nodes to x,y for dag mode
if (state.dagMode) {
const getFFn = (fix, invert) => node => !fix
? undefined
: (nodeDepths[node[state.nodeId]] - maxDepth / 2) * dagLevelDistance * (invert ? -1 : 1);
const fxFn = getFFn(['lr', 'rl'].indexOf(state.dagMode) !== -1, state.dagMode === 'rl');
const fyFn = getFFn(['td', 'bu'].indexOf(state.dagMode) !== -1, state.dagMode === 'bu');
state.graphData.nodes.filter(state.dagNodeFilter).forEach(node => {
node.fx = fxFn(node);
node.fy = fyFn(node);
});
}
// Use radial force for radial dags
state.forceLayout.force('dagRadial',
['radialin', 'radialout'].indexOf(state.dagMode) !== -1
? d3ForceRadial(node => {
const nodeDepth = nodeDepths[node[state.nodeId]] || -1;
return (state.dagMode === 'radialin' ? maxDepth - nodeDepth : nodeDepth) * dagLevelDistance;
})
.strength(node => state.dagNodeFilter(node) ? 1 : 0)
: null
);
for (let i=0; (i<state.warmupTicks) && !(state.d3AlphaMin > 0 && state.forceLayout.alpha() < state.d3AlphaMin); i++) {
state.forceLayout.tick();
} // Initial ticks before starting to render
this.resetCountdown();
state.onFinishUpdate();
}
});

17
VISUALIZACION/node_modules/force-graph/src/color-utils.js generated vendored Executable file
View file

@ -0,0 +1,17 @@
import { scaleOrdinal } from 'd3-scale';
import { schemePaired } from 'd3-scale-chromatic';
const autoColorScale = scaleOrdinal(schemePaired);
// Autoset attribute colorField by colorByAccessor property
// If an object has already a color, don't set it
// Objects can be nodes or links
function autoColorObjects(objects, colorByAccessor, colorField) {
if (!colorByAccessor || typeof colorField !== 'string') return;
objects.filter(obj => !obj[colorField]).forEach(obj => {
obj[colorField] = autoColorScale(colorByAccessor(obj));
});
}
export { autoColorObjects };

51
VISUALIZACION/node_modules/force-graph/src/dagDepths.js generated vendored Executable file
View file

@ -0,0 +1,51 @@
export default function({ nodes, links }, idAccessor, {
nodeFilter = () => true,
onLoopError = loopIds => { throw `Invalid DAG structure! Found cycle in node path: ${loopIds.join(' -> ')}.` }
} = {}) {
// linked graph
const graph = {};
nodes.forEach(node => graph[idAccessor(node)] = { data: node, out : [], depth: -1, skip: !nodeFilter(node) });
links.forEach(({ source, target }) => {
const sourceId = getNodeId(source);
const targetId = getNodeId(target);
if (!graph.hasOwnProperty(sourceId)) throw `Missing source node with id: ${sourceId}`;
if (!graph.hasOwnProperty(targetId)) throw `Missing target node with id: ${targetId}`;
const sourceNode = graph[sourceId];
const targetNode = graph[targetId];
sourceNode.out.push(targetNode);
function getNodeId(node) {
return typeof node === 'object' ? idAccessor(node) : node;
}
});
const foundLoops = [];
traverse(Object.values(graph));
const nodeDepths = Object.assign({}, ...Object.entries(graph)
.filter(([, node]) => !node.skip)
.map(([id, node]) => ({ [id]: node.depth }))
);
return nodeDepths;
function traverse(nodes, nodeStack = [], currentDepth = 0) {
for (let i=0, l=nodes.length; i<l; i++) {
const node = nodes[i];
if (nodeStack.indexOf(node) !== -1) {
const loop = [...nodeStack.slice(nodeStack.indexOf(node)), node].map(d => idAccessor(d.data));
if (!foundLoops.some(foundLoop => foundLoop.length === loop.length && foundLoop.every((id, idx) => id === loop[idx]))) {
foundLoops.push(loop);
onLoopError(loop);
}
continue;
}
if (currentDepth > node.depth) { // Don't unnecessarily revisit chunks of the graph
node.depth = currentDepth;
traverse(node.out, [...nodeStack, node], currentDepth + (node.skip ? 0 : 1));
}
}
}
}

35
VISUALIZACION/node_modules/force-graph/src/force-graph.css generated vendored Executable file
View file

@ -0,0 +1,35 @@
.force-graph-container canvas {
display: block;
user-select: none;
outline: none;
-webkit-tap-highlight-color: transparent;
}
.force-graph-container .graph-tooltip {
position: absolute;
top: 0;
font-family: sans-serif;
font-size: 16px;
padding: 4px;
border-radius: 3px;
color: #eee;
background: rgba(0,0,0,0.65);
visibility: hidden; /* by default */
}
.force-graph-container .clickable {
cursor: pointer;
}
.force-graph-container .grabbable {
cursor: move;
cursor: grab;
cursor: -moz-grab;
cursor: -webkit-grab;
}
.force-graph-container .grabbable:active {
cursor: grabbing;
cursor: -moz-grabbing;
cursor: -webkit-grabbing;
}

650
VISUALIZACION/node_modules/force-graph/src/force-graph.js generated vendored Executable file
View file

@ -0,0 +1,650 @@
import { select as d3Select } from 'd3-selection';
import { zoom as d3Zoom, zoomTransform as d3ZoomTransform } from 'd3-zoom';
import { drag as d3Drag } from 'd3-drag';
import { max as d3Max, min as d3Min } from 'd3-array';
import { throttle } from 'lodash-es';
import * as TWEEN from '@tweenjs/tween.js';
import Kapsule from 'kapsule';
import accessorFn from 'accessor-fn';
import ColorTracker from 'canvas-color-tracker';
import CanvasForceGraph from './canvas-force-graph';
import linkKapsule from './kapsule-link.js';
const HOVER_CANVAS_THROTTLE_DELAY = 800; // ms to throttle shadow canvas updates for perf improvement
const ZOOM2NODES_FACTOR = 4;
// Expose config from forceGraph
const bindFG = linkKapsule('forceGraph', CanvasForceGraph);
const bindBoth = linkKapsule(['forceGraph', 'shadowGraph'], CanvasForceGraph);
const linkedProps = Object.assign(
...[
'nodeColor',
'nodeAutoColorBy',
'nodeCanvasObject',
'nodeCanvasObjectMode',
'linkColor',
'linkAutoColorBy',
'linkLineDash',
'linkWidth',
'linkCanvasObject',
'linkCanvasObjectMode',
'linkDirectionalArrowLength',
'linkDirectionalArrowColor',
'linkDirectionalArrowRelPos',
'linkDirectionalParticles',
'linkDirectionalParticleSpeed',
'linkDirectionalParticleWidth',
'linkDirectionalParticleColor',
'dagMode',
'dagLevelDistance',
'dagNodeFilter',
'onDagError',
'd3AlphaMin',
'd3AlphaDecay',
'd3VelocityDecay',
'warmupTicks',
'cooldownTicks',
'cooldownTime',
'onEngineTick',
'onEngineStop'
].map(p => ({ [p]: bindFG.linkProp(p)})),
...[
'nodeRelSize',
'nodeId',
'nodeVal',
'nodeVisibility',
'linkSource',
'linkTarget',
'linkVisibility',
'linkCurvature'
].map(p => ({ [p]: bindBoth.linkProp(p)}))
);
const linkedMethods = Object.assign(...[
'd3Force',
'd3ReheatSimulation',
'emitParticle'
].map(p => ({ [p]: bindFG.linkMethod(p)})));
function adjustCanvasSize(state) {
if (state.canvas) {
let curWidth = state.canvas.width;
let curHeight = state.canvas.height;
if (curWidth === 300 && curHeight === 150) { // Default canvas dimensions
curWidth = curHeight = 0;
}
const pxScale = window.devicePixelRatio; // 2 on retina displays
curWidth /= pxScale;
curHeight /= pxScale;
// Resize canvases
[state.canvas, state.shadowCanvas].forEach(canvas => {
// Element size
canvas.style.width = `${state.width}px`;
canvas.style.height = `${state.height}px`;
// Memory size (scaled to avoid blurriness)
canvas.width = state.width * pxScale;
canvas.height = state.height * pxScale;
// Normalize coordinate system to use css pixels (on init only)
if (!curWidth && !curHeight) {
canvas.getContext('2d').scale(pxScale, pxScale);
}
});
// Relative center panning based on 0,0
const k = d3ZoomTransform(state.canvas).k;
state.zoom.translateBy(state.zoom.__baseElem,
(state.width - curWidth) / 2 / k,
(state.height - curHeight) / 2 / k
);
state.needsRedraw = true;
}
}
function resetTransform(ctx) {
const pxRatio = window.devicePixelRatio;
ctx.setTransform(pxRatio, 0, 0, pxRatio, 0, 0);
}
function clearCanvas(ctx, width, height) {
ctx.save();
resetTransform(ctx); // reset transform
ctx.clearRect(0, 0, width, height);
ctx.restore(); //restore transforms
}
//
export default Kapsule({
props:{
width: { default: window.innerWidth, onChange: (_, state) => adjustCanvasSize(state), triggerUpdate: false } ,
height: { default: window.innerHeight, onChange: (_, state) => adjustCanvasSize(state), triggerUpdate: false },
graphData: {
default: { nodes: [], links: [] },
onChange: ((d, state) => {
[{ type: 'Node', objs: d.nodes }, { type: 'Link', objs: d.links }].forEach(hexIndex);
state.forceGraph.graphData(d);
state.shadowGraph.graphData(d);
function hexIndex({ type, objs }) {
objs
.filter(d => {
if (!d.hasOwnProperty('__indexColor')) return true;
const cur = state.colorTracker.lookup(d.__indexColor);
return (!cur || !cur.hasOwnProperty('d') || cur.d !== d);
})
.forEach(d => {
// store object lookup color
d.__indexColor = state.colorTracker.register({ type, d });
});
}
}),
triggerUpdate: false
},
backgroundColor: { onChange(color, state) { state.canvas && color && (state.canvas.style.background = color) }, triggerUpdate: false },
nodeLabel: { default: 'name', triggerUpdate: false },
nodePointerAreaPaint: { onChange(paintFn, state) {
state.shadowGraph.nodeCanvasObject(!paintFn ? null :
(node, ctx, globalScale) => paintFn(node, node.__indexColor, ctx, globalScale)
);
state.flushShadowCanvas && state.flushShadowCanvas();
}, triggerUpdate: false },
linkPointerAreaPaint: { onChange(paintFn, state) {
state.shadowGraph.linkCanvasObject(!paintFn ? null :
(link, ctx, globalScale) => paintFn(link, link.__indexColor, ctx, globalScale)
);
state.flushShadowCanvas && state.flushShadowCanvas();
}, triggerUpdate: false },
linkLabel: { default: 'name', triggerUpdate: false },
linkHoverPrecision: { default: 4, triggerUpdate: false },
minZoom: { default: 0.01, onChange(minZoom, state) { state.zoom.scaleExtent([minZoom, state.zoom.scaleExtent()[1]]); }, triggerUpdate: false },
maxZoom: { default: 1000, onChange(maxZoom, state) { state.zoom.scaleExtent([state.zoom.scaleExtent()[0], maxZoom]) }, triggerUpdate: false },
enableNodeDrag: { default: true, triggerUpdate: false },
enableZoomInteraction: { default: true, triggerUpdate: false },
enablePanInteraction: { default: true, triggerUpdate: false },
enableZoomPanInteraction: { default: true, triggerUpdate: false }, // to be deprecated
enablePointerInteraction: { default: true, onChange(_, state) { state.hoverObj = null; }, triggerUpdate: false },
autoPauseRedraw: { default: true, triggerUpdate: false },
onNodeDrag: { default: () => {}, triggerUpdate: false },
onNodeDragEnd: { default: () => {}, triggerUpdate: false },
onNodeClick: { triggerUpdate: false },
onNodeRightClick: { triggerUpdate: false },
onNodeHover: { triggerUpdate: false },
onLinkClick: { triggerUpdate: false },
onLinkRightClick: { triggerUpdate: false },
onLinkHover: { triggerUpdate: false },
onBackgroundClick: { triggerUpdate: false },
onBackgroundRightClick: { triggerUpdate: false },
onZoom: { triggerUpdate: false },
onZoomEnd: { triggerUpdate: false },
onRenderFramePre: { triggerUpdate: false },
onRenderFramePost: { triggerUpdate: false },
...linkedProps
},
aliases: { // Prop names supported for backwards compatibility
stopAnimation: 'pauseAnimation'
},
methods: {
graph2ScreenCoords: function(state, x, y) {
const t = d3ZoomTransform(state.canvas);
return { x: x * t.k + t.x, y: y * t.k + t.y };
},
screen2GraphCoords: function(state, x, y) {
const t = d3ZoomTransform(state.canvas);
return { x: (x - t.x) / t.k, y: (y - t.y) / t.k };
},
centerAt: function(state, x, y, transitionDuration) {
if (!state.canvas) return null; // no canvas yet
// setter
if (x !== undefined || y !== undefined) {
const finalPos = Object.assign({},
x !== undefined ? { x } : {},
y !== undefined ? { y } : {}
);
if (!transitionDuration) { // no animation
setCenter(finalPos);
} else {
new TWEEN.Tween(getCenter())
.to(finalPos, transitionDuration)
.easing(TWEEN.Easing.Quadratic.Out)
.onUpdate(setCenter)
.start();
}
return this;
}
// getter
return getCenter();
//
function getCenter() {
const t = d3ZoomTransform(state.canvas);
return { x: (state.width / 2 - t.x) / t.k, y: (state.height / 2 - t.y) / t.k };
}
function setCenter({ x, y }) {
state.zoom.translateTo(
state.zoom.__baseElem,
x === undefined ? getCenter().x : x,
y === undefined ? getCenter().y : y
);
state.needsRedraw = true;
}
},
zoom: function(state, k, transitionDuration) {
if (!state.canvas) return null; // no canvas yet
// setter
if (k !== undefined) {
if (!transitionDuration) { // no animation
setZoom(k);
} else {
new TWEEN.Tween({ k: getZoom() })
.to({ k }, transitionDuration)
.easing(TWEEN.Easing.Quadratic.Out)
.onUpdate(({ k }) => setZoom(k))
.start();
}
return this;
}
// getter
return getZoom();
//
function getZoom() {
return d3ZoomTransform(state.canvas).k;
}
function setZoom(k) {
state.zoom.scaleTo(state.zoom.__baseElem, k);
state.needsRedraw = true;
}
},
zoomToFit: function(state, transitionDuration = 0, padding = 10, ...bboxArgs) {
const bbox = this.getGraphBbox(...bboxArgs);
if (bbox) {
const center = {
x: (bbox.x[0] + bbox.x[1]) / 2,
y: (bbox.y[0] + bbox.y[1]) / 2,
};
const zoomK = Math.max(1e-12, Math.min(1e12,
(state.width - padding * 2) / (bbox.x[1] - bbox.x[0]),
(state.height - padding * 2) / (bbox.y[1] - bbox.y[0]))
);
this.centerAt(center.x, center.y, transitionDuration);
this.zoom(zoomK, transitionDuration);
}
return this;
},
getGraphBbox: function(state, nodeFilter = () => true) {
const getVal = accessorFn(state.nodeVal);
const getR = node => Math.sqrt(Math.max(0, getVal(node) || 1)) * state.nodeRelSize;
const nodesPos = state.graphData.nodes.filter(nodeFilter).map(node => ({
x: node.x,
y: node.y,
r: getR(node)
}));
return !nodesPos.length ? null : {
x: [
d3Min(nodesPos, node => node.x - node.r),
d3Max(nodesPos, node => node.x + node.r)
],
y: [
d3Min(nodesPos, node => node.y - node.r),
d3Max(nodesPos, node => node.y + node.r)
]
};
},
pauseAnimation: function(state) {
if (state.animationFrameRequestId) {
cancelAnimationFrame(state.animationFrameRequestId);
state.animationFrameRequestId = null;
}
return this;
},
resumeAnimation: function(state) {
if (!state.animationFrameRequestId) {
this._animationCycle();
}
return this;
},
_destructor: function() {
this.pauseAnimation();
this.graphData({ nodes: [], links: []});
},
...linkedMethods
},
stateInit: () => ({
lastSetZoom: 1,
zoom: d3Zoom(),
forceGraph: new CanvasForceGraph(),
shadowGraph: new CanvasForceGraph()
.cooldownTicks(0)
.nodeColor('__indexColor')
.linkColor('__indexColor')
.isShadow(true),
colorTracker: new ColorTracker() // indexed objects for rgb lookup
}),
init: function(domNode, state) {
// Wipe DOM
domNode.innerHTML = '';
// Container anchor for canvas and tooltip
const container = document.createElement('div');
container.classList.add('force-graph-container');
container.style.position = 'relative';
domNode.appendChild(container);
state.canvas = document.createElement('canvas');
if (state.backgroundColor) state.canvas.style.background = state.backgroundColor;
container.appendChild(state.canvas);
state.shadowCanvas = document.createElement('canvas');
// Show shadow canvas
//state.shadowCanvas.style.position = 'absolute';
//state.shadowCanvas.style.top = '0';
//state.shadowCanvas.style.left = '0';
//container.appendChild(state.shadowCanvas);
const ctx = state.canvas.getContext('2d');
const shadowCtx = state.shadowCanvas.getContext('2d', { willReadFrequently: true });
const pointerPos = { x: -1e12, y: -1e12 };
const getObjUnderPointer = () => {
let obj = null;
const pxScale = window.devicePixelRatio;
const px = (pointerPos.x > 0 && pointerPos.y > 0)
? shadowCtx.getImageData(pointerPos.x * pxScale, pointerPos.y * pxScale, 1, 1)
: null;
// Lookup object per pixel color
px && (obj = state.colorTracker.lookup(px.data));
return obj;
};
// Setup node drag interaction
d3Select(state.canvas).call(
d3Drag()
.subject(() => {
if (!state.enableNodeDrag) { return null; }
const obj = getObjUnderPointer();
return (obj && obj.type === 'Node') ? obj.d : null; // Only drag nodes
})
.on('start', ev => {
const obj = ev.subject;
obj.__initialDragPos = { x: obj.x, y: obj.y, fx: obj.fx, fy: obj.fy };
// keep engine running at low intensity throughout drag
if (!ev.active) {
obj.fx = obj.x; obj.fy = obj.y; // Fix points
}
// drag cursor
state.canvas.classList.add('grabbable');
})
.on('drag', ev => {
const obj = ev.subject;
const initPos = obj.__initialDragPos;
const dragPos = ev;
const k = d3ZoomTransform(state.canvas).k;
const translate = {
x: (initPos.x + (dragPos.x - initPos.x) / k) - obj.x,
y: (initPos.y + (dragPos.y - initPos.y) / k) - obj.y
};
// Move fx/fy (and x/y) of nodes based on the scaled drag distance since the drag start
['x', 'y'].forEach(c => obj[`f${c}`] = obj[c] = initPos[c] + (dragPos[c] - initPos[c]) / k);
// prevent freeze while dragging
state.forceGraph
.d3AlphaTarget(0.3) // keep engine running at low intensity throughout drag
.resetCountdown(); // prevent freeze while dragging
state.isPointerDragging = true;
obj.__dragged = true;
state.onNodeDrag(obj, translate);
})
.on('end', ev => {
const obj = ev.subject;
const initPos = obj.__initialDragPos;
const translate = {x: obj.x - initPos.x, y: obj.y - initPos.y};
if (initPos.fx === undefined) { obj.fx = undefined; }
if (initPos.fy === undefined) { obj.fy = undefined; }
delete(obj.__initialDragPos);
if (state.forceGraph.d3AlphaTarget()) {
state.forceGraph
.d3AlphaTarget(0) // release engine low intensity
.resetCountdown(); // let the engine readjust after releasing fixed nodes
}
// drag cursor
state.canvas.classList.remove('grabbable');
state.isPointerDragging = false;
if (obj.__dragged) {
delete(obj.__dragged);
state.onNodeDragEnd(obj, translate);
}
})
);
// Setup zoom / pan interaction
state.zoom(state.zoom.__baseElem = d3Select(state.canvas)); // Attach controlling elem for easy access
state.zoom.__baseElem.on('dblclick.zoom', null); // Disable double-click to zoom
state.zoom
.filter(ev =>
// disable zoom interaction
!ev.button
&& state.enableZoomPanInteraction
&& (state.enableZoomInteraction || ev.type !== 'wheel')
&& (state.enablePanInteraction || ev.type === 'wheel')
)
.on('zoom', ev => {
const t = ev.transform;
[ctx, shadowCtx].forEach(c => {
resetTransform(c);
c.translate(t.x, t.y);
c.scale(t.k, t.k);
});
state.onZoom && state.onZoom({ ...t, ...this.centerAt() }); // report x,y coordinates relative to canvas center
state.needsRedraw = true;
})
.on('end', ev => state.onZoomEnd && state.onZoomEnd({ ...ev.transform, ...this.centerAt() }));
adjustCanvasSize(state);
state.forceGraph
.onNeedsRedraw(() => state.needsRedraw = true)
.onFinishUpdate(() => {
// re-zoom, if still in default position (not user modified)
if (d3ZoomTransform(state.canvas).k === state.lastSetZoom && state.graphData.nodes.length) {
state.zoom.scaleTo(state.zoom.__baseElem,
state.lastSetZoom = ZOOM2NODES_FACTOR / Math.cbrt(state.graphData.nodes.length)
);
state.needsRedraw = true;
}
});
// Setup tooltip
const toolTipElem = document.createElement('div');
toolTipElem.classList.add('graph-tooltip');
container.appendChild(toolTipElem);
// Capture pointer coords on move or touchstart
['pointermove', 'pointerdown'].forEach(evType =>
container.addEventListener(evType, ev => {
if (evType === 'pointerdown') {
state.isPointerPressed = true; // track click state
state.pointerDownEvent = ev;
}
// detect pointer drag on canvas pan
!state.isPointerDragging && ev.type === 'pointermove'
&& (state.onBackgroundClick) // only bother detecting drags this way if background clicks are enabled (so they don't trigger accidentally on canvas panning)
&& (ev.pressure > 0 || state.isPointerPressed) // ev.pressure always 0 on Safari, so we use the isPointerPressed tracker
&& (ev.pointerType !== 'touch' || ev.movementX === undefined || [ev.movementX, ev.movementY].some(m => Math.abs(m) > 1)) // relax drag trigger sensitivity on touch events
&& (state.isPointerDragging = true);
// update the pointer pos
const offset = getOffset(container);
pointerPos.x = ev.pageX - offset.left;
pointerPos.y = ev.pageY - offset.top;
// Move tooltip
toolTipElem.style.top = `${pointerPos.y}px`;
toolTipElem.style.left = `${pointerPos.x}px`;
// adjust horizontal position to not exceed canvas boundaries
toolTipElem.style.transform = `translate(-${pointerPos.x / state.width * 100}%, ${
// flip to above if near bottom
state.height - pointerPos.y < 100 ? 'calc(-100% - 8px)' : '21px'
})`;
//
function getOffset(el) {
const rect = el.getBoundingClientRect(),
scrollLeft = window.pageXOffset || document.documentElement.scrollLeft,
scrollTop = window.pageYOffset || document.documentElement.scrollTop;
return { top: rect.top + scrollTop, left: rect.left + scrollLeft };
}
}, { passive: true })
);
// Handle click/touch events on nodes/links
container.addEventListener('pointerup', ev => {
state.isPointerPressed = false;
if (state.isPointerDragging) {
state.isPointerDragging = false;
return; // don't trigger click events after pointer drag (pan / node drag functionality)
}
const cbEvents = [ev, state.pointerDownEvent];
requestAnimationFrame(() => { // trigger click events asynchronously, to allow hoverObj to be set (on frame)
if (ev.button === 0) { // mouse left-click or touch
if (state.hoverObj) {
const fn = state[`on${state.hoverObj.type}Click`];
fn && fn(state.hoverObj.d, ...cbEvents);
} else {
state.onBackgroundClick && state.onBackgroundClick(...cbEvents);
}
}
if (ev.button === 2) { // mouse right-click
if (state.hoverObj) {
const fn = state[`on${state.hoverObj.type}RightClick`];
fn && fn(state.hoverObj.d, ...cbEvents);
} else {
state.onBackgroundRightClick && state.onBackgroundRightClick(...cbEvents);
}
}
});
}, { passive: true });
container.addEventListener('contextmenu', ev => {
if (!state.onBackgroundRightClick && !state.onNodeRightClick && !state.onLinkRightClick) return true; // default contextmenu behavior
ev.preventDefault();
return false;
});
state.forceGraph(ctx);
state.shadowGraph(shadowCtx);
//
const refreshShadowCanvas = throttle(() => {
// wipe canvas
clearCanvas(shadowCtx, state.width, state.height);
// Adjust link hover area
state.shadowGraph.linkWidth(l => accessorFn(state.linkWidth)(l) + state.linkHoverPrecision);
// redraw
const t = d3ZoomTransform(state.canvas);
state.shadowGraph.globalScale(t.k).tickFrame();
}, HOVER_CANVAS_THROTTLE_DELAY);
state.flushShadowCanvas = refreshShadowCanvas.flush; // hook to immediately invoke shadow canvas paint
// Kick-off renderer
(this._animationCycle = function animate() { // IIFE
const doRedraw = !state.autoPauseRedraw || !!state.needsRedraw || state.forceGraph.isEngineRunning()
|| state.graphData.links.some(d => d.__photons && d.__photons.length);
state.needsRedraw = false;
if (state.enablePointerInteraction) {
// Update tooltip and trigger onHover events
const obj = !state.isPointerDragging ? getObjUnderPointer() : null; // don't hover during drag
if (obj !== state.hoverObj) {
const prevObj = state.hoverObj;
const prevObjType = prevObj ? prevObj.type : null;
const objType = obj ? obj.type : null;
if (prevObjType && prevObjType !== objType) {
// Hover out
const fn = state[`on${prevObjType}Hover`];
fn && fn(null, prevObj.d);
}
if (objType) {
// Hover in
const fn = state[`on${objType}Hover`];
fn && fn(obj.d, prevObjType === objType ? prevObj.d : null);
}
const tooltipContent = obj ? accessorFn(state[`${obj.type.toLowerCase()}Label`])(obj.d) || '' : '';
toolTipElem.style.visibility = tooltipContent ? 'visible' : 'hidden';
toolTipElem.innerHTML = tooltipContent;
// set pointer if hovered object is clickable
state.canvas.classList[
((obj && state[`on${objType}Click`]) || (!obj && state.onBackgroundClick)) ? 'add' : 'remove'
]('clickable');
state.hoverObj = obj;
}
doRedraw && refreshShadowCanvas();
}
if(doRedraw) {
// Wipe canvas
clearCanvas(ctx, state.width, state.height);
// Frame cycle
const globalScale = d3ZoomTransform(state.canvas).k;
state.onRenderFramePre && state.onRenderFramePre(ctx, globalScale);
state.forceGraph.globalScale(globalScale).tickFrame();
state.onRenderFramePost && state.onRenderFramePost(ctx, globalScale);
}
TWEEN.update(); // update canvas animation tweens
state.animationFrameRequestId = requestAnimationFrame(animate);
})();
},
update: function updateFn(state) {}
});

196
VISUALIZACION/node_modules/force-graph/src/index.d.ts generated vendored Executable file
View file

@ -0,0 +1,196 @@
export interface GraphData {
nodes: NodeObject[];
links: LinkObject[];
}
export interface NodeObject {
id?: string | number;
x?: number;
y?: number;
vx?: number;
vy?: number;
fx?: number;
fy?: number;
}
export interface LinkObject {
source?: string | number | NodeObject;
target?: string | number | NodeObject;
}
type Accessor<In, Out> = Out | string | ((obj: In) => Out);
type NodeAccessor<T> = Accessor<NodeObject, T>;
type LinkAccessor<T> = Accessor<LinkObject, T>;
type CanvasCustomRenderMode = 'replace' | 'before' | 'after';
export type CanvasCustomRenderModeFn<T> = (obj: T) => CanvasCustomRenderMode | any;
export type CanvasCustomRenderFn<T> = (obj: T, canvasContext: CanvasRenderingContext2D, globalScale: number) => void;
export type CanvasPointerAreaPaintFn<T> = (obj: T, paintColor: string, canvasContext: CanvasRenderingContext2D, globalScale: number) => void;
type DagMode = 'td' | 'bu' | 'lr' | 'rl' | 'radialout' | 'radialin';
interface ForceFn {
(alpha: number): void;
initialize?: (nodes: NodeObject[], ...args: any[]) => void;
[key: string]: any;
}
export interface ForceGraphGenericInstance<ChainableInstance> {
(element: HTMLElement): ChainableInstance;
resetProps(): ChainableInstance;
_destructor(): void;
// Data input
graphData(): GraphData;
graphData(data: GraphData): ChainableInstance;
nodeId(): string;
nodeId(id: string): ChainableInstance;
linkSource(): string;
linkSource(source: string): ChainableInstance;
linkTarget(): string;
linkTarget(target: string): ChainableInstance;
// Container layout
width(): number;
width(width: number): ChainableInstance;
height(): number;
height(height: number): ChainableInstance;
backgroundColor(): string;
backgroundColor(color?: string): ChainableInstance;
// Node styling
nodeRelSize(): number;
nodeRelSize(size: number): ChainableInstance;
nodeVal(): NodeAccessor<number>;
nodeVal(valAccessor: NodeAccessor<number>): ChainableInstance;
nodeLabel(): NodeAccessor<string>;
nodeLabel(labelAccessor: NodeAccessor<string>): ChainableInstance;
nodeVisibility(): NodeAccessor<boolean>;
nodeVisibility(visibilityAccessor: NodeAccessor<boolean>): ChainableInstance;
nodeColor(): NodeAccessor<string>;
nodeColor(colorAccessor: NodeAccessor<string>): ChainableInstance;
nodeAutoColorBy(): NodeAccessor<string | null>;
nodeAutoColorBy(colorByAccessor: NodeAccessor<string | null>): ChainableInstance;
nodeCanvasObject(): CanvasCustomRenderFn<NodeObject>;
nodeCanvasObject(renderFn: CanvasCustomRenderFn<NodeObject>): ChainableInstance;
nodeCanvasObjectMode(): string | CanvasCustomRenderModeFn<NodeObject>;
nodeCanvasObjectMode(modeAccessor: string | CanvasCustomRenderModeFn<NodeObject>): ChainableInstance;
nodePointerAreaPaint(): CanvasPointerAreaPaintFn<NodeObject>;
nodePointerAreaPaint(renderFn: CanvasPointerAreaPaintFn<NodeObject>): ChainableInstance;
// Link styling
linkLabel(): LinkAccessor<string>;
linkLabel(labelAccessor: LinkAccessor<string>): ChainableInstance;
linkVisibility(): LinkAccessor<boolean>;
linkVisibility(visibilityAccessor: LinkAccessor<boolean>): ChainableInstance;
linkColor(): LinkAccessor<string>;
linkColor(colorAccessor: LinkAccessor<string>): ChainableInstance;
linkAutoColorBy(): LinkAccessor<string | null>;
linkAutoColorBy(colorByAccessor: LinkAccessor<string | null>): ChainableInstance;
linkLineDash(): LinkAccessor<number[] | null>;
linkLineDash(linkLineDashAccessor: LinkAccessor<number[] | null>): ChainableInstance;
linkWidth(): LinkAccessor<number>;
linkWidth(widthAccessor: LinkAccessor<number>): ChainableInstance;
linkCurvature(): LinkAccessor<number>;
linkCurvature(curvatureAccessor: LinkAccessor<number>): ChainableInstance;
linkCanvasObject(): CanvasCustomRenderFn<LinkObject>;
linkCanvasObject(renderFn: CanvasCustomRenderFn<LinkObject>): ChainableInstance;
linkCanvasObjectMode(): string | CanvasCustomRenderModeFn<LinkObject>;
linkCanvasObjectMode(modeAccessor: string | CanvasCustomRenderModeFn<LinkObject>): ChainableInstance;
linkDirectionalArrowLength(): LinkAccessor<number>;
linkDirectionalArrowLength(lengthAccessor: LinkAccessor<number>): ChainableInstance;
linkDirectionalArrowColor(): LinkAccessor<string>;
linkDirectionalArrowColor(colorAccessor: LinkAccessor<string>): ChainableInstance;
linkDirectionalArrowRelPos(): LinkAccessor<number>;
linkDirectionalArrowRelPos(fractionAccessor: LinkAccessor<number>): ChainableInstance;
linkDirectionalParticles(): LinkAccessor<number>;
linkDirectionalParticles(numParticlesAccessor: LinkAccessor<number>): ChainableInstance;
linkDirectionalParticleSpeed(): LinkAccessor<number>;
linkDirectionalParticleSpeed(relDistancePerFrameAccessor: LinkAccessor<number>): ChainableInstance;
linkDirectionalParticleWidth(): LinkAccessor<number>;
linkDirectionalParticleWidth(widthAccessor: LinkAccessor<number>): ChainableInstance;
linkDirectionalParticleColor(): LinkAccessor<string>;
linkDirectionalParticleColor(colorAccessor: LinkAccessor<string>): ChainableInstance;
emitParticle(link: LinkObject): ChainableInstance;
linkPointerAreaPaint(): CanvasPointerAreaPaintFn<LinkObject>;
linkPointerAreaPaint(renderFn: CanvasPointerAreaPaintFn<LinkObject>): ChainableInstance;
// Render control
autoPauseRedraw(): boolean;
autoPauseRedraw(enable?: boolean): ChainableInstance;
pauseAnimation(): ChainableInstance;
resumeAnimation(): ChainableInstance;
centerAt(): {x: number, y: number};
centerAt(x?: number, y?: number, durationMs?: number): ChainableInstance;
zoom(): number;
zoom(scale: number, durationMs?: number): ChainableInstance;
zoomToFit(durationMs?: number, padding?: number, nodeFilter?: (node: NodeObject) => boolean): ChainableInstance;
minZoom(): number;
minZoom(scale: number): ChainableInstance;
maxZoom(): number;
maxZoom(scale: number): ChainableInstance;
onRenderFramePre(callback: (canvasContext: CanvasRenderingContext2D, globalScale: number) => void): ChainableInstance;
onRenderFramePost(callback: (canvasContext: CanvasRenderingContext2D, globalScale: number) => void): ChainableInstance;
// Force engine (d3-force) configuration
dagMode(): DagMode | null;
dagMode(mode: DagMode | null): ChainableInstance;
dagLevelDistance(): number | null;
dagLevelDistance(distance: number): ChainableInstance;
dagNodeFilter(): (node: NodeObject) => boolean;
dagNodeFilter(filterFn: (node: NodeObject) => boolean): ChainableInstance;
onDagError(): (loopNodeIds: (string | number)[]) => void;
onDagError(errorHandleFn: (loopNodeIds: (string | number)[]) => void): ChainableInstance;
d3AlphaMin(): number;
d3AlphaMin(alphaMin: number): ChainableInstance;
d3AlphaDecay(): number;
d3AlphaDecay(alphaDecay: number): ChainableInstance;
d3VelocityDecay(): number;
d3VelocityDecay(velocityDecay: number): ChainableInstance;
d3Force(forceName: 'link' | 'charge' | 'center' | string): ForceFn | undefined;
d3Force(forceName: 'link' | 'charge' | 'center' | string, forceFn: ForceFn | null): ChainableInstance;
d3ReheatSimulation(): ChainableInstance;
warmupTicks(): number;
warmupTicks(ticks: number): ChainableInstance;
cooldownTicks(): number;
cooldownTicks(ticks: number): ChainableInstance;
cooldownTime(): number;
cooldownTime(milliseconds: number): ChainableInstance;
onEngineTick(callback: () => void): ChainableInstance;
onEngineStop(callback: () => void): ChainableInstance;
// Interaction
onNodeClick(callback: (node: NodeObject, event: MouseEvent) => void): ChainableInstance;
onNodeRightClick(callback: (node: NodeObject, event: MouseEvent) => void): ChainableInstance;
onNodeHover(callback: (node: NodeObject | null, previousNode: NodeObject | null) => void): ChainableInstance;
onNodeDrag(callback: (node: NodeObject, translate: { x: number, y: number }) => void): ChainableInstance;
onNodeDragEnd(callback: (node: NodeObject, translate: { x: number, y: number }) => void): ChainableInstance;
onLinkClick(callback: (link: LinkObject, event: MouseEvent) => void): ChainableInstance;
onLinkRightClick(callback: (link: LinkObject, event: MouseEvent) => void): ChainableInstance;
onLinkHover(callback: (link: LinkObject | null, previousLink: LinkObject | null) => void): ChainableInstance;
linkHoverPrecision(): number;
linkHoverPrecision(precision: number): ChainableInstance;
onBackgroundClick(callback: (event: MouseEvent) => void): ChainableInstance;
onBackgroundRightClick(callback: (event: MouseEvent) => void): ChainableInstance;
onZoom(callback: (transform: {k: number, x: number, y: number}) => void): ChainableInstance;
onZoomEnd(callback: (transform: {k: number, x: number, y: number}) => void): ChainableInstance;
enableNodeDrag(): boolean;
enableNodeDrag(enable: boolean): ChainableInstance;
enableZoomInteraction(): boolean;
enableZoomInteraction(enable: boolean): ChainableInstance;
enablePanInteraction(): boolean;
enablePanInteraction(enable: boolean): ChainableInstance;
enablePointerInteraction(): boolean;
enablePointerInteraction(enable?: boolean): ChainableInstance;
// Utility
getGraphBbox(nodeFilter?: (node: NodeObject) => boolean): { x: [number, number], y: [number, number] };
screen2GraphCoords(x: number, y: number): { x: number, y: number };
graph2ScreenCoords(x: number, y: number): { x: number, y: number };
}
export type ForceGraphInstance = ForceGraphGenericInstance<ForceGraphInstance>;
declare function ForceGraph(): ForceGraphInstance;
export default ForceGraph;

3
VISUALIZACION/node_modules/force-graph/src/index.js generated vendored Executable file
View file

@ -0,0 +1,3 @@
import './force-graph.css';
export { default } from "./force-graph";

34
VISUALIZACION/node_modules/force-graph/src/kapsule-link.js generated vendored Executable file
View file

@ -0,0 +1,34 @@
export default function(kapsulePropNames, kapsuleType) {
const propNames = kapsulePropNames instanceof Array ? kapsulePropNames : [kapsulePropNames];
const dummyK = new kapsuleType(); // To extract defaults
return {
linkProp: function(prop) { // link property config
return {
default: dummyK[prop](),
onChange(v, state) { propNames.forEach(propName => state[propName][prop](v)) },
triggerUpdate: false
}
},
linkMethod: function(method) { // link method pass-through
return function(state, ...args) {
const returnVals = [];
propNames.forEach(propName => {
const kapsuleInstance = state[propName];
const returnVal = kapsuleInstance[method](...args);
if (returnVal !== kapsuleInstance) {
returnVals.push(returnVal);
}
});
return returnVals.length
? returnVals[0]
: this; // chain based on the parent object, not the inner kapsule
}
}
}
}