New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

three-forcegraph

Package Overview
Dependencies
Maintainers
1
Versions
145
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

three-forcegraph - npm Package Compare versions

Comparing version 1.1.0 to 1.1.1

440

dist/three-forcegraph.common.js

@@ -17,25 +17,25 @@ 'use strict';

var colorStr2Hex = function colorStr2Hex(str) {
return isNaN(str) ? parseInt(tinyColor(str).toHex(), 16) : str;
return isNaN(str) ? parseInt(tinyColor(str).toHex(), 16) : str;
};
function autoColorNodes(nodes, colorByAccessor, colorField) {
if (!colorByAccessor || typeof colorField !== 'string') return;
if (!colorByAccessor || typeof colorField !== 'string') return;
var colors = d3ScaleChromatic.schemePaired; // Paired color set from color brewer
var colors = d3ScaleChromatic.schemePaired; // Paired color set from color brewer
var uncoloredNodes = nodes.filter(function (node) {
return !node[colorField];
});
var nodeGroups = {};
var uncoloredNodes = nodes.filter(function (node) {
return !node[colorField];
});
var nodeGroups = {};
uncoloredNodes.forEach(function (node) {
nodeGroups[colorByAccessor(node)] = null;
});
Object.keys(nodeGroups).forEach(function (group, idx) {
nodeGroups[group] = idx;
});
uncoloredNodes.forEach(function (node) {
nodeGroups[colorByAccessor(node)] = null;
});
Object.keys(nodeGroups).forEach(function (group, idx) {
nodeGroups[group] = idx;
});
uncoloredNodes.forEach(function (node) {
node[colorField] = colors[nodeGroups[colorByAccessor(node)] % colors.length];
});
uncoloredNodes.forEach(function (node) {
node[colorField] = colors[nodeGroups[colorByAccessor(node)] % colors.length];
});
}

@@ -49,243 +49,243 @@

props: {
jsonUrl: {},
graphData: {
default: {
nodes: [],
links: []
},
onChange: function onChange(_, state) {
state.onFrame = null;
} // Pause simulation
props: {
jsonUrl: {},
graphData: {
default: {
nodes: [],
links: []
},
onChange: function onChange(_, state) {
state.onFrame = null;
} // Pause simulation
},
numDimensions: {
default: 3,
onChange: function onChange(numDim, state) {
if (numDim < 3) {
eraseDimension(state.graphData.nodes, 'z');
}
if (numDim < 2) {
eraseDimension(state.graphData.nodes, 'y');
}
function eraseDimension(nodes, dim) {
nodes.forEach(function (d) {
delete d[dim]; // position
delete d['v' + dim]; // velocity
});
}
}
},
nodeRelSize: { default: 4 }, // volume per val unit
autoColorBy: {},
nodeId: { default: 'id' },
nodeVal: { default: 'val' },
nodeResolution: { default: 8 }, // how many slice segments in the sphere's circumference
nodeColor: { default: 'color' },
nodeThreeObject: {},
linkSource: { default: 'source' },
linkTarget: { default: 'target' },
linkColor: { default: 'color' },
linkOpacity: { default: 0.2 },
forceEngine: { default: 'd3' }, // d3 or ngraph
d3AlphaDecay: { default: 0.0228 },
d3VelocityDecay: { default: 0.4 },
warmupTicks: { default: 0 }, // how many times to tick the force engine at init before starting to render
cooldownTicks: { default: Infinity },
cooldownTime: { default: 15000 } // ms
},
numDimensions: {
default: 3,
onChange: function onChange(numDim, state) {
if (numDim < 3) {
eraseDimension(state.graphData.nodes, 'z');
}
if (numDim < 2) {
eraseDimension(state.graphData.nodes, 'y');
}
methods: {
// Expose d3 forces for external manipulation
d3Force: function d3Force(state, forceName, forceFn) {
if (!state.initialised) {
return null; // d3 force simulation object doesn't exist yet
}
if (forceFn === undefined) {
return state.d3ForceLayout.force(forceName); // Force getter
}
state.d3ForceLayout.force(forceName, forceFn); // Force setter
return this;
},
tickFrame: function tickFrame(state) {
if (state.onFrame) state.onFrame();
return this;
function eraseDimension(nodes, dim) {
nodes.forEach(function (d) {
delete d[dim]; // position
delete d['v' + dim]; // velocity
});
}
}
},
nodeRelSize: { default: 4 }, // volume per val unit
autoColorBy: {},
nodeId: { default: 'id' },
nodeVal: { default: 'val' },
nodeResolution: { default: 8 }, // how many slice segments in the sphere's circumference
nodeColor: { default: 'color' },
nodeThreeObject: {},
linkSource: { default: 'source' },
linkTarget: { default: 'target' },
linkColor: { default: 'color' },
linkOpacity: { default: 0.2 },
forceEngine: { default: 'd3' }, // d3 or ngraph
d3AlphaDecay: { default: 0.0228 },
d3VelocityDecay: { default: 0.4 },
warmupTicks: { default: 0 }, // how many times to tick the force engine at init before starting to render
cooldownTicks: { default: Infinity },
cooldownTime: { default: 15000 } // ms
},
init: function init(threeObj, state) {
// Main three object to manipulate
state.graphScene = threeObj;
// Add D3 force-directed layout
state.d3ForceLayout = d3Force3d.forceSimulation().force('link', d3Force3d.forceLink()).force('charge', d3Force3d.forceManyBody()).force('center', d3Force3d.forceCenter()).stop();
methods: {
// Expose d3 forces for external manipulation
d3Force: function d3Force(state, forceName, forceFn) {
if (forceFn === undefined) {
return state.d3ForceLayout.force(forceName); // Force getter
}
state.d3ForceLayout.force(forceName, forceFn); // Force setter
return this;
},
tickFrame: function tickFrame(state) {
if (state.onFrame) state.onFrame();
return this;
}
},
update: function updateFn(state) {
state.onFrame = null; // Pause simulation
stateInit: function stateInit() {
return {
d3ForceLayout: d3Force3d.forceSimulation().force('link', d3Force3d.forceLink()).force('charge', d3Force3d.forceManyBody()).force('center', d3Force3d.forceCenter()).stop()
};
},
if (state.graphData.nodes.length || state.graphData.links.length) {
console.info('force-graph loading', state.graphData.nodes.length + ' nodes', state.graphData.links.length + ' links');
}
init: function init(threeObj, state) {
// Main three object to manipulate
state.graphScene = threeObj;
},
if (!state.fetchingJson && state.jsonUrl && !state.graphData.nodes.length && !state.graphData.links.length) {
// (Re-)load data
state.fetchingJson = true;
qwest.get(state.jsonUrl).then(function (_, json) {
state.fetchingJson = false;
state.graphData = json;
updateFn(state); // Force re-update
});
}
update: function updateFn(state) {
state.onFrame = null; // Pause simulation
if (state.autoColorBy !== null) {
// Auto add color to uncolored nodes
autoColorNodes(state.graphData.nodes, accessorFn(state.autoColorBy), state.nodeColor);
}
if (state.graphData.nodes.length || state.graphData.links.length) {
console.info('force-graph loading', state.graphData.nodes.length + ' nodes', state.graphData.links.length + ' links');
}
// parse links
state.graphData.links.forEach(function (link) {
link.source = link[state.linkSource];
link.target = link[state.linkTarget];
});
if (!state.fetchingJson && state.jsonUrl && !state.graphData.nodes.length && !state.graphData.links.length) {
// (Re-)load data
state.fetchingJson = true;
qwest.get(state.jsonUrl).then(function (_, json) {
state.fetchingJson = false;
state.graphData = json;
updateFn(state); // Force re-update
});
}
// Add WebGL objects
while (state.graphScene.children.length) {
state.graphScene.remove(state.graphScene.children[0]);
} // Clear the place
if (state.autoColorBy !== null) {
// Auto add color to uncolored nodes
autoColorNodes(state.graphData.nodes, accessorFn(state.autoColorBy), state.nodeColor);
}
var customNodeObjectAccessor = accessorFn(state.nodeThreeObject);
var valAccessor = accessorFn(state.nodeVal);
var colorAccessor = accessorFn(state.nodeColor);
var sphereGeometries = {}; // indexed by node value
var sphereMaterials = {}; // indexed by color
state.graphData.nodes.forEach(function (node) {
var customObj = customNodeObjectAccessor(node);
// parse links
state.graphData.links.forEach(function (link) {
link.source = link[state.linkSource];
link.target = link[state.linkTarget];
});
var obj = void 0;
if (customObj) {
obj = customObj.clone();
} else {
// Default object (sphere mesh)
var val = valAccessor(node) || 1;
if (!sphereGeometries.hasOwnProperty(val)) {
sphereGeometries[val] = new three.SphereGeometry(Math.cbrt(val) * state.nodeRelSize, state.nodeResolution, state.nodeResolution);
}
// Add WebGL objects
while (state.graphScene.children.length) {
state.graphScene.remove(state.graphScene.children[0]);
} // Clear the place
var color = colorAccessor(node);
if (!sphereMaterials.hasOwnProperty(color)) {
sphereMaterials[color] = new three.MeshLambertMaterial({
color: colorStr2Hex(color || '#ffffaa'),
transparent: true,
opacity: 0.75
});
}
var customNodeObjectAccessor = accessorFn(state.nodeThreeObject);
var valAccessor = accessorFn(state.nodeVal);
var colorAccessor = accessorFn(state.nodeColor);
var sphereGeometries = {}; // indexed by node value
var sphereMaterials = {}; // indexed by color
state.graphData.nodes.forEach(function (node) {
var customObj = customNodeObjectAccessor(node);
obj = new three.Mesh(sphereGeometries[val], sphereMaterials[color]);
}
var obj = void 0;
if (customObj) {
obj = customObj.clone();
} else {
// Default object (sphere mesh)
var val = valAccessor(node) || 1;
if (!sphereGeometries.hasOwnProperty(val)) {
sphereGeometries[val] = new three.SphereGeometry(Math.cbrt(val) * state.nodeRelSize, state.nodeResolution, state.nodeResolution);
}
obj.__graphObjType = 'node'; // Add object type
obj.__data = node; // Attach node data
var color = colorAccessor(node);
if (!sphereMaterials.hasOwnProperty(color)) {
sphereMaterials[color] = new three.MeshLambertMaterial({
color: colorStr2Hex(color || '#ffffaa'),
transparent: true,
opacity: 0.75
});
}
state.graphScene.add(node.__threeObj = obj);
obj = new three.Mesh(sphereGeometries[val], sphereMaterials[color]);
}
obj.__graphObjType = 'node'; // Add object type
obj.__data = node; // Attach node data
state.graphScene.add(node.__threeObj = obj);
});
var linkColorAccessor = accessorFn(state.linkColor);
var lineMaterials = {}; // indexed by color
state.graphData.links.forEach(function (link) {
var color = linkColorAccessor(link);
if (!lineMaterials.hasOwnProperty(color)) {
lineMaterials[color] = new three.LineBasicMaterial({
color: colorStr2Hex(color || '#f0f0f0'),
transparent: true,
opacity: state.linkOpacity
});
}
var linkColorAccessor = accessorFn(state.linkColor);
var lineMaterials = {}; // indexed by color
state.graphData.links.forEach(function (link) {
var color = linkColorAccessor(link);
if (!lineMaterials.hasOwnProperty(color)) {
lineMaterials[color] = new three.LineBasicMaterial({
color: colorStr2Hex(color || '#f0f0f0'),
transparent: true,
opacity: state.linkOpacity
});
}
var geometry = new three.BufferGeometry();
geometry.addAttribute('position', new three.BufferAttribute(new Float32Array(2 * 3), 3));
var lineMaterial = lineMaterials[color];
var line = new three.Line(geometry, lineMaterial);
var geometry = new three.BufferGeometry();
geometry.addAttribute('position', new three.BufferAttribute(new Float32Array(2 * 3), 3));
var lineMaterial = lineMaterials[color];
var line = new three.Line(geometry, lineMaterial);
line.renderOrder = 10; // Prevent visual glitches of dark lines on top of nodes by rendering them last
line.renderOrder = 10; // Prevent visual glitches of dark lines on top of nodes by rendering them last
line.__graphObjType = 'link'; // Add object type
line.__graphObjType = 'link'; // Add object type
state.graphScene.add(link.__lineObj = line);
});
state.graphScene.add(link.__lineObj = line);
});
// Feed data to force-directed layout
var isD3Sim = state.forceEngine !== 'ngraph';
var layout = void 0;
if (isD3Sim) {
// D3-force
(layout = state.d3ForceLayout).stop().alpha(1) // re-heat the simulation
.alphaDecay(state.d3AlphaDecay).velocityDecay(state.d3VelocityDecay).numDimensions(state.numDimensions).nodes(state.graphData.nodes).force('link').id(function (d) {
return d[state.nodeId];
}).links(state.graphData.links);
} else {
// ngraph
var _graph = ngraph.graph();
state.graphData.nodes.forEach(function (node) {
_graph.addNode(node[state.nodeId]);
});
state.graphData.links.forEach(function (link) {
_graph.addLink(link.source, link.target);
});
layout = ngraph['forcelayout' + (state.numDimensions === 2 ? '' : '3d')](_graph);
layout.graph = _graph; // Attach graph reference to layout
}
// Feed data to force-directed layout
var isD3Sim = state.forceEngine !== 'ngraph';
var layout = void 0;
if (isD3Sim) {
// D3-force
(layout = state.d3ForceLayout).stop().alpha(1) // re-heat the simulation
.alphaDecay(state.d3AlphaDecay).velocityDecay(state.d3VelocityDecay).numDimensions(state.numDimensions).nodes(state.graphData.nodes).force('link').id(function (d) {
return d[state.nodeId];
}).links(state.graphData.links);
} else {
// ngraph
var _graph = ngraph.graph();
state.graphData.nodes.forEach(function (node) {
_graph.addNode(node[state.nodeId]);
});
state.graphData.links.forEach(function (link) {
_graph.addLink(link.source, link.target);
});
layout = ngraph['forcelayout' + (state.numDimensions === 2 ? '' : '3d')](_graph);
layout.graph = _graph; // Attach graph reference to layout
}
for (var i = 0; i < state.warmupTicks; i++) {
layout[isD3Sim ? 'tick' : 'step']();
} // Initial ticks before starting to render
for (var i = 0; i < state.warmupTicks; i++) {
layout[isD3Sim ? 'tick' : 'step']();
} // Initial ticks before starting to render
var cntTicks = 0;
var startTickTime = new Date();
state.onFrame = layoutTick;
var cntTicks = 0;
var startTickTime = new Date();
state.onFrame = layoutTick;
//
//
function layoutTick() {
if (++cntTicks > state.cooldownTicks || new Date() - startTickTime > state.cooldownTime) {
state.onFrame = null; // Stop ticking graph
} else {
layout[isD3Sim ? 'tick' : 'step'](); // Tick it
}
function layoutTick() {
if (++cntTicks > state.cooldownTicks || new Date() - startTickTime > state.cooldownTime) {
state.onFrame = null; // Stop ticking graph
} else {
layout[isD3Sim ? 'tick' : 'step'](); // Tick it
}
// Update nodes position
state.graphData.nodes.forEach(function (node) {
var obj = node.__threeObj;
if (!obj) return;
// Update nodes position
state.graphData.nodes.forEach(function (node) {
var obj = node.__threeObj;
if (!obj) return;
var pos = isD3Sim ? node : layout.getNodePosition(node[state.nodeId]);
var pos = isD3Sim ? node : layout.getNodePosition(node[state.nodeId]);
obj.position.x = pos.x;
obj.position.y = pos.y || 0;
obj.position.z = pos.z || 0;
});
obj.position.x = pos.x;
obj.position.y = pos.y || 0;
obj.position.z = pos.z || 0;
});
// Update links position
state.graphData.links.forEach(function (link) {
var line = link.__lineObj;
if (!line) return;
// Update links position
state.graphData.links.forEach(function (link) {
var line = link.__lineObj;
if (!line) return;
var pos = isD3Sim ? link : layout.getLinkPosition(layout.graph.getLink(link.source, link.target).id),
start = pos[isD3Sim ? 'source' : 'from'],
end = pos[isD3Sim ? 'target' : 'to'],
linePos = line.geometry.attributes.position;
var pos = isD3Sim ? link : layout.getLinkPosition(layout.graph.getLink(link.source, link.target).id),
start = pos[isD3Sim ? 'source' : 'from'],
end = pos[isD3Sim ? 'target' : 'to'],
linePos = line.geometry.attributes.position;
linePos.array[0] = start.x;
linePos.array[1] = start.y || 0;
linePos.array[2] = start.z || 0;
linePos.array[3] = end.x;
linePos.array[4] = end.y || 0;
linePos.array[5] = end.z || 0;
linePos.array[0] = start.x;
linePos.array[1] = start.y || 0;
linePos.array[2] = start.z || 0;
linePos.array[3] = end.x;
linePos.array[4] = end.y || 0;
linePos.array[5] = end.z || 0;
linePos.needsUpdate = true;
line.geometry.computeBoundingSphere();
});
}
linePos.needsUpdate = true;
line.geometry.computeBoundingSphere();
});
}
}
});

@@ -292,0 +292,0 @@

@@ -13,25 +13,25 @@ import { BufferAttribute, BufferGeometry, Group, Line, LineBasicMaterial, Mesh, MeshLambertMaterial, SphereGeometry } from 'three';

var colorStr2Hex = function colorStr2Hex(str) {
return isNaN(str) ? parseInt(tinyColor(str).toHex(), 16) : str;
return isNaN(str) ? parseInt(tinyColor(str).toHex(), 16) : str;
};
function autoColorNodes(nodes, colorByAccessor, colorField) {
if (!colorByAccessor || typeof colorField !== 'string') return;
if (!colorByAccessor || typeof colorField !== 'string') return;
var colors = schemePaired; // Paired color set from color brewer
var colors = schemePaired; // Paired color set from color brewer
var uncoloredNodes = nodes.filter(function (node) {
return !node[colorField];
});
var nodeGroups = {};
var uncoloredNodes = nodes.filter(function (node) {
return !node[colorField];
});
var nodeGroups = {};
uncoloredNodes.forEach(function (node) {
nodeGroups[colorByAccessor(node)] = null;
});
Object.keys(nodeGroups).forEach(function (group, idx) {
nodeGroups[group] = idx;
});
uncoloredNodes.forEach(function (node) {
nodeGroups[colorByAccessor(node)] = null;
});
Object.keys(nodeGroups).forEach(function (group, idx) {
nodeGroups[group] = idx;
});
uncoloredNodes.forEach(function (node) {
node[colorField] = colors[nodeGroups[colorByAccessor(node)] % colors.length];
});
uncoloredNodes.forEach(function (node) {
node[colorField] = colors[nodeGroups[colorByAccessor(node)] % colors.length];
});
}

@@ -45,243 +45,243 @@

props: {
jsonUrl: {},
graphData: {
default: {
nodes: [],
links: []
},
onChange: function onChange(_, state) {
state.onFrame = null;
} // Pause simulation
props: {
jsonUrl: {},
graphData: {
default: {
nodes: [],
links: []
},
onChange: function onChange(_, state) {
state.onFrame = null;
} // Pause simulation
},
numDimensions: {
default: 3,
onChange: function onChange(numDim, state) {
if (numDim < 3) {
eraseDimension(state.graphData.nodes, 'z');
}
if (numDim < 2) {
eraseDimension(state.graphData.nodes, 'y');
}
function eraseDimension(nodes, dim) {
nodes.forEach(function (d) {
delete d[dim]; // position
delete d['v' + dim]; // velocity
});
}
}
},
nodeRelSize: { default: 4 }, // volume per val unit
autoColorBy: {},
nodeId: { default: 'id' },
nodeVal: { default: 'val' },
nodeResolution: { default: 8 }, // how many slice segments in the sphere's circumference
nodeColor: { default: 'color' },
nodeThreeObject: {},
linkSource: { default: 'source' },
linkTarget: { default: 'target' },
linkColor: { default: 'color' },
linkOpacity: { default: 0.2 },
forceEngine: { default: 'd3' }, // d3 or ngraph
d3AlphaDecay: { default: 0.0228 },
d3VelocityDecay: { default: 0.4 },
warmupTicks: { default: 0 }, // how many times to tick the force engine at init before starting to render
cooldownTicks: { default: Infinity },
cooldownTime: { default: 15000 } // ms
},
numDimensions: {
default: 3,
onChange: function onChange(numDim, state) {
if (numDim < 3) {
eraseDimension(state.graphData.nodes, 'z');
}
if (numDim < 2) {
eraseDimension(state.graphData.nodes, 'y');
}
methods: {
// Expose d3 forces for external manipulation
d3Force: function d3Force(state, forceName, forceFn) {
if (!state.initialised) {
return null; // d3 force simulation object doesn't exist yet
}
if (forceFn === undefined) {
return state.d3ForceLayout.force(forceName); // Force getter
}
state.d3ForceLayout.force(forceName, forceFn); // Force setter
return this;
},
tickFrame: function tickFrame(state) {
if (state.onFrame) state.onFrame();
return this;
function eraseDimension(nodes, dim) {
nodes.forEach(function (d) {
delete d[dim]; // position
delete d['v' + dim]; // velocity
});
}
}
},
nodeRelSize: { default: 4 }, // volume per val unit
autoColorBy: {},
nodeId: { default: 'id' },
nodeVal: { default: 'val' },
nodeResolution: { default: 8 }, // how many slice segments in the sphere's circumference
nodeColor: { default: 'color' },
nodeThreeObject: {},
linkSource: { default: 'source' },
linkTarget: { default: 'target' },
linkColor: { default: 'color' },
linkOpacity: { default: 0.2 },
forceEngine: { default: 'd3' }, // d3 or ngraph
d3AlphaDecay: { default: 0.0228 },
d3VelocityDecay: { default: 0.4 },
warmupTicks: { default: 0 }, // how many times to tick the force engine at init before starting to render
cooldownTicks: { default: Infinity },
cooldownTime: { default: 15000 } // ms
},
init: function init(threeObj, state) {
// Main three object to manipulate
state.graphScene = threeObj;
// Add D3 force-directed layout
state.d3ForceLayout = forceSimulation().force('link', forceLink()).force('charge', forceManyBody()).force('center', forceCenter()).stop();
methods: {
// Expose d3 forces for external manipulation
d3Force: function d3Force(state, forceName, forceFn) {
if (forceFn === undefined) {
return state.d3ForceLayout.force(forceName); // Force getter
}
state.d3ForceLayout.force(forceName, forceFn); // Force setter
return this;
},
tickFrame: function tickFrame(state) {
if (state.onFrame) state.onFrame();
return this;
}
},
update: function updateFn(state) {
state.onFrame = null; // Pause simulation
stateInit: function stateInit() {
return {
d3ForceLayout: forceSimulation().force('link', forceLink()).force('charge', forceManyBody()).force('center', forceCenter()).stop()
};
},
if (state.graphData.nodes.length || state.graphData.links.length) {
console.info('force-graph loading', state.graphData.nodes.length + ' nodes', state.graphData.links.length + ' links');
}
init: function init(threeObj, state) {
// Main three object to manipulate
state.graphScene = threeObj;
},
if (!state.fetchingJson && state.jsonUrl && !state.graphData.nodes.length && !state.graphData.links.length) {
// (Re-)load data
state.fetchingJson = true;
qwest.get(state.jsonUrl).then(function (_, json) {
state.fetchingJson = false;
state.graphData = json;
updateFn(state); // Force re-update
});
}
update: function updateFn(state) {
state.onFrame = null; // Pause simulation
if (state.autoColorBy !== null) {
// Auto add color to uncolored nodes
autoColorNodes(state.graphData.nodes, accessorFn(state.autoColorBy), state.nodeColor);
}
if (state.graphData.nodes.length || state.graphData.links.length) {
console.info('force-graph loading', state.graphData.nodes.length + ' nodes', state.graphData.links.length + ' links');
}
// parse links
state.graphData.links.forEach(function (link) {
link.source = link[state.linkSource];
link.target = link[state.linkTarget];
});
if (!state.fetchingJson && state.jsonUrl && !state.graphData.nodes.length && !state.graphData.links.length) {
// (Re-)load data
state.fetchingJson = true;
qwest.get(state.jsonUrl).then(function (_, json) {
state.fetchingJson = false;
state.graphData = json;
updateFn(state); // Force re-update
});
}
// Add WebGL objects
while (state.graphScene.children.length) {
state.graphScene.remove(state.graphScene.children[0]);
} // Clear the place
if (state.autoColorBy !== null) {
// Auto add color to uncolored nodes
autoColorNodes(state.graphData.nodes, accessorFn(state.autoColorBy), state.nodeColor);
}
var customNodeObjectAccessor = accessorFn(state.nodeThreeObject);
var valAccessor = accessorFn(state.nodeVal);
var colorAccessor = accessorFn(state.nodeColor);
var sphereGeometries = {}; // indexed by node value
var sphereMaterials = {}; // indexed by color
state.graphData.nodes.forEach(function (node) {
var customObj = customNodeObjectAccessor(node);
// parse links
state.graphData.links.forEach(function (link) {
link.source = link[state.linkSource];
link.target = link[state.linkTarget];
});
var obj = void 0;
if (customObj) {
obj = customObj.clone();
} else {
// Default object (sphere mesh)
var val = valAccessor(node) || 1;
if (!sphereGeometries.hasOwnProperty(val)) {
sphereGeometries[val] = new SphereGeometry(Math.cbrt(val) * state.nodeRelSize, state.nodeResolution, state.nodeResolution);
}
// Add WebGL objects
while (state.graphScene.children.length) {
state.graphScene.remove(state.graphScene.children[0]);
} // Clear the place
var color = colorAccessor(node);
if (!sphereMaterials.hasOwnProperty(color)) {
sphereMaterials[color] = new MeshLambertMaterial({
color: colorStr2Hex(color || '#ffffaa'),
transparent: true,
opacity: 0.75
});
}
var customNodeObjectAccessor = accessorFn(state.nodeThreeObject);
var valAccessor = accessorFn(state.nodeVal);
var colorAccessor = accessorFn(state.nodeColor);
var sphereGeometries = {}; // indexed by node value
var sphereMaterials = {}; // indexed by color
state.graphData.nodes.forEach(function (node) {
var customObj = customNodeObjectAccessor(node);
obj = new Mesh(sphereGeometries[val], sphereMaterials[color]);
}
var obj = void 0;
if (customObj) {
obj = customObj.clone();
} else {
// Default object (sphere mesh)
var val = valAccessor(node) || 1;
if (!sphereGeometries.hasOwnProperty(val)) {
sphereGeometries[val] = new SphereGeometry(Math.cbrt(val) * state.nodeRelSize, state.nodeResolution, state.nodeResolution);
}
obj.__graphObjType = 'node'; // Add object type
obj.__data = node; // Attach node data
var color = colorAccessor(node);
if (!sphereMaterials.hasOwnProperty(color)) {
sphereMaterials[color] = new MeshLambertMaterial({
color: colorStr2Hex(color || '#ffffaa'),
transparent: true,
opacity: 0.75
});
}
state.graphScene.add(node.__threeObj = obj);
obj = new Mesh(sphereGeometries[val], sphereMaterials[color]);
}
obj.__graphObjType = 'node'; // Add object type
obj.__data = node; // Attach node data
state.graphScene.add(node.__threeObj = obj);
});
var linkColorAccessor = accessorFn(state.linkColor);
var lineMaterials = {}; // indexed by color
state.graphData.links.forEach(function (link) {
var color = linkColorAccessor(link);
if (!lineMaterials.hasOwnProperty(color)) {
lineMaterials[color] = new LineBasicMaterial({
color: colorStr2Hex(color || '#f0f0f0'),
transparent: true,
opacity: state.linkOpacity
});
}
var linkColorAccessor = accessorFn(state.linkColor);
var lineMaterials = {}; // indexed by color
state.graphData.links.forEach(function (link) {
var color = linkColorAccessor(link);
if (!lineMaterials.hasOwnProperty(color)) {
lineMaterials[color] = new LineBasicMaterial({
color: colorStr2Hex(color || '#f0f0f0'),
transparent: true,
opacity: state.linkOpacity
});
}
var geometry = new BufferGeometry();
geometry.addAttribute('position', new BufferAttribute(new Float32Array(2 * 3), 3));
var lineMaterial = lineMaterials[color];
var line = new Line(geometry, lineMaterial);
var geometry = new BufferGeometry();
geometry.addAttribute('position', new BufferAttribute(new Float32Array(2 * 3), 3));
var lineMaterial = lineMaterials[color];
var line = new Line(geometry, lineMaterial);
line.renderOrder = 10; // Prevent visual glitches of dark lines on top of nodes by rendering them last
line.renderOrder = 10; // Prevent visual glitches of dark lines on top of nodes by rendering them last
line.__graphObjType = 'link'; // Add object type
line.__graphObjType = 'link'; // Add object type
state.graphScene.add(link.__lineObj = line);
});
state.graphScene.add(link.__lineObj = line);
});
// Feed data to force-directed layout
var isD3Sim = state.forceEngine !== 'ngraph';
var layout = void 0;
if (isD3Sim) {
// D3-force
(layout = state.d3ForceLayout).stop().alpha(1) // re-heat the simulation
.alphaDecay(state.d3AlphaDecay).velocityDecay(state.d3VelocityDecay).numDimensions(state.numDimensions).nodes(state.graphData.nodes).force('link').id(function (d) {
return d[state.nodeId];
}).links(state.graphData.links);
} else {
// ngraph
var _graph = ngraph.graph();
state.graphData.nodes.forEach(function (node) {
_graph.addNode(node[state.nodeId]);
});
state.graphData.links.forEach(function (link) {
_graph.addLink(link.source, link.target);
});
layout = ngraph['forcelayout' + (state.numDimensions === 2 ? '' : '3d')](_graph);
layout.graph = _graph; // Attach graph reference to layout
}
// Feed data to force-directed layout
var isD3Sim = state.forceEngine !== 'ngraph';
var layout = void 0;
if (isD3Sim) {
// D3-force
(layout = state.d3ForceLayout).stop().alpha(1) // re-heat the simulation
.alphaDecay(state.d3AlphaDecay).velocityDecay(state.d3VelocityDecay).numDimensions(state.numDimensions).nodes(state.graphData.nodes).force('link').id(function (d) {
return d[state.nodeId];
}).links(state.graphData.links);
} else {
// ngraph
var _graph = ngraph.graph();
state.graphData.nodes.forEach(function (node) {
_graph.addNode(node[state.nodeId]);
});
state.graphData.links.forEach(function (link) {
_graph.addLink(link.source, link.target);
});
layout = ngraph['forcelayout' + (state.numDimensions === 2 ? '' : '3d')](_graph);
layout.graph = _graph; // Attach graph reference to layout
}
for (var i = 0; i < state.warmupTicks; i++) {
layout[isD3Sim ? 'tick' : 'step']();
} // Initial ticks before starting to render
for (var i = 0; i < state.warmupTicks; i++) {
layout[isD3Sim ? 'tick' : 'step']();
} // Initial ticks before starting to render
var cntTicks = 0;
var startTickTime = new Date();
state.onFrame = layoutTick;
var cntTicks = 0;
var startTickTime = new Date();
state.onFrame = layoutTick;
//
//
function layoutTick() {
if (++cntTicks > state.cooldownTicks || new Date() - startTickTime > state.cooldownTime) {
state.onFrame = null; // Stop ticking graph
} else {
layout[isD3Sim ? 'tick' : 'step'](); // Tick it
}
function layoutTick() {
if (++cntTicks > state.cooldownTicks || new Date() - startTickTime > state.cooldownTime) {
state.onFrame = null; // Stop ticking graph
} else {
layout[isD3Sim ? 'tick' : 'step'](); // Tick it
}
// Update nodes position
state.graphData.nodes.forEach(function (node) {
var obj = node.__threeObj;
if (!obj) return;
// Update nodes position
state.graphData.nodes.forEach(function (node) {
var obj = node.__threeObj;
if (!obj) return;
var pos = isD3Sim ? node : layout.getNodePosition(node[state.nodeId]);
var pos = isD3Sim ? node : layout.getNodePosition(node[state.nodeId]);
obj.position.x = pos.x;
obj.position.y = pos.y || 0;
obj.position.z = pos.z || 0;
});
obj.position.x = pos.x;
obj.position.y = pos.y || 0;
obj.position.z = pos.z || 0;
});
// Update links position
state.graphData.links.forEach(function (link) {
var line = link.__lineObj;
if (!line) return;
// Update links position
state.graphData.links.forEach(function (link) {
var line = link.__lineObj;
if (!line) return;
var pos = isD3Sim ? link : layout.getLinkPosition(layout.graph.getLink(link.source, link.target).id),
start = pos[isD3Sim ? 'source' : 'from'],
end = pos[isD3Sim ? 'target' : 'to'],
linePos = line.geometry.attributes.position;
var pos = isD3Sim ? link : layout.getLinkPosition(layout.graph.getLink(link.source, link.target).id),
start = pos[isD3Sim ? 'source' : 'from'],
end = pos[isD3Sim ? 'target' : 'to'],
linePos = line.geometry.attributes.position;
linePos.array[0] = start.x;
linePos.array[1] = start.y || 0;
linePos.array[2] = start.z || 0;
linePos.array[3] = end.x;
linePos.array[4] = end.y || 0;
linePos.array[5] = end.z || 0;
linePos.array[0] = start.x;
linePos.array[1] = start.y || 0;
linePos.array[2] = start.z || 0;
linePos.array[3] = end.x;
linePos.array[4] = end.y || 0;
linePos.array[5] = end.z || 0;
linePos.needsUpdate = true;
line.geometry.computeBoundingSphere();
});
}
linePos.needsUpdate = true;
line.geometry.computeBoundingSphere();
});
}
}
});

@@ -288,0 +288,0 @@

{
"name": "three-forcegraph",
"version": "1.1.0",
"version": "1.1.1",
"description": "Force-directed graph as a ThreeJS 3d object",

@@ -41,3 +41,3 @@ "unpkg": "dist/three-forcegraph.min.js",

"d3-scale-chromatic": "^1.1.1",
"kapsule": "^1.7.8",
"kapsule": "^1.8.1",
"ngraph.forcelayout": "^0.2.1",

@@ -58,3 +58,3 @@ "ngraph.forcelayout3d": "^0.0.16",

"babel-preset-env": "^1.6.1",
"rollup": "^0.53.0",
"rollup": "^0.53.2",
"rollup-plugin-babel": "^3.0.3",

@@ -64,4 +64,4 @@ "rollup-plugin-commonjs": "^8.2.6",

"rollup-watch": "^4.3.1",
"uglify-js": "^3.3.3"
"uglify-js": "^3.3.4"
}
}

@@ -7,19 +7,19 @@ import resolve from 'rollup-plugin-node-resolve';

export default {
external: ['three'],
input: 'src/index.js',
output: [
{
format: 'umd',
name: 'ThreeForceGraph',
globals: { three: 'THREE' },
file: `dist/${name}.js`,
sourcemap: true,
banner: `// Version ${version} ${name} - ${homepage}`
}
],
plugins: [
resolve(),
commonJs(),
babel({ exclude: 'node_modules/**' })
]
external: ['three'],
input: 'src/index.js',
output: [
{
format: 'umd',
name: 'ThreeForceGraph',
globals: { three: 'THREE' },
file: `dist/${name}.js`,
sourcemap: true,
banner: `// Version ${version} ${name} - ${homepage}`
}
],
plugins: [
resolve(),
commonJs(),
babel({ exclude: 'node_modules/**' })
]
};

@@ -5,17 +5,17 @@ import babel from 'rollup-plugin-babel';

export default {
input: 'src/index.js',
output: [
{
format: 'cjs',
file: `dist/${name}.common.js`
},
{
format: 'es',
file: `dist/${name}.module.js`
}
],
external: [...Object.keys(dependencies), ...Object.keys(peerDependencies)],
plugins: [
babel()
]
input: 'src/index.js',
output: [
{
format: 'cjs',
file: `dist/${name}.common.js`
},
{
format: 'es',
file: `dist/${name}.module.js`
}
],
external: [...Object.keys(dependencies), ...Object.keys(peerDependencies)],
plugins: [
babel()
]
};

@@ -7,17 +7,17 @@ import { schemePaired } from 'd3-scale-chromatic';

function autoColorNodes(nodes, colorByAccessor, colorField) {
if (!colorByAccessor || typeof colorField !== 'string') return;
if (!colorByAccessor || typeof colorField !== 'string') return;
const colors = schemePaired; // Paired color set from color brewer
const colors = schemePaired; // Paired color set from color brewer
const uncoloredNodes = nodes.filter(node => !node[colorField]);
const nodeGroups = {};
const uncoloredNodes = nodes.filter(node => !node[colorField]);
const nodeGroups = {};
uncoloredNodes.forEach(node => { nodeGroups[colorByAccessor(node)] = null });
Object.keys(nodeGroups).forEach((group, idx) => { nodeGroups[group] = idx });
uncoloredNodes.forEach(node => { nodeGroups[colorByAccessor(node)] = null });
Object.keys(nodeGroups).forEach((group, idx) => { nodeGroups[group] = idx });
uncoloredNodes.forEach(node => {
node[colorField] = colors[nodeGroups[colorByAccessor(node)] % colors.length];
});
uncoloredNodes.forEach(node => {
node[colorField] = colors[nodeGroups[colorByAccessor(node)] % colors.length];
});
}
export { autoColorNodes, colorStr2Hex };

@@ -33,239 +33,237 @@ import {

props: {
jsonUrl: {},
graphData: {
default: {
nodes: [],
links: []
},
onChange(_, state) { state.onFrame = null; } // Pause simulation
},
numDimensions: {
default: 3,
onChange(numDim, state) {
if (numDim < 3) { eraseDimension(state.graphData.nodes, 'z'); }
if (numDim < 2) { eraseDimension(state.graphData.nodes, 'y'); }
function eraseDimension(nodes, dim) {
nodes.forEach(d => {
delete d[dim]; // position
delete d[`v${dim}`]; // velocity
});
}
}
},
nodeRelSize: { default: 4 }, // volume per val unit
autoColorBy: {},
nodeId: { default: 'id' },
nodeVal: { default: 'val' },
nodeResolution: { default: 8 }, // how many slice segments in the sphere's circumference
nodeColor: { default: 'color' },
nodeThreeObject: {},
linkSource: { default: 'source' },
linkTarget: { default: 'target' },
linkColor: { default: 'color' },
linkOpacity: { default: 0.2 },
forceEngine: { default: 'd3' }, // d3 or ngraph
d3AlphaDecay: { default: 0.0228 },
d3VelocityDecay: { default: 0.4 },
warmupTicks: { default: 0 }, // how many times to tick the force engine at init before starting to render
cooldownTicks: { default: Infinity },
cooldownTime: { default: 15000 }, // ms
props: {
jsonUrl: {},
graphData: {
default: {
nodes: [],
links: []
},
onChange(_, state) { state.onFrame = null; } // Pause simulation
},
numDimensions: {
default: 3,
onChange(numDim, state) {
if (numDim < 3) { eraseDimension(state.graphData.nodes, 'z'); }
if (numDim < 2) { eraseDimension(state.graphData.nodes, 'y'); }
methods: {
// Expose d3 forces for external manipulation
d3Force: function(state, forceName, forceFn) {
if (!state.initialised) {
return null; // d3 force simulation object doesn't exist yet
}
if (forceFn === undefined) {
return state.d3ForceLayout.force(forceName); // Force getter
}
state.d3ForceLayout.force(forceName, forceFn); // Force setter
return this;
},
tickFrame: function(state) {
if(state.onFrame) state.onFrame();
return this;
function eraseDimension(nodes, dim) {
nodes.forEach(d => {
delete d[dim]; // position
delete d[`v${dim}`]; // velocity
});
}
}
},
nodeRelSize: { default: 4 }, // volume per val unit
autoColorBy: {},
nodeId: { default: 'id' },
nodeVal: { default: 'val' },
nodeResolution: { default: 8 }, // how many slice segments in the sphere's circumference
nodeColor: { default: 'color' },
nodeThreeObject: {},
linkSource: { default: 'source' },
linkTarget: { default: 'target' },
linkColor: { default: 'color' },
linkOpacity: { default: 0.2 },
forceEngine: { default: 'd3' }, // d3 or ngraph
d3AlphaDecay: { default: 0.0228 },
d3VelocityDecay: { default: 0.4 },
warmupTicks: { default: 0 }, // how many times to tick the force engine at init before starting to render
cooldownTicks: { default: Infinity },
cooldownTime: { default: 15000 }, // ms
},
init: function(threeObj, state) {
// Main three object to manipulate
state.graphScene = threeObj;
// Add D3 force-directed layout
state.d3ForceLayout = d3ForceSimulation()
.force('link', d3ForceLink())
.force('charge', d3ForceManyBody())
.force('center', d3ForceCenter())
.stop();
methods: {
// Expose d3 forces for external manipulation
d3Force: function(state, forceName, forceFn) {
if (forceFn === undefined) {
return state.d3ForceLayout.force(forceName); // Force getter
}
state.d3ForceLayout.force(forceName, forceFn); // Force setter
return this;
},
tickFrame: function(state) {
if(state.onFrame) state.onFrame();
return this;
}
},
update: function updateFn(state) {
state.onFrame = null; // Pause simulation
stateInit: () => ({
d3ForceLayout: d3ForceSimulation()
.force('link', d3ForceLink())
.force('charge', d3ForceManyBody())
.force('center', d3ForceCenter())
.stop()
}),
if (state.graphData.nodes.length || state.graphData.links.length) {
console.info('force-graph loading', state.graphData.nodes.length + ' nodes', state.graphData.links.length + ' links');
}
init: function(threeObj, state) {
// Main three object to manipulate
state.graphScene = threeObj;
},
if (!state.fetchingJson && state.jsonUrl && !state.graphData.nodes.length && !state.graphData.links.length) {
// (Re-)load data
state.fetchingJson = true;
qwest.get(state.jsonUrl).then((_, json) => {
state.fetchingJson = false;
state.graphData = json;
updateFn(state); // Force re-update
});
}
update: function updateFn(state) {
state.onFrame = null; // Pause simulation
if (state.autoColorBy !== null) {
// Auto add color to uncolored nodes
autoColorNodes(state.graphData.nodes, accessorFn(state.autoColorBy), state.nodeColor);
}
if (state.graphData.nodes.length || state.graphData.links.length) {
console.info('force-graph loading', state.graphData.nodes.length + ' nodes', state.graphData.links.length + ' links');
}
// parse links
state.graphData.links.forEach(link => {
link.source = link[state.linkSource];
link.target = link[state.linkTarget];
});
if (!state.fetchingJson && state.jsonUrl && !state.graphData.nodes.length && !state.graphData.links.length) {
// (Re-)load data
state.fetchingJson = true;
qwest.get(state.jsonUrl).then((_, json) => {
state.fetchingJson = false;
state.graphData = json;
updateFn(state); // Force re-update
});
}
// Add WebGL objects
while (state.graphScene.children.length) { state.graphScene.remove(state.graphScene.children[0]) } // Clear the place
if (state.autoColorBy !== null) {
// Auto add color to uncolored nodes
autoColorNodes(state.graphData.nodes, accessorFn(state.autoColorBy), state.nodeColor);
}
const customNodeObjectAccessor = accessorFn(state.nodeThreeObject);
const valAccessor = accessorFn(state.nodeVal);
const colorAccessor = accessorFn(state.nodeColor);
const sphereGeometries = {}; // indexed by node value
const sphereMaterials = {}; // indexed by color
state.graphData.nodes.forEach(node => {
const customObj = customNodeObjectAccessor(node);
// parse links
state.graphData.links.forEach(link => {
link.source = link[state.linkSource];
link.target = link[state.linkTarget];
});
let obj;
if (customObj) {
obj = customObj.clone();
} else { // Default object (sphere mesh)
const val = valAccessor(node) || 1;
if (!sphereGeometries.hasOwnProperty(val)) {
sphereGeometries[val] = new SphereGeometry(Math.cbrt(val) * state.nodeRelSize, state.nodeResolution, state.nodeResolution);
}
// Add WebGL objects
while (state.graphScene.children.length) { state.graphScene.remove(state.graphScene.children[0]) } // Clear the place
const color = colorAccessor(node);
if (!sphereMaterials.hasOwnProperty(color)) {
sphereMaterials[color] = new MeshLambertMaterial({
color: colorStr2Hex(color || '#ffffaa'),
transparent: true,
opacity: 0.75
});
}
const customNodeObjectAccessor = accessorFn(state.nodeThreeObject);
const valAccessor = accessorFn(state.nodeVal);
const colorAccessor = accessorFn(state.nodeColor);
const sphereGeometries = {}; // indexed by node value
const sphereMaterials = {}; // indexed by color
state.graphData.nodes.forEach(node => {
const customObj = customNodeObjectAccessor(node);
obj = new Mesh(sphereGeometries[val], sphereMaterials[color]);
}
let obj;
if (customObj) {
obj = customObj.clone();
} else { // Default object (sphere mesh)
const val = valAccessor(node) || 1;
if (!sphereGeometries.hasOwnProperty(val)) {
sphereGeometries[val] = new SphereGeometry(Math.cbrt(val) * state.nodeRelSize, state.nodeResolution, state.nodeResolution);
}
obj.__graphObjType = 'node'; // Add object type
obj.__data = node; // Attach node data
const color = colorAccessor(node);
if (!sphereMaterials.hasOwnProperty(color)) {
sphereMaterials[color] = new MeshLambertMaterial({
color: colorStr2Hex(color || '#ffffaa'),
transparent: true,
opacity: 0.75
});
}
state.graphScene.add(node.__threeObj = obj);
obj = new Mesh(sphereGeometries[val], sphereMaterials[color]);
}
obj.__graphObjType = 'node'; // Add object type
obj.__data = node; // Attach node data
state.graphScene.add(node.__threeObj = obj);
});
const linkColorAccessor = accessorFn(state.linkColor);
const lineMaterials = {}; // indexed by color
state.graphData.links.forEach(link => {
const color = linkColorAccessor(link);
if (!lineMaterials.hasOwnProperty(color)) {
lineMaterials[color] = new LineBasicMaterial({
color: colorStr2Hex(color || '#f0f0f0'),
transparent: true,
opacity: state.linkOpacity
});
}
const linkColorAccessor = accessorFn(state.linkColor);
const lineMaterials = {}; // indexed by color
state.graphData.links.forEach(link => {
const color = linkColorAccessor(link);
if (!lineMaterials.hasOwnProperty(color)) {
lineMaterials[color] = new LineBasicMaterial({
color: colorStr2Hex(color || '#f0f0f0'),
transparent: true,
opacity: state.linkOpacity
});
}
const geometry = new BufferGeometry();
geometry.addAttribute('position', new BufferAttribute(new Float32Array(2 * 3), 3));
const lineMaterial = lineMaterials[color];
const line = new Line(geometry, lineMaterial);
const geometry = new BufferGeometry();
geometry.addAttribute('position', new BufferAttribute(new Float32Array(2 * 3), 3));
const lineMaterial = lineMaterials[color];
const line = new Line(geometry, lineMaterial);
line.renderOrder = 10; // Prevent visual glitches of dark lines on top of nodes by rendering them last
line.renderOrder = 10; // Prevent visual glitches of dark lines on top of nodes by rendering them last
line.__graphObjType = 'link'; // Add object type
line.__graphObjType = 'link'; // Add object type
state.graphScene.add(link.__lineObj = line);
});
state.graphScene.add(link.__lineObj = line);
});
// Feed data to force-directed layout
const isD3Sim = state.forceEngine !== 'ngraph';
let layout;
if (isD3Sim) {
// D3-force
(layout = state.d3ForceLayout)
.stop()
.alpha(1)// re-heat the simulation
.alphaDecay(state.d3AlphaDecay)
.velocityDecay(state.d3VelocityDecay)
.numDimensions(state.numDimensions)
.nodes(state.graphData.nodes)
.force('link')
.id(d => d[state.nodeId])
.links(state.graphData.links);
} else {
// ngraph
const graph = ngraph.graph();
state.graphData.nodes.forEach(node => { graph.addNode(node[state.nodeId]); });
state.graphData.links.forEach(link => { graph.addLink(link.source, link.target); });
layout = ngraph['forcelayout' + (state.numDimensions === 2 ? '' : '3d')](graph);
layout.graph = graph; // Attach graph reference to layout
}
// Feed data to force-directed layout
const isD3Sim = state.forceEngine !== 'ngraph';
let layout;
if (isD3Sim) {
// D3-force
(layout = state.d3ForceLayout)
.stop()
.alpha(1)// re-heat the simulation
.alphaDecay(state.d3AlphaDecay)
.velocityDecay(state.d3VelocityDecay)
.numDimensions(state.numDimensions)
.nodes(state.graphData.nodes)
.force('link')
.id(d => d[state.nodeId])
.links(state.graphData.links);
} else {
// ngraph
const graph = ngraph.graph();
state.graphData.nodes.forEach(node => { graph.addNode(node[state.nodeId]); });
state.graphData.links.forEach(link => { graph.addLink(link.source, link.target); });
layout = ngraph['forcelayout' + (state.numDimensions === 2 ? '' : '3d')](graph);
layout.graph = graph; // Attach graph reference to layout
}
for (let i=0; i<state.warmupTicks; i++) { layout[isD3Sim?'tick':'step'](); } // Initial ticks before starting to render
for (let i=0; i<state.warmupTicks; i++) { layout[isD3Sim?'tick':'step'](); } // Initial ticks before starting to render
let cntTicks = 0;
const startTickTime = new Date();
state.onFrame = layoutTick;
let cntTicks = 0;
const startTickTime = new Date();
state.onFrame = layoutTick;
//
//
function layoutTick() {
if (++cntTicks > state.cooldownTicks || (new Date()) - startTickTime > state.cooldownTime) {
state.onFrame = null; // Stop ticking graph
} else {
layout[isD3Sim ? 'tick' : 'step'](); // Tick it
}
function layoutTick() {
if (++cntTicks > state.cooldownTicks || (new Date()) - startTickTime > state.cooldownTime) {
state.onFrame = null; // Stop ticking graph
} else {
layout[isD3Sim ? 'tick' : 'step'](); // Tick it
}
// Update nodes position
state.graphData.nodes.forEach(node => {
const obj = node.__threeObj;
if (!obj) return;
// Update nodes position
state.graphData.nodes.forEach(node => {
const obj = node.__threeObj;
if (!obj) return;
const pos = isD3Sim ? node : layout.getNodePosition(node[state.nodeId]);
const pos = isD3Sim ? node : layout.getNodePosition(node[state.nodeId]);
obj.position.x = pos.x;
obj.position.y = pos.y || 0;
obj.position.z = pos.z || 0;
});
obj.position.x = pos.x;
obj.position.y = pos.y || 0;
obj.position.z = pos.z || 0;
});
// Update links position
state.graphData.links.forEach(link => {
const line = link.__lineObj;
if (!line) return;
// Update links position
state.graphData.links.forEach(link => {
const line = link.__lineObj;
if (!line) return;
const pos = isD3Sim
? link
: layout.getLinkPosition(layout.graph.getLink(link.source, link.target).id),
start = pos[isD3Sim ? 'source' : 'from'],
end = pos[isD3Sim ? 'target' : 'to'],
linePos = line.geometry.attributes.position;
const pos = isD3Sim
? link
: layout.getLinkPosition(layout.graph.getLink(link.source, link.target).id),
start = pos[isD3Sim ? 'source' : 'from'],
end = pos[isD3Sim ? 'target' : 'to'],
linePos = line.geometry.attributes.position;
linePos.array[0] = start.x;
linePos.array[1] = start.y || 0;
linePos.array[2] = start.z || 0;
linePos.array[3] = end.x;
linePos.array[4] = end.y || 0;
linePos.array[5] = end.z || 0;
linePos.array[0] = start.x;
linePos.array[1] = start.y || 0;
linePos.array[2] = start.z || 0;
linePos.array[3] = end.x;
linePos.array[4] = end.y || 0;
linePos.array[5] = end.z || 0;
linePos.needsUpdate = true;
line.geometry.computeBoundingSphere();
});
}
linePos.needsUpdate = true;
line.geometry.computeBoundingSphere();
});
}
}
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc