mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-05 11:11:13 +00:00
Skip layout when only edges changed
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
jest.dontMock('../nodes-layout');
|
||||
jest.dontMock('../../constants/naming'); // edge naming: 'source-target'
|
||||
|
||||
import { fromJS } from 'immutable';
|
||||
import { fromJS, is } from 'immutable';
|
||||
|
||||
describe('NodesLayout', () => {
|
||||
const NodesLayout = require('../nodes-layout');
|
||||
@@ -21,36 +21,36 @@ describe('NodesLayout', () => {
|
||||
|
||||
const nodeSets = {
|
||||
initial4: {
|
||||
nodes: {
|
||||
nodes: fromJS({
|
||||
n1: {id: 'n1'},
|
||||
n2: {id: 'n2'},
|
||||
n3: {id: 'n3'},
|
||||
n4: {id: 'n4'}
|
||||
},
|
||||
edges: {
|
||||
}),
|
||||
edges: fromJS({
|
||||
'n1-n3': {id: 'n1-n3', source: 'n1', target: 'n3'},
|
||||
'n1-n4': {id: 'n1-n4', source: 'n1', target: 'n4'},
|
||||
'n2-n4': {id: 'n2-n4', source: 'n2', target: 'n4'}
|
||||
}
|
||||
})
|
||||
},
|
||||
removeEdge24: {
|
||||
nodes: {
|
||||
nodes: fromJS({
|
||||
n1: {id: 'n1'},
|
||||
n2: {id: 'n2'},
|
||||
n3: {id: 'n3'},
|
||||
n4: {id: 'n4'}
|
||||
},
|
||||
edges: {
|
||||
}),
|
||||
edges: fromJS({
|
||||
'n1-n3': {id: 'n1-n3', source: 'n1', target: 'n3'},
|
||||
'n1-n4': {id: 'n1-n4', source: 'n1', target: 'n4'}
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
it('lays out initial nodeset in a rectangle', () => {
|
||||
const result = NodesLayout.doLayout(
|
||||
fromJS(nodeSets.initial4.nodes),
|
||||
fromJS(nodeSets.initial4.edges));
|
||||
nodeSets.initial4.nodes,
|
||||
nodeSets.initial4.edges);
|
||||
// console.log('initial', result.get('nodes'));
|
||||
nodes = result.nodes.toJS();
|
||||
|
||||
@@ -62,23 +62,66 @@ describe('NodesLayout', () => {
|
||||
expect(nodes.n3.y).toEqual(nodes.n4.y);
|
||||
});
|
||||
|
||||
// it('keeps nodes in rectangle after removing one edge', () => {
|
||||
// history = [{
|
||||
// nodes: nodeSets.initial4.nodes,
|
||||
// edges: nodeSets.initial4.edges
|
||||
// }];
|
||||
// nodes = nodeSets.removeEdge24.nodes;
|
||||
// edges = nodeSets.removeEdge24.edges;
|
||||
// NodesLayout.doLayout(nodes, edges, {history});
|
||||
// console.log('remove 1 edge', nodes);
|
||||
//
|
||||
// expect(nodes.n1.x).toBeLessThan(nodes.n2.x);
|
||||
// expect(nodes.n1.y).toEqual(nodes.n2.y);
|
||||
// expect(nodes.n1.x).toEqual(nodes.n3.x);
|
||||
// expect(nodes.n1.y).toBeLessThan(nodes.n3.y);
|
||||
// expect(nodes.n3.x).toBeLessThan(nodes.n4.x);
|
||||
// expect(nodes.n3.y).toEqual(nodes.n4.y);
|
||||
//
|
||||
// });
|
||||
it('keeps nodes in rectangle after removing one edge', () => {
|
||||
let result = NodesLayout.doLayout(
|
||||
nodeSets.initial4.nodes,
|
||||
nodeSets.initial4.edges);
|
||||
history = [{
|
||||
nodes: result.nodes,
|
||||
edges: result.edges
|
||||
}];
|
||||
result = NodesLayout.doLayout(
|
||||
nodeSets.removeEdge24.nodes,
|
||||
nodeSets.removeEdge24.edges,
|
||||
{history}
|
||||
);
|
||||
nodes = result.nodes.toJS();
|
||||
// console.log('remove 1 edge', nodes, result);
|
||||
|
||||
expect(nodes.n1.x).toBeLessThan(nodes.n2.x);
|
||||
expect(nodes.n1.y).toEqual(nodes.n2.y);
|
||||
expect(nodes.n1.x).toEqual(nodes.n3.x);
|
||||
expect(nodes.n1.y).toBeLessThan(nodes.n3.y);
|
||||
expect(nodes.n3.x).toBeLessThan(nodes.n4.x);
|
||||
expect(nodes.n3.y).toEqual(nodes.n4.y);
|
||||
});
|
||||
|
||||
it('keeps nodes in rectangle after removed edge reappears', () => {
|
||||
let result = NodesLayout.doLayout(
|
||||
nodeSets.initial4.nodes,
|
||||
nodeSets.initial4.edges);
|
||||
|
||||
history = [{
|
||||
nodes: result.nodes,
|
||||
edges: result.edges
|
||||
}];
|
||||
result = NodesLayout.doLayout(
|
||||
nodeSets.removeEdge24.nodes,
|
||||
nodeSets.removeEdge24.edges,
|
||||
{history}
|
||||
);
|
||||
|
||||
history = [{
|
||||
nodes: result.nodes,
|
||||
edges: result.edges
|
||||
}];
|
||||
result = NodesLayout.doLayout(
|
||||
nodeSets.initial4.nodes,
|
||||
nodeSets.initial4.edges,
|
||||
{history}
|
||||
);
|
||||
|
||||
nodes = result.nodes.toJS();
|
||||
// console.log('re-add 1 edge', nodes, result);
|
||||
|
||||
expect(nodes.n1.x).toBeLessThan(nodes.n2.x);
|
||||
expect(nodes.n1.y).toEqual(nodes.n2.y);
|
||||
expect(nodes.n1.x).toEqual(nodes.n3.x);
|
||||
expect(nodes.n1.y).toBeLessThan(nodes.n3.y);
|
||||
expect(nodes.n3.x).toBeLessThan(nodes.n4.x);
|
||||
expect(nodes.n3.y).toEqual(nodes.n4.y);
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
@@ -31,6 +31,7 @@ const NodesChart = React.createClass({
|
||||
return {
|
||||
nodes: makeMap(),
|
||||
edges: makeMap(),
|
||||
history: [],
|
||||
nodeScale: d3.scale.linear(),
|
||||
shiftTranslate: [0, 0],
|
||||
panTranslate: [0, 0],
|
||||
@@ -453,7 +454,8 @@ const NodesChart = React.createClass({
|
||||
height: props.height,
|
||||
scale: nodeScale,
|
||||
margins: MARGINS,
|
||||
topologyId: this.props.topologyId
|
||||
topologyId: this.props.topologyId,
|
||||
history: state.history
|
||||
};
|
||||
|
||||
const timedLayouter = timely(NodesLayout.doLayout);
|
||||
@@ -492,6 +494,7 @@ const NodesChart = React.createClass({
|
||||
}
|
||||
|
||||
return {
|
||||
history: [graph],
|
||||
nodes: stateNodes,
|
||||
edges: stateEdges,
|
||||
nodeScale: nodeScale,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const dagre = require('dagre');
|
||||
const debug = require('debug')('scope:nodes-layout');
|
||||
const ImmSet = require('immutable').Set;
|
||||
const Naming = require('../constants/naming');
|
||||
|
||||
const MAX_NODES = 100;
|
||||
@@ -118,6 +119,38 @@ function runLayoutEngine(imNodes, imEdges, opts) {
|
||||
return layout;
|
||||
}
|
||||
|
||||
function doLayoutEdges(nodes, edges, previousLayout) {
|
||||
const previousEdges = previousLayout.edges;
|
||||
|
||||
// remove old edges
|
||||
let layoutEdges = previousEdges.filter(edge => {
|
||||
return edges.has(edge.get('id'));
|
||||
});
|
||||
|
||||
// add new edges with points from source and target
|
||||
let source;
|
||||
let target;
|
||||
let layoutEdge;
|
||||
edges.forEach(edge => {
|
||||
if (!layoutEdges.has(edge.get('id'))) {
|
||||
source = nodes.get(edge.get('source'));
|
||||
target = nodes.get(edge.get('target'));
|
||||
layoutEdge = edge.set('points', [
|
||||
{x: source.get('x'), y: source.get('y')},
|
||||
{x: target.get('x'), y: target.get('y')}
|
||||
]);
|
||||
layoutEdges = layoutEdges.set(layoutEdge.get('id'), layoutEdge);
|
||||
}
|
||||
});
|
||||
|
||||
previousLayout.edges = layoutEdges;
|
||||
return previousLayout;
|
||||
}
|
||||
|
||||
function hasSameNodes(nodes, prevNodes) {
|
||||
return ImmSet.fromKeys(nodes).equals(ImmSet.fromKeys(prevNodes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout of nodes and edges
|
||||
* @param {Map} nodes All nodes
|
||||
@@ -126,8 +159,22 @@ function runLayoutEngine(imNodes, imEdges, opts) {
|
||||
* @return {object} graph object with nodes, edges, dimensions
|
||||
*/
|
||||
export function doLayout(nodes, edges, opts) {
|
||||
// const options = opts || {};
|
||||
// const history = options.history || [];
|
||||
const options = opts || {};
|
||||
const history = options.history || [];
|
||||
const previous = history.pop();
|
||||
let layout;
|
||||
|
||||
return runLayoutEngine(nodes, edges, opts);
|
||||
if (previous) {
|
||||
// add/remove edges if nodes are the same
|
||||
if (hasSameNodes(previous.nodes, nodes)) {
|
||||
debug('skip layout, only edges changed', edges.size, previous.edges.size);
|
||||
layout = doLayoutEdges(nodes, edges, previous);
|
||||
}
|
||||
}
|
||||
|
||||
if (layout === undefined) {
|
||||
layout = runLayoutEngine(nodes, edges, opts);
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user