Sigma.js 6.94 KB
Newer Older
James Laver's avatar
James Laver committed
1 2
'use strict';

3
const sigma = require('sigma/src/garg.js').sigma;
James Laver's avatar
James Laver committed
4

James Laver's avatar
James Laver committed
5 6
if (typeof window !== 'undefined') {
  window.sigma = sigma;
James Laver's avatar
James Laver committed
7 8
}

9 10 11
const CustomShapes = require('sigma/plugins/garg.js').init(sigma, window).customShapes;
require('sigma/src/utils/sigma.utils.js').init(sigma);

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
// Black circle around a node
(function() {
  var originalDef = sigma.canvas.nodes.def;

  sigma.canvas.nodes.def = (node, context, settings) => {
    var prefix = settings('prefix') || '';

    originalDef(node, context, settings);

    context.strokeStyle = '#000';
    context.lineWidth = 1;
    context.beginPath();
    context.arc(
      node[prefix + 'x'],
      node[prefix + 'y'],
      node[prefix + 'size'],
      0,
      Math.PI * 2,
      true
    );
    context.stroke();
  }
})()

36
sigma.canvas.nodes.selected = (node, context, settings) => {
37 38 39 40 41 42 43
  // hack
  // We need to temporarily set node.type to 'def'. This is for 2 reasons
  // 1. Make it render as a normal node
  // 2. Avoid infinite recursion (hovers.def calls node renderer and we would end up here back
  //    again with node.type = 'hovered')
  node.type = 'def';
  sigma.canvas.hovers.def(node, context, settings);
44
  node.type = 'selected';
45
  //console.log('hovers, settings:', settings);
46 47 48
};

CustomShapes.init();
49

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
let sigmaMouseSelector = (sigma, options) => {
  sigma.plugins = sigma.plugins || {};

  sigma.plugins.mouseSelector = (s, renderer) => {
    var _self = this;
    var _offset = null;
    const _s = s;
    const _renderer = renderer;
    const _container = _renderer.container;
    //renderer.initDOM('canvas', 'mouseSelector');
    // A hack to force resize to be called (there is a width/height equality
    // check which can't be escaped in any other way).
    //renderer.resize(renderer.width - 1, renderer.height - 1);
    //renderer.resize(renderer.width + 1, renderer.height + 1);
    const _context = _renderer.contexts.mouseSelector;
65 66 67 68
    // These are used to prevent using the 'click' event when in fact this was a drag
    let _clickPositionX = null;
    let _clickPositionY = null;
    let _isValidClick = false;
69 70 71

    _container.onmousemove = function(e) { return mouseMove(e); };
    _context.canvas.onclick = function(e) { return onClick(e); };
72 73
    _container.onmousedown = function(e) { return onMouseDown(e); }
    _container.onmouseup = function(e) { return onMouseUp(e); }
74 75 76 77 78 79 80
    s.bind('click', function(e) { return onClick(e); })
    // The mouseSelector canvas will pass its events down to the "mouse" canvas.
    _context.canvas.style.pointerEvents = 'none';

    s.bind('kill', () => _self.unbindAll());

    this.unbindAll = () => {
arturo's avatar
arturo committed
81
      // console.log('[sigmaMouseSelector] unbinding');
82
      _container.onclick = null;
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
      _context.canvas.onmousemove = null;
      _container.onmousedown = null;
      _container.onmouseup = null;
    }

    const onMouseDown = (e) => {
      _clickPositionX = e.clientX;
      _clickPositionY = e.clientY;
    }

    const onMouseUp = (e) => {
      // Prevent triggering click when in fact this was a drag
      if ((_clickPositionX != e.clientX) || (_clickPositionY != e.clientY)) {
        _clickPositionX = null;
        _clickPositionY = null;
        _isValidClick = false;
      } else {
        _isValidClick = true;
      }
102 103 104 105
    }

    const mouseMove = (e) => {
      const size = _s.settings('mouseSelectorSize') || 3;
106 107
      const x = e.clientX + document.body.scrollLeft - _offset.left - size/2;
      const y = e.clientY + document.body.scrollTop - _offset.top - size/2;
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
      _context.clearRect(0, 0, _context.canvas.width, _context.canvas.height);

      _context.fillStyle = 'rgba(91, 192, 222, 0.7)';
      _context.beginPath();
      _context.arc(
        x,
        y,
        size,
        0,
        Math.PI * 2,
        true
      );
      _context.closePath();
      _context.fill();
    }

    const onClick = (e) => {
125 126 127
      if(!_isValidClick) {
        return;
      }
128
      const size = _s.settings('mouseSelectorSize') || 3;
129 130
      const x = e.data.clientX + document.body.scrollLeft - _offset.left - size/2;
      const y = e.data.clientY + document.body.scrollTop - _offset.top - size/2;
131
      const prefix = _renderer.options.prefix;
132
      //console.log('[sigmaMouseSelector] clicked', e, x, y, size);
133 134 135 136 137 138 139 140
      let nodes = [];
      _s.graph.nodes().forEach((node) => {
        const nodeX = node[prefix + 'x'];
        const nodeY = node[prefix + 'y'];
        if(sigma.utils.getDistance(x, y, nodeX, nodeY) <= size) {
          nodes.push(node);
        }
      });
141 142 143 144 145 146 147
      //console.log('[sigmaMouseSelector] nodes', nodes);
      _renderer.dispatchEvent('clickNodes', {
        node: nodes,
        captor: e.data
      })
      _clickPositionX = null;
      _clickPositionY = null;
148 149 150 151 152 153 154 155 156 157 158 159 160
    }

    const calculateOffset = (element) => {
      var style = window.getComputedStyle(element);
      var getCssProperty = function(prop) {
        return parseInt(style.getPropertyValue(prop).replace('px', '')) || 0;
      };
      return {
        left: element.getBoundingClientRect().left + getCssProperty('padding-left'),
        top: element.getBoundingClientRect().top + getCssProperty('padding-top')
      };
    };

161 162 163 164 165 166 167
    // Container resize event listener
    // @TODO: debounce?
    const onContainerResize = (entries) => {
      _offset = calculateOffset(_container);
    };
    const _resizeObserver = new ResizeObserver( onContainerResize );
    _resizeObserver.observe(_container);
168 169 170 171 172
  }
}

sigmaMouseSelector(sigma);

James Laver's avatar
James Laver committed
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
function _sigma(left, right, opts) {
  try {
    return right(new sigma(opts));
  } catch(e) {
    return left(e);
  }
}

function addRenderer(left, right, sigma, renderer) {
  try {
    return right(sigma.addRenderer(renderer));
  } catch(e) {
    return left(e);
  }
}
188 189 190 191 192 193 194 195
function bindMouseSelectorPlugin(left, right, sig) {
  try {
    return right(sigma.plugins.mouseSelector(sig, sig.renderers[0]));
  } catch(e) {
    console.log('[bindMouseSelectorPlugin] error', e);
    return left(e);
  }
}
196
function bind(sigma, event, handler) { sigma.bind(event, handler); }
197

198
function takeScreenshot(sigma) {
199 200 201
  let c = sigma.renderers[0].container;
  let edges = c.getElementsByClassName('sigma-edges')[0];
  let scene = c.getElementsByClassName('sigma-scene')[0];
202 203 204 205 206 207 208 209
  // let sceneCtx = scene.getContext('2d');
  // sceneCtx.globalAlpha = 1;
  // sceneCtx.drawImage(edges, 0, 0);
  // return scene.toDataURL('image/png');
  let edgesCtx = edges.getContext('2d');
  edgesCtx.globalAlpha = 1;
  edgesCtx.drawImage(scene, 0, 0);
  return edges.toDataURL('image/png');
210 211
}

212 213 214 215 216 217 218 219
function getEdges(sigma) {
  return sigma.graph.edges();
}

function getNodes(sigma) {
  return sigma.graph.nodes();
}

220 221 222 223 224 225 226
function proxySetSettings(window, sigma, settings) {
  var id = sigma.id;

  window.sigma.instances(id).settings(settings);
  window.sigma.instances(id).refresh();
}

James Laver's avatar
James Laver committed
227 228
exports._sigma = _sigma;
exports._addRenderer = addRenderer;
229
exports._bindMouseSelectorPlugin = bindMouseSelectorPlugin;
230
exports._bind = bind;
231
exports._takeScreenshot = takeScreenshot;
232 233
exports._getEdges = getEdges;
exports._getNodes = getNodes;
234
exports._proxySetSettings = proxySetSettings;