diff --git a/packages/joint-layout-directed-graph/DirectedGraph.d.ts b/packages/joint-layout-directed-graph/DirectedGraph.d.ts index 34f9cd051..c56c217f1 100644 --- a/packages/joint-layout-directed-graph/DirectedGraph.d.ts +++ b/packages/joint-layout-directed-graph/DirectedGraph.d.ts @@ -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'; @@ -27,17 +27,28 @@ 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; } @@ -45,6 +56,8 @@ export namespace DirectedGraph { 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; } diff --git a/packages/joint-layout-directed-graph/DirectedGraph.mjs b/packages/joint-layout-directed-graph/DirectedGraph.mjs index c9e65a9a6..fef1b73cf 100644 --- a/packages/joint-layout-directed-graph/DirectedGraph.mjs +++ b/packages/joint-layout-directed-graph/DirectedGraph.mjs @@ -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); @@ -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); @@ -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)); } } @@ -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. @@ -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) { diff --git a/packages/joint-layout-directed-graph/test/index.js b/packages/joint-layout-directed-graph/test/index.js index bfb6a82e0..11d659296 100644 --- a/packages/joint-layout-directed-graph/test/index.js +++ b/packages/joint-layout-directed-graph/test/index.js @@ -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) {