mermaid/src/diagrams/gitGraph/gitGraphRenderer.js

259 lines
9.2 KiB
JavaScript
Raw Normal View History

var db = require('./gitGraphAst');
2016-03-31 10:40:35 +05:30
var _ = require('lodash');
var gitGraphParser = require('./parser/gitGraph');
2016-03-29 08:33:38 +05:30
var d3 = require('../../d3');
var Logger = require('../../logger');
var log = new Logger.Log();
2016-03-31 10:40:35 +05:30
var allCommitsDict = {};
2016-04-01 14:17:21 +05:30
var branchNum;
var config = {
2016-04-26 22:52:52 +05:30
nodeSpacing: 75,
2016-04-01 14:17:21 +05:30
lineStrokeWidth: 4,
2016-04-26 22:52:52 +05:30
branchOffset: 50,
2016-04-25 12:38:48 +05:30
lineColor: 'grey',
leftMargin: 50,
2016-04-26 22:52:52 +05:30
nodeRadius: 15,
nodeLabel: {
2016-04-25 13:01:17 +05:30
width: 75,
height: 100,
x: -25,
2016-04-25 13:01:17 +05:30
y: 15
}
2016-04-01 14:17:21 +05:30
}
2016-04-01 20:01:46 +05:30
var apiConfig = {};
exports.setConf = function(c) {
apiConfig = c;
}
2016-03-29 08:33:38 +05:30
2016-04-01 14:17:21 +05:30
2016-03-31 10:40:35 +05:30
function svgCreateDefs(svg) {
svg
2016-04-25 12:38:48 +05:30
.append('defs')
.append('g')
.attr('id', 'def-commit')
.append('circle')
2016-04-26 22:52:52 +05:30
.attr('r', config.nodeRadius)
2016-04-25 12:38:48 +05:30
.attr('cx', 0)
.attr('cy', 0);
svg.select('#def-commit')
2016-04-01 12:05:59 +05:30
.append('foreignObject')
.attr('width', config.nodeLabel.width)
.attr('height', config.nodeLabel.height)
.attr('x', config.nodeLabel.x)
.attr('y', config.nodeLabel.y)
2016-04-25 12:38:48 +05:30
.attr('class', 'node-label')
2016-04-01 12:05:59 +05:30
.attr('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Extensibility')
.append('xhtml:p')
2016-04-25 12:38:48 +05:30
.html('a big chunk of text that should wrap');
2016-03-31 10:40:35 +05:30
}
2016-04-01 12:05:59 +05:30
function svgDrawLine(svg, points, interpolate) {
2016-04-25 12:38:48 +05:30
interpolate = interpolate || 'basis';
2016-03-31 10:40:35 +05:30
var lineGen = d3.svg.line()
2016-04-01 12:05:59 +05:30
.x(function(d) {
return Math.round(d.x)
})
.y(function(d) {
return Math.round(d.y)
})
.interpolate(interpolate);
2016-03-31 10:40:35 +05:30
svg
2016-04-25 12:38:48 +05:30
.append('svg:path')
.attr('d', lineGen(points))
.style('stroke', config.lineColor)
.style('stroke-width', config.lineStrokeWidth)
.style('fill', 'none');
2016-03-31 10:40:35 +05:30
}
2016-04-01 12:05:59 +05:30
// Pass in the element and its pre-transform coords
function getElementCoords(element, coords) {
coords = coords || element.node().getBBox();
var ctm = element.node().getCTM(),
xn = ctm.e + coords.x * ctm.a,
yn = ctm.f + coords.y * ctm.d;
//log.debug(ctm, coords);
return {
left: xn,
top: yn,
width: coords.width,
height: coords.height
};
2016-04-25 12:38:48 +05:30
}
2016-03-31 10:40:35 +05:30
2016-04-26 22:52:52 +05:30
function svgDrawLineForCommits(svg, fromId, toId, direction) {
2016-04-25 12:38:48 +05:30
log.debug('svgDrawLineForCommits: ', fromId, toId);
var fromBbox = getElementCoords(svg.select('#node-' + fromId + ' circle'));
var toBbox = getElementCoords(svg.select('#node-' + toId + ' circle'));
//log.debug('svgDrawLineForCommits: ', fromBbox, toBbox);
2016-04-26 22:52:52 +05:30
switch (direction) {
case 'LR':
if (fromBbox.left - toBbox.left > config.nodeSpacing) {
var lineStart = { x: fromBbox.left - config.nodeSpacing, y: toBbox.top + toBbox.height/2};
var lineEnd ={ x: toBbox.left + toBbox.width, y: toBbox.top + toBbox.height/2 };
svgDrawLine(svg, [lineStart , lineEnd], 'linear')
svgDrawLine(svg, [
{x: fromBbox.left, y: fromBbox.top + fromBbox.height/2},
{x: fromBbox.left - config.nodeSpacing/2, y: fromBbox.top + fromBbox.height/2},
{x: fromBbox.left - config.nodeSpacing/2, y: lineStart.y},
lineStart]);
} else {
svgDrawLine(svg, [{
'x': fromBbox.left,
'y': fromBbox.top + fromBbox.height / 2
}, {
'x': fromBbox.left - config.nodeSpacing/2,
'y': fromBbox.top + fromBbox.height / 2
}, {
'x': fromBbox.left - config.nodeSpacing/2,
'y': toBbox.top + toBbox.height / 2
}, {
'x': toBbox.left + toBbox.width,
'y': toBbox.top + toBbox.height / 2
}]);
}
break;
case 'BT':
// + (fromBbox)
// |
// |
// + (toBbox)
if (toBbox.top - fromBbox.top > config.nodeSpacing) {
lineStart = { x: toBbox.left + toBbox.width/2, y: fromBbox.top + fromBbox.height + config.nodeSpacing};
lineEnd ={ x: toBbox.left + toBbox.width/2, y: toBbox.top };
svgDrawLine(svg, [lineStart , lineEnd], 'linear')
svgDrawLine(svg, [
{x: fromBbox.left + fromBbox.width/2, y: fromBbox.top + fromBbox.height},
{x: fromBbox.left + fromBbox.width/2, y: fromBbox.top + fromBbox.height + config.nodeSpacing/2},
{x: toBbox.left + toBbox.width/2, y: lineStart.y - config.nodeSpacing/2},
lineStart]);
} else {
svgDrawLine(svg, [{
'x': fromBbox.left + fromBbox.width/2,
'y': fromBbox.top + fromBbox.height
}, {
'x': fromBbox.left + fromBbox.width/2,
'y': fromBbox.top + config.nodeSpacing/2
}, {
'x': toBbox.left + toBbox.width/2,
'y': toBbox.top - config.nodeSpacing/2
}, {
'x': toBbox.left + toBbox.width/2,
'y': toBbox.top
}]);
}
break;
2016-04-01 12:05:59 +05:30
}
}
function cloneNode(svg, selector) {
return svg.select(selector).node().cloneNode(true);
2016-03-31 10:40:35 +05:30
}
2016-04-01 12:05:59 +05:30
2016-04-01 14:17:21 +05:30
function renderCommitHistory(svg, commitid, branches, direction) {
2016-03-31 10:40:35 +05:30
var commit;
2016-04-26 22:52:52 +05:30
var numCommits = Object.keys(allCommitsDict).length;
2016-03-31 10:40:35 +05:30
if (_.isString(commitid)) {
do {
commit = allCommitsDict[commitid];
2016-04-25 12:38:48 +05:30
log.debug('in renderCommitHistory', commit.id, commit.seq);
if (svg.select('#node-' + commitid).size() > 0) {
2016-04-26 22:52:52 +05:30
return;
2016-04-01 14:17:21 +05:30
}
2016-03-31 10:40:35 +05:30
svg
2016-04-01 12:05:59 +05:30
.append(function() {
2016-04-25 12:38:48 +05:30
return cloneNode(svg, '#def-commit');
2016-04-01 12:05:59 +05:30
})
2016-04-25 12:38:48 +05:30
.attr('class', 'commit')
.attr('id', function() {
return 'node-' + commit.id;
2016-04-01 12:05:59 +05:30
})
2016-04-25 12:38:48 +05:30
.attr('transform', function() {
2016-04-26 22:52:52 +05:30
switch (direction) {
case 'LR':
return 'translate(' + (commit.seq * config.nodeSpacing + config.leftMargin) + ', '
+ (branchNum * config.branchOffset) + ')';
case 'BT':
return 'translate(' + (branchNum * config.branchOffset + config.leftMargin) + ', '
+ ((numCommits - commit.seq) * config.nodeSpacing) + ')';
}
2016-03-31 10:40:35 +05:30
})
2016-04-25 12:38:48 +05:30
.attr('fill', 'yellow')
.attr('stroke', 'grey')
.attr('stroke-width', '2');
2016-03-31 10:40:35 +05:30
2016-04-25 12:38:48 +05:30
svg.select('#node-' + commit.id + ' p')
.text(commit.id);
2016-04-25 12:38:48 +05:30
var branch = _.find(branches, ['commit', commit]);
if (branch) {
2016-04-25 12:38:48 +05:30
log.debug('found branch ', branch.name);
// don't try to select foreignObject - doesn't work.
// instead select by class name and it works.
2016-04-26 22:52:52 +05:30
svg.select('#node-' + commit.id + ' p')
.append('xhtml:span')
.attr('class', 'branch-label')
.text( ' ' + branch.name);
}
2016-03-31 10:40:35 +05:30
commitid = commit.parent
} while (commitid && allCommitsDict[commitid]);
}
if (_.isArray(commitid)) {
2016-04-25 12:38:48 +05:30
log.debug('found merge commmit', commitid);
2016-04-01 14:17:21 +05:30
renderCommitHistory(svg, commitid[0], branches, direction);
branchNum++;
renderCommitHistory(svg, commitid[1], branches, direction);
2016-03-31 10:40:35 +05:30
}
}
2016-04-26 22:52:52 +05:30
function renderLines(svg, commit, direction) {
while (commit.seq > 0) {
if (_.isString(commit.parent)) {
2016-04-26 22:52:52 +05:30
svgDrawLineForCommits(svg, commit.id, commit.parent, direction);
commit = allCommitsDict[commit.parent];
} else if (_.isArray(commit.parent)) {
2016-04-26 22:52:52 +05:30
svgDrawLineForCommits(svg, commit.id, commit.parent[0], direction)
svgDrawLineForCommits(svg, commit.id, commit.parent[1], direction)
renderLines(svg, allCommitsDict[commit.parent[1]], direction);
commit = allCommitsDict[commit.parent[0]];
}
}
}
2016-04-01 12:05:59 +05:30
exports.draw = function(txt, id, ver) {
2016-03-30 14:36:39 +05:30
try {
var parser;
parser = gitGraphParser.parser;
parser.yy = db;
2016-03-29 08:33:38 +05:30
log.debug('in gitgraph renderer', txt, id, ver);
// Parse the graph definition
2016-04-25 12:38:48 +05:30
parser.parse(txt + '\n');
2016-04-01 20:01:46 +05:30
config = _.extend(config, apiConfig, db.getOptions());
2016-04-25 12:38:48 +05:30
log.debug('effective options', config);
2016-03-30 14:36:39 +05:30
var direction = db.getDirection();
2016-04-26 22:52:52 +05:30
if (direction === 'BT') {
config.nodeLabel.x += 200;
config.nodeLabel.y = -1 * 2* config.nodeRadius;
}
2016-03-31 10:40:35 +05:30
allCommitsDict = db.getCommits();
var branches = db.getBranchesAsObjArray();
2016-03-30 14:36:39 +05:30
var svg = d3.select('#' + id);
2016-03-31 10:40:35 +05:30
svgCreateDefs(svg);
2016-04-01 14:17:21 +05:30
branchNum = 1;
2016-04-25 12:38:48 +05:30
_.each(branches, function(v) {
2016-04-01 14:17:21 +05:30
renderCommitHistory(svg, v.commit.id, branches, direction);
2016-04-26 22:52:52 +05:30
renderLines(svg, v.commit, direction);
branchNum++;
})
2016-03-29 08:33:38 +05:30
2016-04-26 22:52:52 +05:30
//svg.attr('height', 2500);
//svg.attr('width', 2500);
2016-03-30 14:36:39 +05:30
} catch (e) {
2016-04-25 12:38:48 +05:30
log.error('Error while rendering gitgraph');
log.error(e.message);
}
2016-03-29 08:33:38 +05:30
};