Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

graphology-canvas

Package Overview
Dependencies
Maintainers
2
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

graphology-canvas - npm Package Compare versions

Comparing version 0.2.0 to 0.3.0

browser.js

24

defaults.js

@@ -0,2 +1,5 @@

var merge = require('lodash/merge');
var DEFAULTS = {
batchSize: 500,
margin: 20,

@@ -17,2 +20,23 @@ width: 2048,

exports.refineSettings = function refineSettings(settings) {
var dimensions = {
width: settings.width,
height: settings.height
};
if (!dimensions.width && !dimensions.height)
throw new Error('graphology-canvas: need at least a valid width or height!');
if (dimensions.width && !dimensions.height)
dimensions.height = dimensions.width;
if (dimensions.height && !dimensions.width)
dimensions.width = dimensions.height;
settings = merge({}, DEFAULTS, settings, dimensions);
return settings;
};
exports.DEFAULT_NODE_REDUCER = function(settings, node, attr) {

@@ -19,0 +43,0 @@ var reduced = {

78

helpers.js

@@ -9,10 +9,17 @@ /**

// Taken from @jacomyma (graph-recipes)
var CAMERA = {
x: 0.5,
y: 0.5,
angle: 0,
ratio: 1
};
function reduceNodes(graph, settings) {
var width = settings.width,
height = settings.height;
var containerWidth = settings.width,
containerHeight = settings.height;
var xBarycenter = 0,
yBarycenter = 0,
totalWeight = 0;
var xMin = Infinity,
xMax = -Infinity,
yMin = Infinity,
yMax = -Infinity;

@@ -30,39 +37,50 @@ var data = {};

// Computing rescaling items
xBarycenter += attr.size * attr.x;
yBarycenter += attr.size * attr.y;
totalWeight += attr.size;
// Finding bounds
if (attr.x < xMin)
xMin = attr.x;
if (attr.x > xMax)
xMax = attr.x;
if (attr.y < yMin)
yMin = attr.y;
if (attr.y > yMax)
yMax = attr.y;
});
xBarycenter /= totalWeight;
yBarycenter /= totalWeight;
var graphWidth = xMax - xMin,
graphHeight = yMax - yMin;
var d, ratio, n;
var dMax = -Infinity;
var ratio = Math.max(graphWidth, graphHeight) || 1;
var k;
var dX = (xMax + xMin) / 2;
var dY = (yMax + yMin) / 2;
for (k in data) {
n = data[k];
d = Math.pow(n.x - xBarycenter, 2) + Math.pow(n.y - yBarycenter, 2);
containerWidth -= settings.margin * 2;
containerHeight -= settings.margin * 2;
if (d > dMax)
dMax = d;
}
var smallest = Math.min(containerWidth, containerHeight);
ratio = (Math.min(width, height) - 2 * settings.margin) / (2 * Math.sqrt(dMax));
var dpX = smallest / containerWidth;
var dpY = smallest / containerHeight;
var dpRatio = CAMERA.ratio / smallest;
var k, n;
var x, y;
for (k in data) {
n = data[k];
n.x = width / 2 + (n.x - xBarycenter) * ratio;
n.y = height / 2 + (n.y - yBarycenter) * ratio;
// Normalize
x = 0.5 + (n.x - dX) / ratio;
y = 0.5 + (n.y - dY) / ratio;
// Conserving original orientation
if (xBarycenter < 0)
n.x = width - n.x;
if (yBarycenter < 0)
n.y = height - n.y;
// Align
x = (x - CAMERA.x) / dpRatio;
y = (CAMERA.y - y) / dpRatio;
n.size *= ratio; // TODO: keep?
// Rotate
x = x * Math.cos(CAMERA.angle) - y * Math.sin(CAMERA.angle);
y = y * Math.cos(CAMERA.angle) + x * Math.sin(CAMERA.angle);
n.x = settings.margin + x + smallest / 2 / dpX;
n.y = settings.margin + y + smallest / 2 / dpY;
}

@@ -69,0 +87,0 @@

@@ -1,4 +0,47 @@

import Graph from 'graphology-types';
import Graph, {Attributes, NodeKey, EdgeKey} from 'graphology-types';
export default function render(graph: Graph, outputPath: string, callback: () => void): void;
export default function render(graph: Graph, outputPath: string, settings: any, callback: () => void): void;
export type CanvasRendererSettings<
NodeAttributes extends Attributes = Attributes,
EdgeAttributes extends Attributes = Attributes
> = {
batchSize?: number;
margin?: number;
width?: number;
height?: number;
nodes?: {
defaultColor?: string;
reducer?: (settings: CanvasRendererSettings, node: NodeKey, attributes: NodeAttributes) => Attributes;
},
edges?: {
defaultColor?: string;
reducer?: (settings: CanvasRendererSettings, edge: EdgeKey, attributes: EdgeAttributes) => Attributes;
}
};
export function render<
NodeAttributes extends Attributes = Attributes,
EdgeAttributes extends Attributes = Attributes
>(
graph: Graph,
context: CanvasRenderingContext2D,
settings?: CanvasRendererSettings<NodeAttributes, EdgeAttributes>
): void;
export function renderAsync<
NodeAttributes extends Attributes = Attributes,
EdgeAttributes extends Attributes = Attributes
>(
graph: Graph,
context: CanvasRenderingContext2D,
settings: CanvasRendererSettings<NodeAttributes, EdgeAttributes>,
callback: () => void
): void;
export function renderAsync<
NodeAttributes extends Attributes = Attributes,
EdgeAttributes extends Attributes = Attributes
>(
graph: Graph,
context: CanvasRenderingContext2D,
callback: () => void
): void;

@@ -5,12 +5,19 @@ /**

*
* Node.js API relying on `node-canvas` and Cairo.
* Publicly-exposed routine used to render the given graph into an arbitrary
* canvas context.
*/
var fs = require('fs');
var canvasApi = require('canvas');
var defaultsDeep = require('lodash/defaultsDeep');
var renderer = require('./renderer.js');
var isGraph = require('graphology-utils/is-graph');
var lib = require('./renderer.js');
var refineSettings = require('./defaults.js').refineSettings;
var DEFAULTS = require('./defaults.js').DEFAULTS;
exports.render = function render(graph, context, settings) {
if (!isGraph(graph))
throw new Error('graphology-canvas/render: expecting a valid graphology instance.');
module.exports = function render(graph, outputPath, settings, callback) {
settings = refineSettings(settings);
lib.renderSync(graph, context, settings);
};
exports.renderAsync = function renderAsync(graph, context, settings, callback) {
if (arguments.length === 3) {

@@ -21,17 +28,5 @@ callback = settings;

settings = defaultsDeep({}, DEFAULTS, settings);
settings = refineSettings(settings);
var canvas = canvasApi.createCanvas(settings.width, settings.height);
var context = canvas.getContext('2d');
renderer(graph, context, settings);
var out = fs.createWriteStream(outputPath);
var pngStream = canvas.createPNGStream();
pngStream.pipe(out);
out.once('finish', function() {
callback();
});
lib.renderAsync(graph, context, settings, callback);
};
{
"name": "graphology-canvas",
"version": "0.2.0",
"version": "0.3.0",
"description": "Canvas rendering routines for graphology.",

@@ -9,7 +9,9 @@ "main": "index.js",

"*.d.ts",
"browser.js",
"components",
"defaults.js",
"helpers.js",
"index.js",
"renderer.js",
"helpers.js"
"node.js",
"renderer.js"
],

@@ -45,7 +47,8 @@ "scripts": {

"@yomguithereal/eslint-config": "^4.0.0",
"eslint": "^7.13.0",
"canvas": "^2.6.1",
"eslint": "^7.17.0",
"fs-extra": "^9.0.1",
"graphology": "^0.19.1",
"graphology-gexf": "^0.6.0",
"graphology-types": "^0.19.0",
"graphology": "^0.19.3",
"graphology-gexf": "^0.7.2",
"graphology-types": "^0.19.2",
"mocha": "^8.2.1"

@@ -60,9 +63,14 @@ },

"dependencies": {
"canvas": "^2.6.1",
"graphology-utils": "^1.8.0",
"graphology-utils": "^2.0.0",
"lodash": "^4.17.20"
},
"peerDependencies": {
"canvas": "^2.6.1",
"graphology-types": ">=0.19.0"
},
"peerDependenciesMeta": {
"canvas": {
"optional": true
}
}
}

@@ -13,11 +13,53 @@ [![Build Status](https://travis-ci.org/graphology/graphology-canvas.svg)](https://travis-ci.org/graphology/graphology-canvas)

Note that `graphology-canvas` relies on [`node-canvas`](https://www.npmjs.com/package/canvas). As such, if you experience issues when installing the libray check that you have the required dependencies as listed [here](https://www.npmjs.com/package/canvas#compiling).
If you need to use this package's functions in node, you will also need to install [`node-canvas`](https://www.npmjs.com/package/canvas) thusly:
```
npm install canvas
```
If you experience any issue when installing the libray check that you have the required dependencies as listed [here](https://www.npmjs.com/package/canvas#compiling).
## Usage
### Rendering a graph in an arbitrary canvas context
```js
var render = require('graphology-canvas');
import {render} from 'graphology-canvas';
render(graph, './graph.png', () => console.log('Done!'));
render(graph, './graph.png', settings, () => console.log('Done!'));
render(graph, context, settings);
```
### Rendering asynchronously to avoid freezing main thread
```js
import {renderAsync} from 'graphology-canvas';
renderAsync(graph, context, settings, function() {
console.log('Done!');
});
```
### Rendering a graph to PNG in node
```js
import {renderToPNG} from 'graphology-canvas/node';
renderToPNG(graph, './graph.png', () => console.log('Done!'));
renderToPNG(graph, './graph.png', settings, () => console.log('Done!'));
```
### Settings
* **width** *?number* [`2048`]: width of the canvas. Will be the same as `height` if not provided.
* **height** *?number* [`2048`]: height of the canvas. Will be the same as `width` if not provided.
* **margin** *?number* [`20`]: margin to keep around the drawing.
* **nodes** *?object*: node-related settings:
* **defaultColor** *?string* [`#999`]: default color for nodes.
* **reducer** *?function*: reducer fonction for nodes taking the rendering settings, the node key and its attributes and tasked to return rendering info such as `color`, `size` etc.
* **edges** *?object*: node-related settings:
* **defaultColor** *?string* [`#999`]: default color for edges.
* **reducer** *?function*: reducer fonction for edges taking the rendering settings, the node key and its attributes and tasked to return rendering info such as `color`, `size` etc.
### Async Settings
* **batchSize** *?number* [`500`]: number of items to render on canvas on each animation frame, increase or decrease to tweak performance vs. UI availability.

@@ -7,3 +7,2 @@ /**

*/
var isGraph = require('graphology-utils/is-graph');
var helpers = require('./helpers.js');

@@ -22,5 +21,3 @@ var defaults = require('./defaults.js');

function renderer(graph, context, settings) {
if (!isGraph(graph))
throw new Error('graphology-canvas/renderer: expecting a valid graphology instance.');
exports.renderSync = function renderSync(graph, context, settings) {

@@ -50,3 +47,2 @@ // Reducing nodes

// Drawing nodes
// TODO: should we draw in size order to avoid weird overlaps? Should we run noverlap?
var k, d;

@@ -58,4 +54,96 @@

}
};
var raf = function(fn) {
return setTimeout(fn, 16);
};
if (typeof requestAnimationFrame !== 'undefined')
raf = requestAnimationFrame;
function asyncWhile(condition, task, callback) {
if (condition()) {
task();
// Early termination to avoid delay
if (!condition())
return callback();
return raf(function() {
asyncWhile(condition, task, callback);
});
}
return callback();
}
module.exports = renderer;
exports.renderAsync = function renderAsync(graph, context, settings, callback) {
// Reducing nodes
var nodeData = helpers.reduceNodes(graph, settings);
// Filling background
fillBackground(context, 'white', settings.width, settings.height);
// Drawing edges asynchronously
var edges = graph.edges();
var edgesDone = 0;
function renderEdgeBatch() {
var l;
var edge, attr, extremities, source, target, sourceData, targetData;
for (l = Math.min(edgesDone + settings.batchSize, edges.length); edgesDone < l; edgesDone++) {
edge = edges[edgesDone];
attr = graph.getEdgeAttributes(edge);
extremities = graph.extremities(edge);
source = extremities[0];
target = extremities[1];
// Reducing edge
if (typeof settings.edges.reducer === 'function')
attr = settings.edges.reducer(settings, edge, attr);
attr = defaults.DEFAULT_EDGE_REDUCER(settings, edge, attr);
sourceData = nodeData[source];
targetData = nodeData[target];
components.edges[attr.type](settings, context, attr, sourceData, targetData);
}
}
var nodes = new Array(graph.order);
var step = 0;
for (var k in nodeData)
nodes[step++] = nodeData[k];
var nodesDone = 0;
function renderNodeBatch() {
var l;
var node;
for (l = Math.min(nodesDone + settings.batchSize, nodes.length); nodesDone < l; nodesDone++) {
node = nodes[nodesDone];
components.nodes[node.type](settings, context, node);
}
}
// Async logic
function edgesCondition() {
return edgesDone < edges.length;
}
function nodesCondition() {
return nodesDone < nodes.length;
}
asyncWhile(edgesCondition, renderEdgeBatch, function() {
asyncWhile(nodesCondition, renderNodeBatch, function() {
return callback();
});
});
};
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