Skip to content
This repository has been archived by the owner on Jun 10, 2024. It is now read-only.

Commit

Permalink
Merge pull request #25 from sarbull/main
Browse files Browse the repository at this point in the history
Implement Align links and arrows accordingly to nodes positions
  • Loading branch information
bogdan-sava committed Jul 15, 2021
2 parents 2dfeced + 03fc16c commit 9b78b7d
Show file tree
Hide file tree
Showing 7 changed files with 5,202 additions and 744 deletions.
7 changes: 7 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"env": {
"test": {
"plugins": ["@babel/plugin-transform-modules-commonjs"]
}
}
}
35 changes: 35 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@
<button id="add-data">add-data</button>
</div>


<div class="container bordered">
<happi-graph id="happi-graph-four"></happi-graph>
</div>

<div class="container bordered">
<happi-graph id="happi-graph"></happi-graph>
</div>
Expand All @@ -78,6 +83,36 @@
...itemGroupIconMap
};

let dataFour = {
graphDirection: 'HORIZONTAL',
selectedNodeId: 1,
nodes: [
{ id: 1, label: 'Node 1', group: 'GroupName', properties: { GroupName: 'IBS_UTBLIACML' } },
{ id: 2, label: 'Node 2', group: 'GroupName', properties: {} }
],
links: [
{ from: 1, to: 2, connectionFrom: false, connectionTo: true, type: null },
]
}

let happiGraphInstance4 = document.querySelector('#happi-graph-four');
happiGraphInstance4.iconsMap = iconsMap;
happiGraphInstance4.propertiesMap = propertiesMap;
happiGraphInstance4.linksTypeIconMap = linksTypeIconMap;
happiGraphInstance4.algorithm = 'ELK';
happiGraphInstance4.elkWorkerUrl = '/node_modules/elkjs/lib/elk-worker.js';

happiGraphInstance4.graphData = { ...dataFour };
happiGraphInstance4.init();

happiGraphInstance4.addEventListener('happi-graph-on-node-click', ({ detail }) => {
console.log(detail);
});





let data = {
graphDirection: 'HORIZONTAL',
selectedNodeId: 0,
Expand Down
85 changes: 84 additions & 1 deletion happi-graph-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,87 @@ export const getNodeHeight = (length) => {
(length >= 1 ? (length * 30) : 0);

return defaultHeight + computedHeight;
};
};

export const relativeTo = (nodeA, nodeB) => {
let a = {
x1: nodeA.x,
y1: nodeA.y,
x2: nodeA.x + nodeA.width,
y2: nodeA.y + nodeA.height
};

let b = {
x1: nodeB.x,
y1: nodeB.y,
x2: nodeB.x + nodeB.width,
y2: nodeB.y + nodeB.height
};

if((a.x1 < b.x2) && !(a.x2 > b.x1) && (a.y1 < b.y2) && (a.y2 > b.y1)) {
return { a: 'RIGHT', b: 'LEFT' };
}

if((a.x1 < b.x2) && !(a.x2 > b.x1) && !(a.y1 < b.y2) && (a.y2 > b.y1)) {
return { a: 'RIGHT', b: 'LEFT' };
}

if((a.x1 < b.x2) && !(a.x2 > b.x1) && (a.y1 < b.y2) && !(a.y2 > b.y1)) {
return { a: 'RIGHT', b: 'LEFT' };
}

if((a.x1 < b.x2) && (a.x2 > b.x1) && (a.y1 < b.y2) && !(a.y2 > b.y1)) {
return { a: 'BOTTOM', b: 'TOP' };
}

if(!(a.x1 < b.x2) && (a.x2 > b.x1) && (a.y1 < b.y2) && !(a.y2 > b.y1)) {
return { a: 'LEFT', b: 'RIGHT' };
}

if(!(a.x1 < b.x2) && (a.x2 > b.x1) && (a.y1 < b.y2) && (a.y2 > b.y1)) {
return { a: 'LEFT', b: 'RIGHT' };
}

if(!(a.x1 < b.x2) && (a.x2 > b.x1) && !(a.y1 < b.y2) && (a.y2 > b.y1)) {
return { a: 'LEFT', b: 'RIGHT' };
}

if((a.x1 < b.x2) && (a.x2 > b.x1) && !(a.y1 < b.y2) && (a.y2 > b.y1)) {
return { a: 'TOP', b: 'BOTTOM' };
}

if((a.x1 < b.x2) && (a.x2 > b.x1) && (a.y1 < b.y2) && (a.y2 > b.y1)) {
return { a: 'RIGHT', b: 'RIGHT' };
}
};

export const getNodeAnchorPoint = (node, point) => {
let { width, height } = node;

switch(point) {
case 'TOP':
return { x: node.x + (width / 2), y: node.y };
case 'BOTTOM':
return { x: node.x + (width / 2), y: node.y + height };
case 'LEFT':
return { x: node.x, y: node.y + (height / 2)};
case 'RIGHT':
return { x: node.x + width, y: node.y + (height / 2)};
default:
console.log('WRONG_ANCHOR_POINT_SELECTED');
break;
}
};

export const getLinkCoordinates = (nodeA, nodeB, graphDirection) => {
let _relativeTo = relativeTo(nodeA, nodeB);

let from = getNodeAnchorPoint(nodeA, _relativeTo.a);
let to = getNodeAnchorPoint(nodeB, _relativeTo.b);

return {
from: { x: from.x, y: from.y },
to: { x: to.x, y: to.y }
};
};

128 changes: 128 additions & 0 deletions happi-graph-helpers.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import {
relativeTo,
getNodeAnchorPoint,
getLinkCoordinates
} from './happi-graph-helpers';

/*
nodeB(0, 1)
.
.
.
nodeA(0, 0)
*/
test('nodeB should be above nodeA', () => {
let nodeA = { x: 0, y: 0, width: 100, height: 100 };
let nodeB = { x: 0, y: -101, width: 100, height: 100 };

expect(relativeTo(nodeA, nodeB)).toMatchObject({"a": "TOP", "b": "BOTTOM"});
});

/*
. nodeB(1, 1)
.
nodeA(0, 0) .
*/
test('nodeB should be above nodeA and on the right', () => {
let nodeA = { x: 0, y: 0, width: 100, height: 100 };
let nodeB = { x: 101, y: -101, width: 100, height: 100 };

expect(relativeTo(nodeA, nodeB)).toMatchObject({"a": "RIGHT", "b": "LEFT"});
});

/*
nodeA(0, 0) . . . nodeB(1, 0)
*/
test('nodeB should be on the right of nodeA', () => {
let nodeA = { x: 0, y: 0, width: 100, height: 100 };
let nodeB = { x: 101, y: 0, width: 100, height: 100 };

expect(relativeTo(nodeA, nodeB)).toMatchObject({"a": "RIGHT", "b": "LEFT"});
});

/*
nodeA(0, 0) .
.
. nodeB(1, -1)
*/
test('nodeB should be below nodeA on the right', () => {
let nodeA = { x: 0, y: 0, width: 100, height: 100 };
let nodeB = { x: 100, y: -101, width: 100, height: 100 };

expect(relativeTo(nodeA, nodeB)).toMatchObject({"a": "RIGHT", "b": "LEFT"});
});

/*
nodeA(0, 0)
.
.
.
nodeB(0, -1)
*/
test('nodeB should be below nodeA', () => {
let nodeA = { x: 0, y: 0, width: 100, height: 100 };
let nodeB = { x: 0, y: 101, width: 100, height: 100 };

expect(relativeTo(nodeA, nodeB)).toMatchObject({"a": "BOTTOM", "b": "TOP"});
});

/*
. nodeA(0, 0)
.
nodeB(-1, -1) .
*/
test('nodeB should be below nodeA on the left', () => {
let nodeA = { x: 0, y: 0, width: 100, height: 100 };
let nodeB = { x: -101, y: 101, width: 100, height: 100 };

expect(relativeTo(nodeA, nodeB)).toMatchObject({"a": "LEFT", "b": "RIGHT"});
});

/*
nodeB(-1, 0) . . . nodeA(0, 0)
*/
test('nodeB should be on left of nodeA', () => {
let nodeA = { x: 0, y: 0, width: 100, height: 100 };
let nodeB = { x: -101, y: 0, width: 100, height: 100 };

expect(relativeTo(nodeA, nodeB)).toMatchObject({"a": "LEFT", "b": "RIGHT"});
});

/*
nodeB(-1, 1) .
.
. nodeA(0, 0)
*/
test('nodeB should be above nodeA on the left', () => {
let nodeA = { x: 0, y: 0, width: 100, height: 100 };
let nodeB = { x: -101, y: -101, width: 100, height: 100 };

expect(relativeTo(nodeA, nodeB)).toMatchObject({"a": "LEFT", "b": "RIGHT"});
});

test('getNodeAnchorPoint', () => {
let node = {
width: 100,
height: 100,
properties: {},
x: 0,
y: 0
};

expect(getNodeAnchorPoint(node, 'TOP')).toMatchObject({ x: 50, y: 0 });
expect(getNodeAnchorPoint(node, 'BOTTOM')).toMatchObject({ x: 50, y: 100 });
expect(getNodeAnchorPoint(node, 'LEFT')).toMatchObject({ x: 0, y: 50 });
expect(getNodeAnchorPoint(node, 'RIGHT')).toMatchObject({ x: 100, y: 50 });
expect(getNodeAnchorPoint({ ...node, properties: { a: 1 } }, 'RIGHT')).toMatchObject({ x: 100, y: 50 });
});

test('getLinkCoordinates', () => {
let nodeA = { width: 100, height: 100, properties: { a: 1, b: 2 }, x: 0, y: 0 };

let nodeB = { width: 100, height: 100, properties: { a: 1 }, x: 0, y: 200 };

expect(getLinkCoordinates(nodeA, nodeB, 'HORIZONTAL')).toMatchObject({
from: {x: 50, y: 100},
to: {x: 50, y: 200}
});
});
72 changes: 62 additions & 10 deletions happi-graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
addIcon,
addProperties,
getNodeHeight,
isSelected
isSelected,
getLinkCoordinates
} from './happi-graph-helpers';

import 'elkjs/lib/elk-api';
Expand Down Expand Up @@ -322,7 +323,6 @@ class HappiGraph extends PolymerElement {

elk.layout(graph)
.then((g) => {
console.log(g)

this.nodes = [ ...g.children ];
this.links = [ ...g.edges ];
Expand Down Expand Up @@ -407,15 +407,51 @@ class HappiGraph extends PolymerElement {
.filter(function(_d) {
return _d.from.id === d.id;
})
.attr('x1', () => self.graphDirection === 'HORIZONTAL' ? d3.event.x + d.width + 3 : d3.event.x + (d.width/2))
.attr('y1', () => self.graphDirection === 'HORIZONTAL' ? d3.event.y + (d.height/2) : d3.event.y - 3);
.attr('x1', (_d) => {
let { from, to } = getLinkCoordinates(_d.from, _d.to, self.graphDirection);

return from.x;
})
.attr('y1', (_d) => {
let { from, to } = getLinkCoordinates(_d.from, _d.to, self.graphDirection);

return from.y;
})
.attr('x2', (_d) => {
let { from, to } = getLinkCoordinates(_d.from, _d.to, self.graphDirection);

return to.x;
})
.attr('y2', (_d) => {
let { from, to } = getLinkCoordinates(_d.from, _d.to, self.graphDirection);

return to.y;
});

_links
.filter(function(_d) {
return _d.to.id === d.id;
})
.attr('x2', () => self.graphDirection === 'HORIZONTAL' ? d3.event.x - 5 : d3.event.x + (d.width/2))
.attr('y2', () => self.graphDirection === 'HORIZONTAL' ? d3.event.y + (d.height/2) : d3.event.y + (d.height) + 5);
.attr('x1', (_d) => {
let { from, to } = getLinkCoordinates(_d.from, _d.to, self.graphDirection);

return from.x;
})
.attr('y1', (_d) => {
let { from, to } = getLinkCoordinates(_d.from, _d.to, self.graphDirection);

return from.y;
})
.attr('x2', (_d) => {
let { from, to } = getLinkCoordinates(_d.from, _d.to, self.graphDirection);

return to.x;
})
.attr('y2', (_d) => {
let { from, to } = getLinkCoordinates(_d.from, _d.to, self.graphDirection);

return to.y;
});
})
.on('end', (d) => {
// console.log('DRAG_END', d);
Expand Down Expand Up @@ -455,10 +491,26 @@ class HappiGraph extends PolymerElement {
.attr('marker-end', (d) => (d.connectionTo) ? 'url(#arrow-end)' : null)
.attr('from', function(d) { return d.from.id; })
.attr('to', function(d) { return d.to.id; })
.attr('x1', (d) => self.graphDirection === 'HORIZONTAL' ? d.from.x + d.from.width + 3 : d.from.x + (d.from.width/2))
.attr('y1', (d) => self.graphDirection === 'HORIZONTAL' ? d.from.y + (d.from.height/2) : d.from.y - 3)
.attr('x2', (d) => self.graphDirection === 'HORIZONTAL' ? d.to.x - 5 : d.to.x + (d.to.width/2))
.attr('y2', (d) => self.graphDirection === 'HORIZONTAL' ? d.to.y + (d.to.height/2) : d.to.y + (d.to.height) + 5);
.attr('x1', (d) => {
let { from, to } = getLinkCoordinates(d.from, d.to, self.graphDirection);

return from.x;
})
.attr('y1', (d) => {
let { from, to } = getLinkCoordinates(d.from, d.to, self.graphDirection);

return from.y;
})
.attr('x2', (d) => {
let { from, to } = getLinkCoordinates(d.from, d.to, self.graphDirection);

return to.x;
})
.attr('y2', (d) => {
let { from, to } = getLinkCoordinates(d.from, d.to, self.graphDirection);

return to.y;
});
}

zooming() {
Expand Down
Loading

0 comments on commit 9b78b7d

Please sign in to comment.