Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/graph layout #20

Merged
merged 8 commits into from
May 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pipeline-builder",
"version": "0.3.4-dev",
"version": "0.3.5-dev",
"description": "Pipeline Builder",
"main": "dist/pipeline.js",
"jsnext:main": "dist/pipeline.module.js",
Expand Down
51 changes: 24 additions & 27 deletions src/visual/VisualGroup.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,47 @@
import joint from 'jointjs';
import _ from 'lodash';
import VisualStep from './VisualStep';

const cDefaultWidth = 100;
const cMinHeight = 100;
const cEmbedsPadding = 50;

/** Class that provides graphical representation for the Group.
* @private
*/
export default class VisualGroup extends joint.shapes.devs.Model {
constructor(step, opts = {}) {
super({
position: {
x: (opts.x - cDefaultWidth / 2) || 0,
y: (opts.y - cMinHeight / 2) || 0,
},
export default class VisualGroup extends VisualStep {
/**
* Creates new Group visual representation. Accepts all options for
* joint.shapes.dev.Model and Step and embeds padding values.
*/
constructor(opts = {}) {
super(_.defaultsDeep(opts, {
attrs: {
'.label': {
text: step.type,
text: opts.step.type,
},
},
type: 'VisualGroup',
});

this.step = step;
this.update();
embedsPadding: {
left: cEmbedsPadding,
right: cEmbedsPadding,
top: cEmbedsPadding,
bottom: cEmbedsPadding,
},
}));
}

/**
* Updates visual step according to the model.
*/
update() {
const step = this.step;

this.attr('.label', {
text: step.type,
});
super.update();

if (this.graph) {
this.fitEmbeds({
deep: true,
padding: {
left: 5,
right: 5,
top: 50,
bottom: 50,
},
padding: this.attributes.embedsPadding,
});
}
}

_getLabel() {
return this.step.type;
}
}
18 changes: 6 additions & 12 deletions src/visual/VisualLink.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import _ from 'lodash';
import joint from 'jointjs';

/**
Expand All @@ -6,19 +7,12 @@ import joint from 'jointjs';
*/
export default class VisualLink extends joint.shapes.devs.Link {

constructor(sourceId, sourcePort, targetId, targetPort, conn) {
super({
source: {
id: sourceId,
port: sourcePort,
},
target: {
id: targetId,
port: targetPort,
},
});
constructor(opts) {
super(_.defaultsDeep(opts, {
conn: null,
}));
/** Connection from model. */
this.conn = conn;
this.conn = this.attributes.conn;
}

_check(name, port) {
Expand Down
33 changes: 24 additions & 9 deletions src/visual/VisualStep.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import joint from 'jointjs';
import _ from 'lodash';
import joint from 'jointjs';

const cDefaultWidth = 100;
const cMinHeight = 100;
const cPixelPerSymbol = 15;
const cPixelPerSymbol = 10;
const cLabelMargin = 20;
const cHeightPerPort = 50;

function findMaxLen(strList) {
Expand All @@ -14,21 +15,30 @@ function findMaxLen(strList) {
* @private
*/
export default class VisualStep extends joint.shapes.devs.Model {
constructor(step, opts = {}) {
super({
/**
* Creates new Step visual representation. Accepts all options for
* joint.shapes.dev.Model and various custom parameters.
* @param {object=} opts - Action description.
* @param {Object.<string, Step>} opts.step - Step instance.
* @param {Object.<string, int>} [opts.x=0] - Center x coordinate.
* @param {Object.<string, int>} [opts.y=0] - Center y coordinate.
*/
constructor(opts = { step: null, x: 0, y: 0 }) {
super(_.defaultsDeep(opts, {
position: {
x: (opts.x - cDefaultWidth / 2) || 0,
y: (opts.y - cMinHeight / 2) || 0,
},
attrs: {
'.label': {
text: step.name,
text: opts.step.name,
},
},
type: 'VisualStep',
});
step: opts.step,
}));

this.step = step;
this.step = opts.step;
this.update();
}

Expand All @@ -40,7 +50,8 @@ export default class VisualStep extends joint.shapes.devs.Model {
const inNames = Object.keys(step.i);
const outNames = Object.keys(step.o);
const height = Math.max(cMinHeight, cHeightPerPort * Math.max(inNames.length, outNames.length));
const width = Math.max(cDefaultWidth, step.name.length * cPixelPerSymbol);
const label = this._getLabel();
const width = Math.max(cDefaultWidth, label.length * cPixelPerSymbol + 2 * cLabelMargin);
this.set({
inPorts: inNames,
outPorts: outNames,
Expand All @@ -50,7 +61,7 @@ export default class VisualStep extends joint.shapes.devs.Model {
},
});
this.attr('.label', {
text: step.name,
text: label,
});

const ports = this.getPorts();
Expand Down Expand Up @@ -84,4 +95,8 @@ export default class VisualStep extends joint.shapes.devs.Model {
}
return bbox;
}

_getLabel() {
return this.step.name;
}
}
82 changes: 75 additions & 7 deletions src/visual/Visualizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,57 @@ V.prototype.transform = function fun(matrix, opt) {
return this;
};

function getDescendants(cell, currDepth) {
const embeds = cell.getEmbeddedCells();
let best = { cell, currDepth };
_.forEach(embeds, (child) => {
const chBest = getDescendants(child, currDepth + 1);
if (chBest.currDepth > best.currDepth) {
best = chBest;
}
});
return best;
}

function getHighestDescendant(cell) {
return getDescendants(cell, 0).cell;
}

function createSubstituteCells(graph) {
const newCellsMap = graph.cloneCells(graph.getCells());

const newCells = [];
let cellIdx = 0;
_.forEach(newCellsMap, (newCell, key) => {
const cellProto = graph.getCell(key);
if (newCell.isLink()) {
const protoSrc = cellProto.getSourceElement();
const protoDst = cellProto.getTargetElement();
if (protoDst.isEmbeddedIn(protoSrc, { deep: true }) ||
protoSrc.isEmbeddedIn(protoDst, { deep: true })) {
return;
}
const source = newCellsMap[getHighestDescendant(protoSrc).id];
const target = newCellsMap[getHighestDescendant(protoDst).id];
newCell.get('source').id = source.id;
newCell.get('target').id = target.id;
} else {
const bbox = cellProto.getBBox();
newCell.set('size', {
width: bbox.width,
height: bbox.height,
});
}
newCells[cellIdx] = newCell;
cellIdx += 1;
newCell.proto = cellProto;
});

const gr = new joint.dia.Graph();
gr.addCells(newCells);
return gr;
}

/**
* Class that allows to work with graphical pipeline representation.
*
Expand Down Expand Up @@ -144,6 +195,7 @@ export default class Visualizer {
this._step = step;
this._update();
this.layout();
this._update();// call update once more to invoke fitEmbeds
this.zoom.fitToPage({ padding: 10, maxScale: 1 });
this._timer = setInterval(() => this._update(), 30);

Expand All @@ -154,17 +206,22 @@ export default class Visualizer {
* Layout the contents automatically.
*/
layout() {
const newCells = createSubstituteCells(this._graph);
const settings = {
marginX: 100,
marginY: 10,
rankSep: 230,
rankSep: 100,
nodeSep: 80,
rankDir: 'LR',
setLinkVertices: false,
resizeClusters: false,
resizeClusters: true,
setPosition: (element, glNode) => {
element.proto.set('position', {
x: glNode.x - glNode.width / 2,
y: glNode.y - glNode.height / 2 });
}, // setVertices is ignored
};

joint.layout.DirectedGraph.layout(this._graph, settings);
joint.layout.DirectedGraph.layout(newCells, settings);
}

_loopPorts(ports, source) {
Expand All @@ -180,8 +237,17 @@ export default class Visualizer {
_.find(links, link => link.conn === conn) === undefined &&
!srcIsGroup &&
!dstIsGroup) {
const link = new VisualLink(
source.id, port.name, children[targetName].id, conn.to.name, conn);
const link = new VisualLink({
source: {
id: source.id,
port: port.name,
},
target: {
id: children[targetName].id,
port: conn.to.name,
},
conn,
});
link.addTo(this._graph);
}
});
Expand Down Expand Up @@ -244,14 +310,16 @@ export default class Visualizer {
x: this.paper.el.offsetWidth / 2,
y: this.paper.el.offsetHeight / 2,
});
opts.step = child;
if (!visChild) {
visChild = _.isUndefined(child.type) ? new VisualStep(child, opts) : new VisualGroup(child, opts);
visChild = _.isUndefined(child.type) ? new VisualStep(opts) : new VisualGroup(opts);

children[name] = visChild;

this._graph.addCell(visChild);
if (parent) {
parent.embed(visChild);
parent.update();
}
} else {
// it is essential to update links before the step!
Expand Down