Skip to content

Commit

Permalink
feat(layout.DirectedGraph): add graph option to fromGraphLib() (#2783)
Browse files Browse the repository at this point in the history
  • Loading branch information
kumilingus authored Oct 30, 2024
1 parent 0091f59 commit 66ab6b7
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 22 deletions.
25 changes: 19 additions & 6 deletions packages/joint-layout-directed-graph/DirectedGraph.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export namespace DirectedGraph {
height?: number;
}

interface LayoutOptions {
interface LayoutOptions extends ImportOptions, ExportOptions {
align?: 'UR' | 'UL' | 'DR' | 'DL';
rankDir?: 'TB' | 'BT' | 'LR' | 'RL';
ranker?: 'network-simplex' | 'tight-tree' | 'longest-path';
Expand All @@ -27,24 +27,37 @@ export namespace DirectedGraph {
marginY?: number;
resizeClusters?: boolean;
clusterPadding?: dia.Padding;
debugTiming?: boolean;
}

interface ImportOptions {
setPosition?: (element: dia.Element, position: dia.BBox) => void;
setVertices?: boolean | ((link: dia.Link, vertices: dia.Point[]) => void);
setLabels?: boolean | ((link: dia.Link, position: dia.Point, points: dia.Point[]) => void);
debugTiming?: boolean;
exportElement?: (element: dia.Element) => Node;
exportLink?: (link: dia.Link) => Edge;
// deprecated
setLinkVertices?: boolean;
}

interface toGraphLibOptions {
interface ExportOptions {
exportElement?: (element: dia.Element) => Node;
exportLink?: (link: dia.Link) => Edge;
}

interface toGraphLibOptions extends ExportOptions {
[key: string]: any;
}

interface fromGraphLibOptions extends ImportOptions {
graph?: dia.Graph;
[key: string]: any;
}

export function layout(graph: dia.Graph | dia.Cell[], opt?: LayoutOptions): g.Rect;

export function toGraphLib(graph: dia.Graph, opt?: toGraphLibOptions): any;

export function fromGraphLib(glGraph: any, opt?: { [key: string]: any }): dia.Graph;
export function fromGraphLib(glGraph: any, opt?: fromGraphLibOptions): dia.Graph;

// @deprecated pass the `graph` option instead
export function fromGraphLib(this: dia.Graph, glGraph: any, opt?: { [key: string]: any }): dia.Graph;
}
38 changes: 22 additions & 16 deletions packages/joint-layout-directed-graph/DirectedGraph.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ export const DirectedGraph = {
return edge;
},

importElement: function(opt, v, gl) {
importElement: function(nodeId, glGraph, graph, opt) {

var element = this.getCell(v);
var glNode = gl.node(v);
var element = graph.getCell(nodeId);
var glNode = glGraph.node(nodeId);

if (opt.setPosition) {
opt.setPosition(element, glNode);
Expand All @@ -49,12 +49,12 @@ export const DirectedGraph = {
}
},

importLink: function(opt, edgeObj, gl) {
importLink: function(edgeObj, glGraph, graph, opt) {

const SIMPLIFY_THRESHOLD = 0.001;

const link = this.getCell(edgeObj.name);
const glEdge = gl.edge(edgeObj);
const link = graph.getCell(edgeObj.name);
const glEdge = glGraph.edge(edgeObj);
const points = glEdge.points || [];
const polyline = new g.Polyline(points);

Expand All @@ -68,7 +68,7 @@ export const DirectedGraph = {
const polylinePoints = polyline.points.map((point) => (point.toJSON())); // JSON of points after simplification
const numPolylinePoints = polylinePoints.length; // number of points after simplification
// set simplified polyline points as link vertices
// remove first and last polyline points (= source/target sonnectionPoints)
// remove first and last polyline points (= source/target connectionPoints)
link.set('vertices', polylinePoints.slice(1, numPolylinePoints - 1));
}
}
Expand Down Expand Up @@ -166,16 +166,12 @@ export const DirectedGraph = {
graph.startBatch('layout');

DirectedGraph.fromGraphLib(glGraph, {
importNode: this.importElement.bind(graph, opt),
importEdge: this.importLink.bind(graph, opt)
importNode: this.importElement,
importEdge: this.importLink,
...opt,
graph,
});

// // Update the graph.
// graph.fromGraphLib(glGraph, {
// importNode: this.importElement.bind(graph, opt),
// importEdge: this.importLink.bind(graph, opt)
// });

if (opt.resizeClusters) {
// Resize and reposition cluster elements (parents of other elements)
// to fit their children.
Expand Down Expand Up @@ -212,7 +208,17 @@ export const DirectedGraph = {

var importNode = opt.importNode || util.noop;
var importEdge = opt.importEdge || util.noop;
var graph = (this instanceof dia.Graph) ? this : new dia.Graph();
var graph = opt.graph;
if (!graph) {
// The graph should be required as an option.
// @deprecated
if (this instanceof dia.Graph) {
// Backwards compatibility.
graph = this;
} else {
graph = new dia.Graph();
}
}

// Import all nodes.
glGraph.nodes().forEach(function(node) {
Expand Down
87 changes: 87 additions & 0 deletions packages/joint-layout-directed-graph/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,93 @@ QUnit.module('DirectedGraph', function(hooks) {
});
});

QUnit.test('should correctly convert a graphlib graph into JointJS graph', function(assert) {

const glGraph = new graphlib.Graph();
glGraph.setNode(1, { x: 50, y: 50, width: 100, height: 50, label: 'A' });
glGraph.setNode(2, { x: 50, y: 150, width: 100, height: 50, label: 'B' });
glGraph.setNode(3, { x: 50, y: 250, width: 100, height: 50, label: 'C' });
glGraph.setEdge(1, 2, { label: 'Hello' });
glGraph.setEdge(2, 3, { label: 'World!' });

const targetGraph = new joint.dia.Graph({}, { cellNamespace: joint.shapes });
const graph = DirectedGraph.fromGraphLib(glGraph, {
graph: targetGraph,
importNode: (nodeId, glGraph, graph, _opt) => {
const nodeData = glGraph.node(nodeId);
const element = new joint.shapes.standard.Rectangle({
id: nodeId,
position: { x: nodeData.x, y: nodeData.y },
size: { width: nodeData.width, height: nodeData.height },
attrs: { label: { text: nodeData.label }}
});
graph.addCell(element);
},
importEdge: (edgeObj, glGraph, graph, _opt) => {
const edgeData = glGraph.edge(edgeObj);
const link = new joint.shapes.standard.Link({
source: { id: edgeObj.v },
target: { id: edgeObj.w },
labels: [{ attrs: { text: { text: edgeData.label }}}]
});
graph.addCell(link);
}
});

assert.equal(graph, targetGraph);

// elements
const elements = graph.getElements();
assert.equal(elements.length, 3);
let id, x, y, width, height, elementLabel;

(id = elements[0].id);
assert.equal(id, '1');
({ x, y } = elements[0].position());
assert.deepEqual({ x, y }, { x: 50, y: 50 });
({ width, height} = elements[0].size());
assert.deepEqual({ width, height }, {width: 100, height: 50 });
(elementLabel = elements[0].attr('label/text'));
assert.equal(elementLabel, 'A');

(id = elements[1].id);
assert.equal(id, '2');
({ x, y } = elements[1].position());
assert.deepEqual({ x, y }, { x: 50, y: 150 });
({ width, height} = elements[1].size());
assert.deepEqual({ width, height }, {width: 100, height: 50 });
(elementLabel = elements[1].attr('label/text'));
assert.equal(elementLabel, 'B');

(id = elements[2].id);
assert.equal(id, '3');
({ x, y } = elements[2].position());
assert.deepEqual({ x, y }, { x: 50, y: 250 });
({ width, height} = elements[2].size());
assert.deepEqual({ width, height }, {width: 100, height: 50 });
(elementLabel = elements[2].attr('label/text'));
assert.equal(elementLabel, 'C');

// links
const links = graph.getLinks();
assert.equal(links.length, 2);
let source, target, linkLabel;

(source = links[0].source().id);
assert.equal(source, '1');
(target = links[0].target().id);
assert.equal(target, '2');
(linkLabel = links[0].label(0).attrs.text.text);
assert.equal(linkLabel, 'Hello');

(source = links[1].source().id);
assert.equal(source, '2');
(target = links[1].target().id);
assert.equal(target, '3');
(linkLabel = links[1].label(0).attrs.text.text);
assert.equal(linkLabel, 'World!');
});

QUnit.module('toGraphLib(jointGraph[, opt])', function(hooks) {

QUnit.test('should be a function', function(assert) {
Expand Down

0 comments on commit 66ab6b7

Please sign in to comment.