2017-02-20 18:15:36 +02:00
|
|
|
import { forceSimulation, forceLink, forceCollide, forceCenter } from 'd3';
|
2017-03-02 20:28:27 +02:00
|
|
|
import Constants from './constants';
|
2017-02-14 13:30:57 +02:00
|
|
|
|
2017-05-18 21:21:33 +03:00
|
|
|
const hypotenuse = (a, b) => Math.sqrt(a * a + b * b);
|
2017-02-14 13:30:57 +02:00
|
|
|
|
2017-05-18 21:21:33 +03:00
|
|
|
const rectRadius = ({ width, height }) =>
|
|
|
|
Math.round(hypotenuse(width, height) / 2);
|
2017-02-14 13:30:57 +02:00
|
|
|
|
2017-04-27 14:49:52 +03:00
|
|
|
const forcePlayAnimation = (simulation, animationTicks) => {
|
2017-05-18 21:21:33 +03:00
|
|
|
const n =
|
|
|
|
Math.ceil(
|
|
|
|
Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay())
|
|
|
|
) - animationTicks;
|
|
|
|
|
|
|
|
for (let i = 0; i < n; ++i) {
|
2017-04-27 14:49:52 +03:00
|
|
|
simulation.tick();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-05-18 21:21:33 +03:00
|
|
|
const createLinks = services =>
|
|
|
|
services.reduce(
|
|
|
|
(acc, service, index) =>
|
|
|
|
service.connections
|
|
|
|
? acc.concat(
|
|
|
|
service.connections.map((connection, index) => ({
|
|
|
|
source: service.uuid,
|
|
|
|
target: connection
|
|
|
|
}))
|
|
|
|
)
|
|
|
|
: acc,
|
|
|
|
[]
|
|
|
|
);
|
|
|
|
|
|
|
|
const createSimulation = (services, svgSize, animationTicks = 0) => {
|
2017-02-14 13:30:57 +02:00
|
|
|
// This is not going to work given that as well as the d3 layout stuff, other things might be at play too
|
|
|
|
// We should pass two objects to the components - one for positioning and one for data
|
2017-02-22 17:18:18 +02:00
|
|
|
const nodes = services.map((service, index) => ({
|
2017-04-27 14:49:52 +03:00
|
|
|
uuid: service.uuid,
|
2017-05-18 21:21:33 +03:00
|
|
|
index
|
2017-02-14 13:30:57 +02:00
|
|
|
}));
|
2017-02-22 17:18:18 +02:00
|
|
|
|
|
|
|
const links = createLinks(services);
|
2017-02-14 13:30:57 +02:00
|
|
|
|
2017-05-18 21:21:33 +03:00
|
|
|
const { width, height } = svgSize;
|
2017-02-14 13:30:57 +02:00
|
|
|
|
2017-03-02 20:28:27 +02:00
|
|
|
const nodeRadius = rectRadius(Constants.nodeSizeWithChildren);
|
2017-02-14 13:30:57 +02:00
|
|
|
|
2017-04-27 14:49:52 +03:00
|
|
|
const simulation = forceSimulation(nodes)
|
|
|
|
.force('link', forceLink(links).id(d => d.uuid))
|
|
|
|
.force('collide', forceCollide(nodeRadius))
|
2017-05-18 21:21:33 +03:00
|
|
|
.force('center', forceCenter(width / 2, height / 2));
|
2017-04-27 14:49:52 +03:00
|
|
|
|
|
|
|
forcePlayAnimation(simulation, animationTicks);
|
|
|
|
|
2017-05-18 21:21:33 +03:00
|
|
|
return {
|
2017-04-27 14:49:52 +03:00
|
|
|
nodes,
|
|
|
|
links,
|
|
|
|
simulation
|
2017-05-18 21:21:33 +03:00
|
|
|
};
|
2017-02-14 13:30:57 +02:00
|
|
|
};
|
|
|
|
|
2017-05-18 21:21:33 +03:00
|
|
|
// TODO we need to kill the previous simulation
|
2017-02-14 13:30:57 +02:00
|
|
|
const updateSimulation = (
|
|
|
|
simulation,
|
2017-02-22 17:18:18 +02:00
|
|
|
services,
|
2017-02-15 12:44:16 +02:00
|
|
|
simNodes,
|
|
|
|
simLinks,
|
2017-02-14 13:30:57 +02:00
|
|
|
svgSize,
|
|
|
|
onTick,
|
|
|
|
onEnd
|
|
|
|
) => {
|
2017-02-22 17:18:18 +02:00
|
|
|
const nodes = services.map((service, index) => {
|
2017-02-15 12:44:16 +02:00
|
|
|
const simNode = simNodes.reduce((acc, n, i) => {
|
2017-02-22 17:18:18 +02:00
|
|
|
return service.uuid === n.id ? n : acc;
|
2017-02-15 12:44:16 +02:00
|
|
|
}, null);
|
|
|
|
|
2017-05-18 21:21:33 +03:00
|
|
|
return simNode
|
|
|
|
? {
|
|
|
|
id: simNode.id,
|
2017-05-25 23:03:39 +03:00
|
|
|
// Fx: simNode.x,
|
2017-05-18 21:21:33 +03:00
|
|
|
// fy: simNode.y,
|
|
|
|
index
|
|
|
|
}
|
|
|
|
: {
|
|
|
|
id: service.uuid,
|
|
|
|
index
|
|
|
|
};
|
2017-02-14 13:30:57 +02:00
|
|
|
});
|
|
|
|
|
2017-02-22 17:18:18 +02:00
|
|
|
const links = createLinks(services);
|
2017-02-14 13:30:57 +02:00
|
|
|
|
2017-05-18 21:21:33 +03:00
|
|
|
const { width, height } = svgSize;
|
2017-02-14 13:30:57 +02:00
|
|
|
|
2017-03-02 20:28:27 +02:00
|
|
|
const nodeRadius = rectRadius(Constants.nodeSizeWithChildren);
|
2017-02-14 13:30:57 +02:00
|
|
|
|
2017-05-18 21:21:33 +03:00
|
|
|
return {
|
2017-02-20 18:15:36 +02:00
|
|
|
simulation: forceSimulation(nodes)
|
|
|
|
.force('link', forceLink(links).id(d => d.id))
|
|
|
|
.force('collide', forceCollide(nodeRadius))
|
2017-05-18 21:21:33 +03:00
|
|
|
.force('center', forceCenter(width / 2, height / 2))
|
2017-02-15 12:44:16 +02:00
|
|
|
.on('tick', onTick)
|
|
|
|
.on('end', onEnd),
|
2017-05-18 21:21:33 +03:00
|
|
|
nodes,
|
|
|
|
links
|
|
|
|
};
|
2017-02-14 13:30:57 +02:00
|
|
|
};
|
|
|
|
|
2017-05-18 21:21:33 +03:00
|
|
|
export { createSimulation, updateSimulation };
|
2017-02-14 13:30:57 +02:00
|
|
|
|
|
|
|
/*
|
2017-05-25 23:03:39 +03:00
|
|
|
Const simulation = forceSimulation(dataNodes)
|
2017-02-14 13:30:57 +02:00
|
|
|
// .alpha(1).alphaDecay(0.1)
|
2017-02-20 18:15:36 +02:00
|
|
|
// .force('charge', forceManyBody())
|
|
|
|
.force('link', forceLink(dataLinks)
|
2017-02-14 13:30:57 +02:00
|
|
|
//.distance(() => linkDistance)
|
|
|
|
.id(d => d.id))
|
2017-02-20 18:15:36 +02:00
|
|
|
.force('collide', forceCollide(nodeRadius))
|
|
|
|
.force('center', forceCenter(1024/2, 860/2))
|
2017-02-14 13:30:57 +02:00
|
|
|
.on('tick', () => {
|
|
|
|
console.log('SIMULATION TICK');
|
|
|
|
console.log('tickCounter = ', tickCounter);
|
|
|
|
tickCounter++;
|
|
|
|
this.forceUpdate();
|
|
|
|
})
|
|
|
|
.on('end', () => {
|
|
|
|
console.log('SIMULATION END');
|
|
|
|
console.log('tickCounter = ', tickCounter);
|
|
|
|
// this.forceUpdate();
|
|
|
|
})
|
|
|
|
*/
|