Skip to content

Instantly share code, notes, and snippets.

@vasturiano
Last active October 23, 2025 13:36
Show Gist options
  • Save vasturiano/02affe306ce445e423f992faeea13521 to your computer and use it in GitHub Desktop.
Save vasturiano/02affe306ce445e423f992faeea13521 to your computer and use it in GitHub Desktop.

Revisions

  1. vasturiano revised this gist Jun 26, 2019. 2 changed files with 4 additions and 8 deletions.
    9 changes: 3 additions & 6 deletions data-set-loader.js
    Original file line number Diff line number Diff line change
    @@ -13,7 +13,7 @@ function getGraphDataSets() {
    //

    const loadBlocks = function(Graph) {
    qwest.get('.blocks.json').then((_, data) => {
    fetch('.blocks.json').then(r => r.json()).then(data => {
    data.nodes.forEach(node => { node.name = `${node.user?node.user+': ':''}${node.description || node.id}` });

    Graph
    @@ -29,12 +29,9 @@ function getGraphDataSets() {
    //

    const loadD3Dependencies = function(Graph) {
    qwest.get('.d3.csv').then((_, csvData) => {
    const { data: [, ...data] } = Papa.parse(csvData); // Parse csv
    data.pop(); // Remove last empty row

    fetch('.d3.csv').then(r => r.text()).then(d3.csvParse).then(data => {
    const nodes = [], links = [];
    data.forEach(([size, path]) => {
    data.forEach(({ size, path }) => {
    const levels = path.split('/'),
    module = levels.length > 1 ? levels[1] : null,
    leaf = levels.pop(),
    3 changes: 1 addition & 2 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,6 @@
    <head>
    <script src="//unpkg.com/3d-force-graph@1"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/qwest/4.4.5/qwest.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>
    <script src="//unpkg.com/d3-dsv"></script>

    <script src="data-set-loader.js"></script>

  2. vasturiano revised this gist Mar 18, 2018. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -9,11 +9,12 @@
    </head>

    <body>
    <div id="3d-graph"></div>

    <div class="graph-data">
    <span id="graph-data-description"></span>
    <button class="toggle-data-btn" onClick="toggleData()">Show me something else</button>
    </div>
    <div id="3d-graph"></div>

    <script src="index.js"></script>
    </body>
  3. vasturiano revised this gist Jan 5, 2018. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions data-set-loader.js
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@ function getGraphDataSets() {
    Graph
    .cooldownTicks(200)
    .nodeLabel('id')
    .autoColorBy('group')
    .nodeAutoColorBy('group')
    .forceEngine('ngraph')
    .jsonUrl('.miserables.json');
    };
    @@ -19,7 +19,7 @@ function getGraphDataSets() {
    Graph
    .cooldownTicks(300)
    .cooldownTime(20000)
    .autoColorBy('user')
    .nodeAutoColorBy('user')
    .forceEngine('ngraph')
    .graphData(data);
    });
    @@ -58,7 +58,7 @@ function getGraphDataSets() {
    .nodeId('path')
    .nodeVal('size')
    .nodeLabel('path')
    .autoColorBy('module')
    .nodeAutoColorBy('module')
    .forceEngine('ngraph')
    .graphData({ nodes: nodes, links: links });
    });
  4. vasturiano revised this gist Jan 2, 2018. 2 changed files with 5 additions and 5 deletions.
    8 changes: 4 additions & 4 deletions data-set-loader.js
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@ function getGraphDataSets() {
    const loadMiserables = function(Graph) {
    Graph
    .cooldownTicks(200)
    .nameField('id')
    .nodeLabel('id')
    .autoColorBy('group')
    .forceEngine('ngraph')
    .jsonUrl('.miserables.json');
    @@ -55,9 +55,9 @@ function getGraphDataSets() {
    Graph
    .cooldownTicks(300)
    .nodeRelSize(0.5)
    .idField('path')
    .valField('size')
    .nameField('path')
    .nodeId('path')
    .nodeVal('size')
    .nodeLabel('path')
    .autoColorBy('module')
    .forceEngine('ngraph')
    .graphData({ nodes: nodes, links: links });
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    <head>
    <script src="//unpkg.com/3d-force-graph@1/dist/3d-force-graph.min.js"></script>
    <script src="//unpkg.com/3d-force-graph@1"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/qwest/4.4.5/qwest.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>

  5. vasturiano revised this gist Sep 19, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    <head>
    <script src="//unpkg.com/3d-force-graph@1.5/dist/3d-force-graph.min.js"></script>
    <script src="//unpkg.com/3d-force-graph@1/dist/3d-force-graph.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/qwest/4.4.5/qwest.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>

  6. vasturiano revised this gist Jul 4, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    <head>
    <script src="//unpkg.com/3d-force-graph@1.4/dist/3d-force-graph.min.js"></script>
    <script src="//unpkg.com/3d-force-graph@1.5/dist/3d-force-graph.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/qwest/4.4.5/qwest.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>

  7. vasturiano revised this gist Jul 4, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    <head>
    <script src="//unpkg.com/3d-force-graph@1.3/dist/3d-force-graph.min.js"></script>
    <script src="//unpkg.com/3d-force-graph@1.4/dist/3d-force-graph.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/qwest/4.4.5/qwest.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>

  8. vasturiano revised this gist Jun 28, 2017. 1 changed file with 1 addition and 3 deletions.
    4 changes: 1 addition & 3 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,4 @@

    Cycle through various topological data sets using the button on the top-right.

    See also [this version](https://bl.ocks.org/vasturiano/f59675656258d3f490e9faa40828c0e7) for 3D simulation using D3-force.

    And the [VR version](https://bl.ocks.org/vasturiano/972ca4f3e8e074dacf14d7071aad8ef9).
    See also the [VR version](https://bl.ocks.org/vasturiano/972ca4f3e8e074dacf14d7071aad8ef9).
  9. vasturiano revised this gist Jun 21, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    <head>
    <script src="//unpkg.com/3d-force-graph@1.2/dist/3d-force-graph.js"></script>
    <script src="//unpkg.com/3d-force-graph@1.3/dist/3d-force-graph.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/qwest/4.4.5/qwest.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>

  10. vasturiano revised this gist Jun 21, 2017. 4 changed files with 3 additions and 3 deletions.
    File renamed without changes.
    File renamed without changes.
    File renamed without changes.
    6 changes: 3 additions & 3 deletions data-set-loader.js
    Original file line number Diff line number Diff line change
    @@ -6,14 +6,14 @@ function getGraphDataSets() {
    .nameField('id')
    .autoColorBy('group')
    .forceEngine('ngraph')
    .jsonUrl('miserables.json');
    .jsonUrl('.miserables.json');
    };
    loadMiserables.description = "<em>Les Miserables</em> data (<a href='https://bl.ocks.org/mbostock/4062045'>4062045</a>)";

    //

    const loadBlocks = function(Graph) {
    qwest.get('blocks.json').then((_, data) => {
    qwest.get('.blocks.json').then((_, data) => {
    data.nodes.forEach(node => { node.name = `${node.user?node.user+': ':''}${node.description || node.id}` });

    Graph
    @@ -29,7 +29,7 @@ function getGraphDataSets() {
    //

    const loadD3Dependencies = function(Graph) {
    qwest.get('d3.csv').then((_, csvData) => {
    qwest.get('.d3.csv').then((_, csvData) => {
    const { data: [, ...data] } = Papa.parse(csvData); // Parse csv
    data.pop(); // Remove last empty row

  11. vasturiano revised this gist May 10, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    <head>
    <script src="//unpkg.com/3d-force-graph@1.2/dist/3d-force-graph.js"></script>
    <script src="//unpkg.com/3d-force-graph@1.3/dist/3d-force-graph.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/qwest/4.4.5/qwest.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>

  12. vasturiano revised this gist May 1, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    3-dimensional representation of a force-directed iterative layout, using [3d-force-graph](https://github.com/vasturiano/3d-force-graph). This component uses [ThreeJS](https://github.com/mrdoob/three.js/)/WebGL for rendering and [ngraph](https://github.com/anvaka/ngraph.forcelayout3d) for the 3D physics engine.
    3-dimensional representation of a force-directed iterative layout, using [3d-force-graph](https://github.com/vasturiano/3d-force-graph). This component uses [ThreeJS](https://github.com/mrdoob/three.js/)/WebGL for rendering and either [d3-force-3d](https://github.com/vasturiano/d3-force-3d) or [ngraph](https://github.com/anvaka/ngraph.forcelayout3d) for the 3D physics engine.

    Cycle through various topological data sets using the button on the top-right.

  13. vasturiano revised this gist May 1, 2017. 2 changed files with 5 additions and 1 deletion.
    4 changes: 4 additions & 0 deletions data-set-loader.js
    Original file line number Diff line number Diff line change
    @@ -5,6 +5,7 @@ function getGraphDataSets() {
    .cooldownTicks(200)
    .nameField('id')
    .autoColorBy('group')
    .forceEngine('ngraph')
    .jsonUrl('miserables.json');
    };
    loadMiserables.description = "<em>Les Miserables</em> data (<a href='https://bl.ocks.org/mbostock/4062045'>4062045</a>)";
    @@ -19,6 +20,7 @@ function getGraphDataSets() {
    .cooldownTicks(300)
    .cooldownTime(20000)
    .autoColorBy('user')
    .forceEngine('ngraph')
    .graphData(data);
    });
    };
    @@ -57,6 +59,7 @@ function getGraphDataSets() {
    .valField('size')
    .nameField('path')
    .autoColorBy('module')
    .forceEngine('ngraph')
    .graphData({ nodes: nodes, links: links });
    });
    };
    @@ -86,6 +89,7 @@ function getGraphDataSets() {

    Graph
    .cooldownTicks(300)
    .forceEngine('ngraph')
    .graphData({ nodes: nodes, links: links });
    };
    tunnel.description = "fabric data for a cylindrical tunnel shape";
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    <head>
    <script src="//unpkg.com/3d-force-graph@1.1/dist/3d-force-graph.js"></script>
    <script src="//unpkg.com/3d-force-graph@1.2/dist/3d-force-graph.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/qwest/4.4.5/qwest.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>

  14. vasturiano revised this gist Apr 30, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    <head>
    <script src="//unpkg.com/3d-force-graph@1.0/dist/3d-force-graph.js"></script>
    <script src="//unpkg.com/3d-force-graph@1.1/dist/3d-force-graph.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/qwest/4.4.5/qwest.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>

  15. vasturiano revised this gist Apr 29, 2017. 2 changed files with 7 additions and 15 deletions.
    20 changes: 6 additions & 14 deletions data-set-loader.js
    Original file line number Diff line number Diff line change
    @@ -1,16 +1,11 @@
    function getGraphDataSets() {

    const loadMiserables = function(Graph) {
    qwest.get('miserables.json').then((_, data) => {
    Graph
    .cooldownTicks(200)
    .nameField('id')
    .autoColorBy('group')
    .graphData({
    nodes: data.nodes,
    links: data.links
    });
    });
    Graph
    .cooldownTicks(200)
    .nameField('id')
    .autoColorBy('group')
    .jsonUrl('miserables.json');
    };
    loadMiserables.description = "<em>Les Miserables</em> data (<a href='https://bl.ocks.org/mbostock/4062045'>4062045</a>)";

    @@ -24,10 +19,7 @@ function getGraphDataSets() {
    .cooldownTicks(300)
    .cooldownTime(20000)
    .autoColorBy('user')
    .graphData({
    nodes: data.nodes,
    links: data.links
    });
    .graphData(data);
    });
    };
    loadBlocks.description = "<em>Blocks</em> data (<a href='https://bl.ocks.org/mbostock/afecf1ce04644ad9036ca146d2084895'>afecf1ce04644ad9036ca146d2084895</a>)";
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    <head>
    <script src="//unpkg.com/3d-force-graph@0.5/dist/3d-force-graph.js"></script>
    <script src="//unpkg.com/3d-force-graph@1.0/dist/3d-force-graph.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/qwest/4.4.5/qwest.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>

  16. vasturiano revised this gist Apr 28, 2017. 1 changed file with 30 additions and 48 deletions.
    78 changes: 30 additions & 48 deletions data-set-loader.js
    Original file line number Diff line number Diff line change
    @@ -1,20 +1,14 @@
    function getGraphDataSets() {

    // Color brewer paired set
    const colors = ['#a6cee3','#1f78b4','#b2df8a','#33a02c','#fb9a99','#e31a1c','#fdbf6f','#ff7f00','#cab2d6','#6a3d9a','#ffff99','#b15928'];

    const loadMiserables = function(Graph) {
    qwest.get('miserables.json').then((_, data) => {
    const nodes = {};
    data.nodes.forEach(node => { nodes[node.id] = node }); // Index by ID

    Graph
    .coolDownTicks(200)
    .nameAccessor(node => node.id)
    .colorAccessor(node => parseInt(colors[node.group%colors.length].slice(1),16))
    .cooldownTicks(200)
    .nameField('id')
    .autoColorBy('group')
    .graphData({
    nodes: nodes,
    links: data.links.map(link => [link.source, link.target])
    nodes: data.nodes,
    links: data.links
    });
    });
    };
    @@ -24,21 +18,15 @@ function getGraphDataSets() {

    const loadBlocks = function(Graph) {
    qwest.get('blocks.json').then((_, data) => {
    const userColors = {};
    Array.from(new Set(data.nodes.map(node => node.user || null))).forEach((user, idx) => {
    userColors[user] = colors[idx%colors.length]; // Rotate colors
    });

    const nodes = {};
    data.nodes.forEach(node => { nodes[node.id] = node }); // Index by ID
    data.nodes.forEach(node => { node.name = `${node.user?node.user+': ':''}${node.description || node.id}` });

    Graph
    .coolDownTicks(600)
    .nameAccessor(node => `${node.user?node.user+': ':''}${node.description || node.id}`)
    .colorAccessor(node => parseInt(userColors[node.user || null].slice(1), 16))
    .cooldownTicks(300)
    .cooldownTime(20000)
    .autoColorBy('user')
    .graphData({
    nodes: nodes,
    links: data.links.map(link => [link.source, link.target])
    nodes: data.nodes,
    links: data.links
    });
    });
    };
    @@ -51,70 +39,64 @@ function getGraphDataSets() {
    const { data: [, ...data] } = Papa.parse(csvData); // Parse csv
    data.pop(); // Remove last empty row

    const nodes = {}, links = [], modules = new Set();
    const nodes = [], links = [];
    data.forEach(([size, path]) => {
    const levels = path.split('/'),
    module = levels.length > 1 ? levels[1] : null,
    leaf = levels.pop(),
    parent = levels.join('/');

    modules.add(module);

    nodes[path] = {
    leaf: leaf,
    module: module,
    path: path,
    nodes.push({
    path,
    leaf,
    module,
    size: +size || 1
    };
    });

    if (parent) {
    links.push([parent, path]);
    links.push({ source: parent, target: path});
    }
    });

    const moduleColors = {};
    Array.from(modules).forEach((module, idx) => {
    moduleColors[module] = colors[idx%colors.length]; // Rotate colors
    });

    Graph
    .coolDownTicks(300)
    .cooldownTicks(300)
    .nodeRelSize(0.5)
    .valAccessor(node => node.size)
    .nameAccessor(node => node.path)
    .colorAccessor(node => parseInt(moduleColors[node.module || null].slice(1), 16))
    .idField('path')
    .valField('size')
    .nameField('path')
    .autoColorBy('module')
    .graphData({ nodes: nodes, links: links });
    });
    };
    loadD3Dependencies.description = "<em>D3 dependencies</em> data (<a href='https://bl.ocks.org/mbostock/9a8124ccde3a4e9625bc413b48f14b30'>9a8124ccde3a4e9625bc413b48f14b30</a>)";

    const tunnel = function(Graph) {

    const perimeter = 15, length = 50;
    const perimeter = 12, length = 30;

    const getId = (col, row) => `${col},${row}`;

    let nodes = {}, links = [];
    let nodes = [], links = [];
    for (let colIdx=0; colIdx<perimeter; colIdx++) {
    for (let rowIdx=0; rowIdx<length; rowIdx++) {
    const id = getId(colIdx, rowIdx);
    nodes[id] = {};
    nodes.push({id});

    // Link vertically
    if (rowIdx>0) {
    links.push([getId(colIdx, rowIdx-1), id]);
    links.push({ source: getId(colIdx, rowIdx-1), target: id });
    }

    // Link horizontally
    links.push([getId((colIdx || perimeter) - 1, rowIdx), id]);
    links.push({ source: getId((colIdx || perimeter) - 1, rowIdx), target: id });
    }
    }

    Graph
    .coolDownTicks(300)
    .cooldownTicks(300)
    .graphData({ nodes: nodes, links: links });
    };
    tunnel.description = "fabric data for a cyllindrical tunnel shape";
    tunnel.description = "fabric data for a cylindrical tunnel shape";

    //

  17. vasturiano revised this gist Apr 21, 2017. 2 changed files with 3 additions and 4 deletions.
    5 changes: 1 addition & 4 deletions data-set-loader.js
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,6 @@ function getGraphDataSets() {
    data.nodes.forEach(node => { nodes[node.id] = node }); // Index by ID

    Graph
    .resetProps()
    .coolDownTicks(200)
    .nameAccessor(node => node.id)
    .colorAccessor(node => parseInt(colors[node.group%colors.length].slice(1),16))
    @@ -34,7 +33,6 @@ function getGraphDataSets() {
    data.nodes.forEach(node => { nodes[node.id] = node }); // Index by ID

    Graph
    .resetProps()
    .coolDownTicks(600)
    .nameAccessor(node => `${node.user?node.user+': ':''}${node.description || node.id}`)
    .colorAccessor(node => parseInt(userColors[node.user || null].slice(1), 16))
    @@ -80,7 +78,6 @@ function getGraphDataSets() {
    });

    Graph
    .resetProps()
    .coolDownTicks(300)
    .nodeRelSize(0.5)
    .valAccessor(node => node.size)
    @@ -113,7 +110,7 @@ function getGraphDataSets() {
    }
    }

    Graph.resetProps()
    Graph
    .coolDownTicks(300)
    .graphData({ nodes: nodes, links: links });
    };
    2 changes: 2 additions & 0 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -9,6 +9,8 @@ let toggleData;
    curDataSetIdx = curDataSetIdx === undefined ? 0 : (curDataSetIdx+1)%dataSets.length;
    const dataSet = dataSets[curDataSetIdx];

    Graph.resetProps(); // Wipe current state
    dataSet(Graph); // Load data set

    document.getElementById('graph-data-description').innerHTML = dataSet.description ? `Viewing ${dataSet.description}` : '';
    })(); // IIFE init
  18. vasturiano revised this gist Apr 21, 2017. 2 changed files with 5 additions and 5 deletions.
    8 changes: 4 additions & 4 deletions data-set-loader.js
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,7 @@ function getGraphDataSets() {
    data.nodes.forEach(node => { nodes[node.id] = node }); // Index by ID

    Graph
    .resetState()
    .resetProps()
    .coolDownTicks(200)
    .nameAccessor(node => node.id)
    .colorAccessor(node => parseInt(colors[node.group%colors.length].slice(1),16))
    @@ -34,7 +34,7 @@ function getGraphDataSets() {
    data.nodes.forEach(node => { nodes[node.id] = node }); // Index by ID

    Graph
    .resetState()
    .resetProps()
    .coolDownTicks(600)
    .nameAccessor(node => `${node.user?node.user+': ':''}${node.description || node.id}`)
    .colorAccessor(node => parseInt(userColors[node.user || null].slice(1), 16))
    @@ -80,7 +80,7 @@ function getGraphDataSets() {
    });

    Graph
    .resetState()
    .resetProps()
    .coolDownTicks(300)
    .nodeRelSize(0.5)
    .valAccessor(node => node.size)
    @@ -113,7 +113,7 @@ function getGraphDataSets() {
    }
    }

    Graph.resetState()
    Graph.resetProps()
    .coolDownTicks(300)
    .graphData({ nodes: nodes, links: links });
    };
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    <head>
    <script src="//unpkg.com/3d-force-graph@0.4/dist/3d-force-graph.js"></script>
    <script src="//unpkg.com/3d-force-graph@0.5/dist/3d-force-graph.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/qwest/4.4.5/qwest.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>

  19. vasturiano revised this gist Apr 20, 2017. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -2,4 +2,6 @@

    Cycle through various topological data sets using the button on the top-right.

    See also [this version](https://bl.ocks.org/vasturiano/f59675656258d3f490e9faa40828c0e7) for 3D simulation using D3-force.
    See also [this version](https://bl.ocks.org/vasturiano/f59675656258d3f490e9faa40828c0e7) for 3D simulation using D3-force.

    And the [VR version](https://bl.ocks.org/vasturiano/972ca4f3e8e074dacf14d7071aad8ef9).
  20. vasturiano revised this gist Apr 13, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    <head>
    <script src="//unpkg.com/3d-force-graph@0.3/dist/3d-force-graph.js"></script>
    <script src="//unpkg.com/3d-force-graph@0.4/dist/3d-force-graph.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/qwest/4.4.5/qwest.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>

  21. vasturiano revised this gist Mar 30, 2017. 2 changed files with 5 additions and 1 deletion.
    4 changes: 4 additions & 0 deletions data-set-loader.js
    Original file line number Diff line number Diff line change
    @@ -10,6 +10,7 @@ function getGraphDataSets() {

    Graph
    .resetState()
    .coolDownTicks(200)
    .nameAccessor(node => node.id)
    .colorAccessor(node => parseInt(colors[node.group%colors.length].slice(1),16))
    .graphData({
    @@ -34,6 +35,7 @@ function getGraphDataSets() {

    Graph
    .resetState()
    .coolDownTicks(600)
    .nameAccessor(node => `${node.user?node.user+': ':''}${node.description || node.id}`)
    .colorAccessor(node => parseInt(userColors[node.user || null].slice(1), 16))
    .graphData({
    @@ -79,6 +81,7 @@ function getGraphDataSets() {

    Graph
    .resetState()
    .coolDownTicks(300)
    .nodeRelSize(0.5)
    .valAccessor(node => node.size)
    .nameAccessor(node => node.path)
    @@ -111,6 +114,7 @@ function getGraphDataSets() {
    }

    Graph.resetState()
    .coolDownTicks(300)
    .graphData({ nodes: nodes, links: links });
    };
    tunnel.description = "fabric data for a cyllindrical tunnel shape";
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    <head>
    <script src="//unpkg.com/3d-force-graph@0.2/dist/3d-force-graph.js"></script>
    <script src="//unpkg.com/3d-force-graph@0.3/dist/3d-force-graph.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/qwest/4.4.5/qwest.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>

  22. vasturiano revised this gist Mar 30, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    <head>
    <script src="//unpkg.com/3d-force-graph@0.1.0/dist/3d-force-graph.js"></script>
    <script src="//unpkg.com/3d-force-graph@0.2/dist/3d-force-graph.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/qwest/4.4.5/qwest.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>

  23. vasturiano revised this gist Mar 27, 2017. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    3-dimensional representation of a force-directed iterative layout, using [3d-force-graph](https://github.com/vasturiano/3d-force-graph). This component uses [ThreeJS](https://github.com/mrdoob/three.js/)/WebGL for rendering and [ngraph](https://github.com/anvaka/ngraph.forcelayout3d) for the 3D physics engine.

    Cycle through various topological data sets using the button on the top-right.
    Cycle through various topological data sets using the button on the top-right.

    See also [this version](https://bl.ocks.org/vasturiano/f59675656258d3f490e9faa40828c0e7) for 3D simulation using D3-force.
  24. vasturiano revised this gist Mar 27, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    <head>
    <script src="//unpkg.com/3d-force-graph@0.0.4/dist/3d-force-graph.js"></script>
    <script src="//unpkg.com/3d-force-graph@0.1.0/dist/3d-force-graph.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/qwest/4.4.5/qwest.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>

  25. vasturiano revised this gist Mar 27, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,3 @@
    3-dimensional representation of a force-directed iterative layout, using [3d-force-graph](https://github.com/vasturiano/3d-force-graph). This wrapper component uses [ThreeJS](https://github.com/mrdoob/three.js/)/WebGL for rendering and [ngraph](https://github.com/anvaka/ngraph.forcelayout3d) for the 3D physics engine.
    3-dimensional representation of a force-directed iterative layout, using [3d-force-graph](https://github.com/vasturiano/3d-force-graph). This component uses [ThreeJS](https://github.com/mrdoob/three.js/)/WebGL for rendering and [ngraph](https://github.com/anvaka/ngraph.forcelayout3d) for the 3D physics engine.

    Cycle through various topological data sets using the button on the top-right.
  26. vasturiano revised this gist Mar 26, 2017. 4 changed files with 1 addition and 280 deletions.
    15 changes: 0 additions & 15 deletions 3d-force-graph.css
    Original file line number Diff line number Diff line change
    @@ -1,15 +0,0 @@
    .graph-nav-info {
    position: absolute;
    bottom: 5px;
    width: 100%;
    text-align: center;
    color: slategrey;
    opacity: 0.7;
    font-size: 10px;
    }

    .graph-tooltip {
    position: absolute;
    color: lavender;
    font-size: 18px;
    }
    258 changes: 0 additions & 258 deletions 3d-force-graph.js
    Original file line number Diff line number Diff line change
    @@ -1,258 +0,0 @@
    function ForceGraph3D() {

    const CAMERA_DISTANCE2NODES_FACTOR = 150;

    class CompProp {
    constructor(name, initVal = null, redigest = true, onChange = newVal => {}) {
    this.name = name;
    this.initVal = initVal;
    this.redigest = redigest;
    this.onChange = onChange;
    }
    }

    const env = { // Holds component state
    initialised: false,
    onFrame: () => {}
    };

    const exposeProps = [
    new CompProp('width', window.innerWidth),
    new CompProp('height', window.innerHeight),
    new CompProp('graphData', {
    nodes: { 1: { name: 'mock', val: 1 } },
    links: [[1, 1]] // [from, to]
    }),
    new CompProp('nodeRelSize', 4), // volume per val unit
    new CompProp('lineOpacity', 0.2),
    new CompProp('valAccessor', node => node.val),
    new CompProp('nameAccessor', node => node.name),
    new CompProp('colorAccessor', node => node.color),
    new CompProp('initialEngineTicks', 0), // how many times to tick the force engine at init before starting to render
    new CompProp('maxConvergeTime', 15000), // ms
    new CompProp('maxConvergeFrames', 300)
    ];

    function initStatic() {
    // Wipe DOM
    env.domNode.innerHTML = '';

    // Add nav info section
    const navInfo = document.createElement('div');
    navInfo.classList.add('graph-nav-info');
    navInfo.innerHTML = "MOVE mouse &amp; press LEFT/A: rotate, MIDDLE/S: zoom, RIGHT/D: pan";
    env.domNode.appendChild(navInfo);

    // Setup tooltip
    env.toolTipElem = document.createElement('div');
    env.toolTipElem.classList.add('graph-tooltip');
    env.domNode.appendChild(env.toolTipElem);

    // Capture mouse coords on move
    env.raycaster = new THREE.Raycaster();
    env.mouse = new THREE.Vector2();
    env.mouse.x = -2; // Initialize off canvas
    env.mouse.y = -2;
    env.domNode.addEventListener("mousemove", ev => {
    // update the mouse pos
    const offset = getOffset(env.domNode),
    relPos = {
    x: ev.pageX - offset.left,
    y: ev.pageY - offset.top
    };
    env.mouse.x = (relPos.x / env.width) * 2 - 1;
    env.mouse.y = -(relPos.y / env.height) * 2 + 1;

    // Move tooltip
    env.toolTipElem.style.top = (relPos.y - 40) + 'px';
    env.toolTipElem.style.left = (relPos.x - 20) + 'px';

    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 };
    }
    }, false);

    // Setup camera
    env.camera = new THREE.PerspectiveCamera();
    env.camera.far = 20000;
    env.camera.position.z = 1000;

    // Setup scene
    env.scene = new THREE.Scene();

    // Setup renderer
    env.renderer = new THREE.WebGLRenderer();
    env.domNode.appendChild(env.renderer.domElement);

    // Add camera interaction
    env.controls = new THREE.TrackballControls(env.camera, env.renderer.domElement);


    env.initialised = true;

    //

    // Kick-off renderer
    (function animate() { // IIFE
    env.onFrame();

    // Update tooltip
    env.raycaster.setFromCamera(env.mouse, env.camera);
    const intersects = env.raycaster.intersectObjects(env.scene.children);
    env.toolTipElem.innerHTML = intersects.length ? intersects[0].object.name || '' : '';

    // Frame cycle
    env.controls.update();
    env.renderer.render(env.scene, env.camera);
    requestAnimationFrame(animate);
    })()
    }

    function digest() {
    if (!env.initialised) { return }

    resizeCanvas();

    env.onFrame = ()=>{}; // Clear previous frame hook
    env.scene = new THREE.Scene(); // Clear the place

    // Build graph with data
    const graph = ngraph.graph();
    for (let nodeId in env.graphData.nodes) {
    graph.addNode(nodeId, env.graphData.nodes[nodeId]);
    }
    for (let link of env.graphData.links) {
    graph.addLink(...link, {});
    }

    // Add WebGL objects
    graph.forEachNode(node => {
    const nodeMaterial = new THREE.MeshBasicMaterial({ color: env.colorAccessor(node.data) || 0xffffaa, transparent: true });
    nodeMaterial.opacity = 0.75;

    const sphere = new THREE.Mesh(
    new THREE.SphereGeometry(Math.cbrt(env.valAccessor(node.data) || 1) * env.nodeRelSize),
    nodeMaterial
    );
    sphere.name = env.nameAccessor(node.data) || '';

    env.scene.add(node.data.sphere = sphere)
    });

    const lineMaterial = new THREE.MeshBasicMaterial({ color: 0xf0f0f0, transparent: true });
    lineMaterial.opacity = env.lineOpacity;
    graph.forEachLink(link => {
    const line = new THREE.Line(new THREE.Geometry(), lineMaterial);
    line.name = `${getNodeName(link.fromId)} > ${getNodeName(link.toId)}`;

    env.scene.add(link.data.line = line)

    function getNodeName(nodeId) {
    return env.nameAccessor(graph.getNode(nodeId).data);
    }
    });

    env.camera.lookAt(env.scene.position);
    env.camera.position.z = Math.cbrt(Object.keys(env.graphData.nodes).length) * CAMERA_DISTANCE2NODES_FACTOR;

    // Add force-directed layout
    const layout = ngraph.forcelayout3d(graph);

    for (let i=0; i<env.initialEngineTicks; i++) { layout.step(); } // Initial ticks before starting to render

    let cntTicks = 0;
    const startTickTime = new Date();
    env.onFrame = layoutTick;

    //

    function resizeCanvas() {
    if (env.width && env.height) {
    env.renderer.setSize(env.width, env.height);
    env.camera.aspect = env.width/env.height;
    env.camera.updateProjectionMatrix();
    }
    }

    function layoutTick() {
    if (cntTicks++ > env.maxConvergeFrames || (new Date()) - startTickTime > env.maxConvergeTime) {
    env.onFrame = ()=>{}; // Stop ticking graph
    }

    layout.step(); // Tick it

    // Update nodes position
    graph.forEachNode(node => {
    const sphere = node.data.sphere,
    pos = layout.getNodePosition(node.id);

    sphere.position.x = pos.x;
    sphere.position.y = pos.y;
    sphere.position.z = pos.z;
    });

    // Update links position
    graph.forEachLink(link => {
    const line = link.data.line,
    pos = layout.getLinkPosition(link.id);

    line.geometry.vertices = [
    new THREE.Vector3(pos.from.x, pos.from.y, pos.from.z),
    new THREE.Vector3(pos.to.x, pos.to.y, pos.to.z)
    ];

    line.geometry.verticesNeedUpdate = true;
    line.geometry.computeBoundingSphere();
    });
    }
    }

    // Component constructor
    function chart(nodeElement) {
    env.domNode = nodeElement;

    initStatic();
    digest();

    return chart;
    }

    // Getter/setter methods
    exposeProps.forEach(prop => {
    chart[prop.name] = getSetEnv(prop.name, prop.redigest, prop.onChange);
    env[prop.name] = prop.initVal;
    prop.onChange(prop.initVal);

    function getSetEnv(prop, redigest = false, onChange = newVal => {}) {
    return _ => {
    if (!arguments.length) { return env[prop] }
    env[prop] = _;
    onChange(_);
    if (redigest) { digest() }
    return chart;
    }
    }
    });

    // Reset to default state
    chart.resetState = function() {
    this.graphData({nodes: [], links: []})
    .nodeRelSize(4)
    .lineOpacity(0.2)
    .valAccessor(node => node.val)
    .nameAccessor(node => node.name)
    .colorAccessor(node => node.color)
    .initialEngineTicks(0)
    .maxConvergeTime(15000) // ms
    .maxConvergeFrames(300);

    return this;
    };

    chart.resetState(); // Set defaults at instantiation

    return chart;
    }
    6 changes: 1 addition & 5 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -1,14 +1,10 @@
    <head>
    <script src="//cdnjs.cloudflare.com/ajax/libs/three.js/84/three.min.js"></script>
    <script src="//unpkg.com/[email protected]/dist/three-trackballcontrols.min.js"></script>
    <script src="//unpkg.com/[email protected]/dist/forcelayout3d.min.js"></script>
    <script src="//unpkg.com/[email protected]/dist/3d-force-graph.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/qwest/4.4.5/qwest.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>

    <script src="data-set-loader.js"></script>
    <script src="3d-force-graph.js"></script>

    <link rel="stylesheet" href="3d-force-graph.css">
    <link rel="stylesheet" href="style.css">
    </head>

    2 changes: 0 additions & 2 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,3 @@
    THREE.TrackballControls = TrackballControls; //Link module to Three

    const Graph = ForceGraph3D()
    (document.getElementById("3d-graph"));

  27. vasturiano revised this gist Mar 26, 2017. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion 3d-force-graph.js
    Original file line number Diff line number Diff line change
    @@ -205,7 +205,8 @@ function ForceGraph3D() {
    ];

    line.geometry.verticesNeedUpdate = true;
    })
    line.geometry.computeBoundingSphere();
    });
    }
    }

  28. vasturiano revised this gist Mar 7, 2017. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions data-set-loader.js
    Original file line number Diff line number Diff line change
    @@ -90,13 +90,13 @@ function getGraphDataSets() {

    const tunnel = function(Graph) {

    const perimeter = 15, height = 50;
    const perimeter = 15, length = 50;

    const getId = (col, row) => `${col},${row}`;

    let nodes = {}, links = [];
    for (let colIdx=0; colIdx<perimeter; colIdx++) {
    for (let rowIdx=0; rowIdx<height; rowIdx++) {
    for (let rowIdx=0; rowIdx<length; rowIdx++) {
    const id = getId(colIdx, rowIdx);
    nodes[id] = {};

  29. vasturiano revised this gist Mar 7, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion data-set-loader.js
    Original file line number Diff line number Diff line change
    @@ -113,7 +113,7 @@ function getGraphDataSets() {
    Graph.resetState()
    .graphData({ nodes: nodes, links: links });
    };
    tunnel.description = "texture data for a cyllindrical tunnel shape";
    tunnel.description = "fabric data for a cyllindrical tunnel shape";

    //

  30. vasturiano revised this gist Mar 7, 2017. 1 changed file with 28 additions and 2 deletions.
    30 changes: 28 additions & 2 deletions data-set-loader.js
    Original file line number Diff line number Diff line change
    @@ -46,7 +46,7 @@ function getGraphDataSets() {

    //

    const loadD3Dependencies= function(Graph) {
    const loadD3Dependencies = function(Graph) {
    qwest.get('d3.csv').then((_, csvData) => {
    const { data: [, ...data] } = Papa.parse(csvData); // Parse csv
    data.pop(); // Remove last empty row
    @@ -88,8 +88,34 @@ function getGraphDataSets() {
    };
    loadD3Dependencies.description = "<em>D3 dependencies</em> data (<a href='https://bl.ocks.org/mbostock/9a8124ccde3a4e9625bc413b48f14b30'>9a8124ccde3a4e9625bc413b48f14b30</a>)";

    const tunnel = function(Graph) {

    const perimeter = 15, height = 50;

    const getId = (col, row) => `${col},${row}`;

    let nodes = {}, links = [];
    for (let colIdx=0; colIdx<perimeter; colIdx++) {
    for (let rowIdx=0; rowIdx<height; rowIdx++) {
    const id = getId(colIdx, rowIdx);
    nodes[id] = {};

    // Link vertically
    if (rowIdx>0) {
    links.push([getId(colIdx, rowIdx-1), id]);
    }

    // Link horizontally
    links.push([getId((colIdx || perimeter) - 1, rowIdx), id]);
    }
    }

    Graph.resetState()
    .graphData({ nodes: nodes, links: links });
    };
    tunnel.description = "texture data for a cyllindrical tunnel shape";

    //

    return [loadMiserables, loadBlocks, loadD3Dependencies];
    return [loadMiserables, loadBlocks, loadD3Dependencies, tunnel];
    }