Commit fca7a92d authored by Andrew Danger Lyon's avatar Andrew Danger Lyon

adding a new radial layout i ended up not using. oh well. tried a bunch of different l

ayouts, ended up back with what we had in the last commit. adding zoom/pan for the viewport and drag for network nodes. really working great. id like to do a more localized layout of workers around the copanies they work at instead of a forced radial plan, but until d3.force supports masses for nodes (or at least makes setting link bias a public api) this will likely have to wait!
parent 1e3b2138
......@@ -10,3 +10,33 @@ exports.bin_to_hex = (uarr) => {
return Array.prototype.map.call(uarr, x => ('00' + x.toString(16)).slice(-2)).join('');
};
exports.timer = (ms, cb) => {
let triggered = false;
let timeout = null;
const trigger = () => {
triggered = true;
console.log('triggered!');
if(cb) cb();
};
const stop = () => {
if(timeout) clearTimeout(timeout);
timeout = null;
};
const reset = () => {
stop();
triggered = false;
timeout = setTimeout(trigger, ms);
};
const is_triggered = () => triggered;
return {
reset,
stop,
is_triggered,
};
};
"use strict";
const util = require('../util');
const radial = require('./radial');
const Radial = require('./radial');
const Events = require('../events');
module.exports = function Vizz(options) {
options || (options = {});
const bus = options.bus;
if(!bus) throw new Error('Vizz() -- missing `options.bus`');
const vizzbus = new Events();
let width = 1024;
let height = 500;
let x = width / 2;
let y = height / 2;
let zoom = 1.0;
let dragging = false;
const svg = d3.select('.network')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('class', 'container');
const apply_svg_transform = () => {
svg.attr('transform', `translate(${x}, ${y}) scale(${zoom})`);
};
apply_svg_transform();
const svg_node = d3.select('.network').select('svg').node();
svg_node.addEventListener('wheel', function(e) {
const zoom_speed = 1.03;
const dir = e.deltaY < 0 ? zoom_speed : 1 - (zoom_speed - 1);
zoom *= dir;
apply_svg_transform();
}, false);
const pan_dragger = d3.drag()
.on('drag', function() {
const e = d3.event;
//console.log('pan', this, e);
x += e.dx;
y += e.dy;
apply_svg_transform();
})
.container(() => svg_node);
d3.select('.network').select('svg').call(pan_dragger);
const node_dragger = d3.drag()
.on('drag', function() {
const e = d3.event;
e.subject.fx = e.x;
e.subject.fy = e.y;
vizzbus.emit('force-reset');
})
.on('end', function() {
const e = d3.event;
delete e.subject.fx;
delete e.subject.fy;
vizzbus.emit('force-reset');
})
.container(() => svg_node);
vizzbus.on('node-add', () => {
setTimeout(() => {
svg.select('.nodes').selectAll('.node')
.attr('cursor', 'pointer')
.call(node_dragger);
}, 500);
});
svg.append('g')
.attr('class', 'origin')
.append('circle')
.attr('r', 2)
.attr('fill', '#ccc')
.attr('cx', 0)
.attr('cy', 0);
svg.append('g').attr('class', 'links');
svg.append('g').attr('class', 'pings');
svg.append('g').attr('class', 'nodes');
......@@ -23,7 +84,6 @@ module.exports = function Vizz(options) {
var d3_links = null;
var d3_pings = null;
const ticked = (e) => {
const alpha = force.alpha();
if(d3_links) {
d3_links
.select('line')
......@@ -57,8 +117,8 @@ module.exports = function Vizz(options) {
.strength((d) => {
const map = {
'order': 1 / 200,
'consumer-order': 1 / 1000,
'labor': 1 / 1000,
'consumer-order': 1 / 600,
'labor': 1 / 200,
'member': 0,
'_': 0,
};
......@@ -69,37 +129,49 @@ module.exports = function Vizz(options) {
const dist_map = {
'order': 80,
'consumer-order': 50,
'labor': 50,
'labor': 80,
'member': 70,
'_': 100,
};
const dist = dist_map[d.type] || dist_map['_'];
return Math.min(width, height) * dist * scale;
});
const centerx = d3.forceX(() => width / 2);
const centery = d3.forceY(() => height / 2);
const centerx = d3.forceX(() => 0)
.strength((d) => d.type == 'consumer' ? -0.0004 : 0.001);
const centery = d3.forceY(() => 0)
.strength((d) => d.type == 'consumer' ? -0.0004 : 0.001);
const radial_map = {
'consumer': {
radius: () => (Math.min(width, height) / 2) - 30,
strength: () => 0.3,
strength: () => 1 / 5,
},
// NOTE: disabled. be free, companies.
'company_': {
'_company': {
radius: () => (Math.min(width, height) / 4) - 30,
strength: () => 1 / 10,
},
};
const radial_force = (d, axis) => {
const entry = radial_map[d.type];
if(!entry) return d[axis];
const filtered = nodes.filter((f) => f.type == d.type);
const pos = filtered.findIndex((i) => i.id == d.id);
const xy = radial(pos, {
radius: entry.radius(d),
centerx: width / 2,
centery: height / 2,
len: filtered.length,
});
let xy = null;
if(true) {
const filtered = nodes.filter((f) => f.type == d.type);
const pos = filtered.findIndex((i) => i.id == d.id);
xy = Radial.place(pos, {
radius: entry.radius(d),
centerx: 0,
centery: 0,
len: filtered.length,
});
} else {
xy = Radial.nearest(d.x, d.y, {
radius: entry.radius(d),
centerx: 0,
centery: 0,
});
}
return xy[axis];
};
const radialx = d3
......@@ -109,17 +181,24 @@ module.exports = function Vizz(options) {
.forceY((d, i) => radial_force(d, 'y'))
.strength((d) => radial_map[d.type] ? radial_map[d.type].strength() : 0);
const repulse = d3.forceManyBody()
.strength((d) => -1)
.distanceMax((d) => 40);
.strength((d) => d.type == 'consumer' ? -5 : -20)
.distanceMax((d) => d.type == 'consumer' ? 80 : 200);
const force = d3.forceSimulation()
.alphaDecay(1 - Math.pow(0.001, 1 / 600))
.velocityDecay(0.3)
.force('link', linkforce)
.force('charge', repulse)
//.force('centerx', centerx)
//.force('centery', centery)
.force('centerx', centerx)
.force('centery', centery)
.force('radialx', radialx)
.force('radialy', radialy)
.on('tick', ticked);
vizzbus.on('force-reset', () => {
force.alpha(1);
force.restart();
});
const nodes = [];
const links = [];
const pings = [];
......@@ -152,10 +231,11 @@ module.exports = function Vizz(options) {
id,
type,
name,
x: place(width, 0.7),
y: place(height, 0.7),
x: place(width, 0.7) - width / 2,
y: place(height, 0.7) - height / 2,
};
nodes.push(node);
vizzbus.emit('node-add', id, type, name);
const attr_map = {
'consumer': {
r: 12,
......@@ -197,8 +277,7 @@ module.exports = function Vizz(options) {
d3_nodes = nodes_enter.merge(d3_nodes);
force.stop();
force.nodes(nodes);
force.alpha(1);
force.restart();
vizzbus.emit('force-reset');
};
const link_attr_map = {
......@@ -233,7 +312,7 @@ module.exports = function Vizz(options) {
pr: 2,
},
};
const add_link = (id, id_from, id_to, {type, name, timeout, ping}) => {
const add_link = (id, id_from, id_to, {type, name, timeout}) => {
const attr = link_attr_map[type] || link_attr_map['_'];
const existing_link = exists(links, id);
if(timeout) {
......@@ -255,6 +334,7 @@ module.exports = function Vizz(options) {
target: id_to,
};
links.push(link);
vizzbus.emit('link-add', id, id_from, id_to, type, name);
d3_links = svg
.select('g.links')
.selectAll('g.link')
......@@ -271,8 +351,7 @@ module.exports = function Vizz(options) {
d3_links = links_enter.merge(d3_links);
force.stop();
force.force('link').links(links);
force.alpha(1);
force.restart();
vizzbus.emit('force-reset');
};
const add_ping = (link, pingspec) => {
......@@ -287,6 +366,7 @@ module.exports = function Vizz(options) {
link: link,
};
pings.push(ping);
vizzbus.emit('ping-add', link, ping, pingspec);
setTimeout(() => remove_ping(ping_id), timeout);
const attr = link_attr_map[link.type] || link_attr_type['_'];
......@@ -308,6 +388,7 @@ module.exports = function Vizz(options) {
options || (options = {});
if(!exists(links, id)) return;
remove(links, id);
vizzbus.emit('link-remove', id, options);
d3_links = svg
.select('g.links')
.selectAll('g.link')
......@@ -322,13 +403,13 @@ module.exports = function Vizz(options) {
//d3_links = links_exit.merge(d3_links);
force.stop();
force.force('link').links(links);
force.alpha(1);
force.restart();
vizzbus.emit('force-reset');
};
const remove_ping = (id) => {
if(!exists(pings, id)) return;
remove(pings, id);
vizzbus.emit('ping-remove', id);
d3_pings = svg
.select('g.pings')
.selectAll('.ping')
......
const RADS = Math.PI / 180;
module.exports = function radial(i, {radius, centerx, centery, len}) {
exports.place = function(i, {radius, centerx, centery, len}) {
const start_angle = 30;
const current_angle = start_angle + ((360 / len) * i);
const radians = current_angle * RADS;
......@@ -10,3 +10,13 @@ module.exports = function radial(i, {radius, centerx, centery, len}) {
};
};
exports.nearest = function(px, py, {radius, centerx, centery}) {
const vx = px - centerx;
const vy = py - centery;
const magv = Math.pow((vx * vx) + (vy * vy), 0.5);
return {
x: centerx + ((vx / magv) * radius),
y: centery + ((vy / magv) * radius),
};
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment