diff --git a/src/constants.js b/src/constants.js index bb9986aa..11b4c14f 100644 --- a/src/constants.js +++ b/src/constants.js @@ -19,7 +19,8 @@ var GRAPHICS = { defaultEasing: 'easeInOut', defaultAnimationTime: 300, - rectFill: '#FF3A3A', + //rectFill: '#FF3A3A', + rectFill: 'hsb(0.8816909813322127,0.7,1)', headRectFill: '#2831FF', rectStroke: '#FFF', rectStrokeWidth: '3', @@ -31,7 +32,13 @@ var GRAPHICS = { edgeUpstreamNoneOpacity: 0.15, visBranchStrokeWidth: 2, - visBranchStrokeColorNone: '#333' + visBranchStrokeColorNone: '#333', + + defaultNodeFill: 'hsba(0.5,0.8,0.7,1)', + defaultNodeStrokeWidth: 2, + defaultNodeStroke: '#FFF', + + orphanNodeFill: 'hsb(0.5,0.8,0.7)', }; /** diff --git a/src/git.js b/src/git.js index 052c0bc8..21072ccd 100644 --- a/src/git.js +++ b/src/git.js @@ -355,6 +355,17 @@ GitEngine.prototype.getUpstreamBranchSet = function() { // this is expensive!! so only call once in a while var commitToSet = {}; + var inArray = function(arr, id) { + var found = false; + _.each(arr, function(wrapper) { + if (wrapper.id == id) { + found = true; + } + }); + + return found; + }; + var bfsSearch = function(commit) { var set = []; var pQueue = [commit]; @@ -373,7 +384,14 @@ GitEngine.prototype.getUpstreamBranchSet = function() { var set = bfsSearch(branch.get('target')); _.each(set, function(id) { commitToSet[id] = commitToSet[id] || []; - commitToSet[id].push(branch.get('id')); + + // only add it if it's not there, so hue blending is ok + if (!inArray(commitToSet[id], branch.get('id'))) { + commitToSet[id].push({ + obj: branch, + id: branch.get('id') + }); + } }); }); @@ -684,7 +702,8 @@ GitEngine.prototype.mergeStarter = function() { GitEngine.prototype.merge = function(targetSource, currentLocation) { // first some conditions - if (this.isUpstreamOf(targetSource, currentLocation)) { + if (this.isUpstreamOf(targetSource, currentLocation) || + this.getCommitFromRef(targetSource) === this.getCommitFromRef(currentLocation)) { throw new CommandResult({ msg: 'Branch already up-to-date' }); @@ -980,6 +999,7 @@ GitEngine.prototype.isUpstreamOf = function(child, ancestor) { GitEngine.prototype.getUpstreamSet = function(ancestor) { var commit = this.getCommitFromRef(ancestor); + var ancestorID = commit.get('id'); var queue = [commit]; var exploredSet = {}; diff --git a/src/tree.js b/src/tree.js index de703569..ed862ec6 100644 --- a/src/tree.js +++ b/src/tree.js @@ -9,6 +9,7 @@ var VisBranch = Backbone.Model.extend({ fill: GRAPHICS.rectFill, stroke: GRAPHICS.rectStroke, + 'stroke-width': GRAPHICS.rectStrokeWidth, offsetX: GRAPHICS.nodeRadius * 4.75, offsetY: 0, @@ -147,7 +148,7 @@ var VisBranch = Backbone.Model.extend({ -this.get('arrowEdgeHeight') ); - var tailLength = 45; + var tailLength = 49; var arrowStartUp = offset2d(arrowInnerUp, f * tailLength, 0); var arrowStartLow = offset2d(arrowInnerLow, f * tailLength, 0); @@ -249,22 +250,7 @@ var VisBranch = Backbone.Model.extend({ } // woof. now it's hard, we need to blend hues... - var myArray = this.getBranchStackArray(); - var hueStrings = []; - _.each(this.getBranchStackArray(), function(branchWrapper) { - var fill = branchWrapper.obj.get('visBranch').get('fill'); - - if (fill.slice(0,3) !== 'hsb') { - // crap. convert to a hsb - var color = Raphael.color(fill); - fill = 'hsb(' + String(color.h) + ',' + String(color.s); - fill = fill + ',' + String(color.l) + ')'; - } - - hueStrings.push(fill); - }); - - return blendHueStrings(hueStrings); + return gitVisuals.blendHuesFromBranchStack(this.getBranchStackArray()); }, genGraphics: function(paper) { @@ -281,23 +267,15 @@ var VisBranch = Backbone.Model.extend({ var rectPos = this.getRectPosition(); var sizeOfRect = this.getRectSize(); - var rect = paper.rect(rectPos.x, rectPos.y, sizeOfRect.w, sizeOfRect.h, 8); - rect.attr({ - fill: this.getFill(), - stroke: this.get('stroke'), - 'stroke-width': GRAPHICS.rectStrokeWidth, - opacity: this.getNonTextOpacity() - }); + var rect = paper + .rect(rectPos.x, rectPos.y, sizeOfRect.w, sizeOfRect.h, 8) + .attr(this.getAttributes().rect); this.set('rect', rect); var arrowPath = this.getArrowPath(); - var arrow = paper.path(arrowPath); - arrow.attr({ - fill: this.getFill(), - stroke: this.get('stroke'), - 'stroke-width': GRAPHICS.rectStrokeWidth, - opacity: this.getNonTextOpacity() - }); + var arrow = paper + .path(arrowPath) + .attr(this.getAttributes().arrow); this.set('arrow', arrow); rect.toFront(); @@ -348,11 +326,15 @@ var VisBranch = Backbone.Model.extend({ height: rectSize.h, opacity: nonTextOpacity, fill: this.getFill(), + stroke: this.get('stroke'), + 'stroke-width': this.get('stroke-width') }, arrow: { path: arrowPath, opacity: nonTextOpacity, - fill: this.getFill() + fill: this.getFill(), + stroke: this.get('stroke'), + 'stroke-width': this.get('stroke-width') } }; }, @@ -388,7 +370,11 @@ var VisNode = Backbone.Model.extend({ commit: null, animationSpeed: GRAPHICS.defaultAnimationTime, - animationEasing: GRAPHICS.defaultEasing + animationEasing: GRAPHICS.defaultEasing, + + fill: GRAPHICS.defaultNodeFill, + 'stroke-width': GRAPHICS.defaultNodeStrokeWidth, + stroke: GRAPHICS.defaultNodeStroke }, validateAtInit: function() { @@ -467,7 +453,10 @@ var VisNode = Backbone.Model.extend({ cx: pos.x, cy: pos.y, opacity: opacity, - r: this.getRadius() + r: this.getRadius(), + fill: this.getFill(), + 'stroke-width': this.get('stroke-width'), + 'stroke': this.get('stroke') }, text: { x: textPos.x, @@ -565,6 +554,21 @@ var VisNode = Backbone.Model.extend({ } }, + getFill: function() { + // first get our status, might be easy from this + var stat = gitVisuals.getCommitUpstreamStatus(this.get('commit')); + if (stat == 'head') { + return GRAPHICS.headRectFill; + } else if (stat == 'none') { + return GRAPHICS.orphanNodeFill; + } + + // now we need to get branch hues + + return gitVisuals.getBlendedHuesForCommit(this.get('commit')); + return this.get('fill'); + }, + attachClickHandlers: function() { var commandStr = 'git show ' + this.get('commit').get('id'); _.each([this.get('circle'), this.get('text')], function(rObj) { @@ -578,9 +582,12 @@ var VisNode = Backbone.Model.extend({ var pos = this.getScreenCoords(); var textPos = this.getTextScreenCoords(); - var circle = cuteSmallCircle(paper, pos.x, pos.y, { - radius: this.getRadius() - }); + var circle = paper.circle( + pos.x, + pos.y, + this.getRadius() + ).attr(this.getAttributes().circle); + var text = paper.text(textPos.x, textPos.y, String(this.get('id'))); text.attr({ 'font-size': this.getFontSize(this.get('id')), diff --git a/src/visuals.js b/src/visuals.js index 3abdf039..4a90bc4a 100644 --- a/src/visuals.js +++ b/src/visuals.js @@ -150,6 +150,37 @@ GitVisuals.prototype.calcUpstreamSets = function() { this.upstreamHeadSet = gitEngine.getUpstreamHeadSet(); }; +GitVisuals.prototype.getCommitUpstreamBranches = function(commit) { + return this.branchStackMap[commit.get('id')]; +}; + +GitVisuals.prototype.getBlendedHuesForCommit = function(commit) { + var branches = this.upstreamBranchSet[commit.get('id')]; + if (!branches) { + throw new Error('that commit doesnt have upstream branches!'); + } + + return this.blendHuesFromBranchStack(branches); +}; + +GitVisuals.prototype.blendHuesFromBranchStack = function(branchStackArray) { + var hueStrings = []; + _.each(branchStackArray, function(branchWrapper) { + var fill = branchWrapper.obj.get('visBranch').get('fill'); + + if (fill.slice(0,3) !== 'hsb') { + // crap! convert + var color = Raphael.color(fill); + fill = 'hsb(' + String(color.h) + ',' + String(color.l); + fill = fill + ',' + String(color.s) + ')'; + } + + hueStrings.push(fill); + }); + + return blendHueStrings(hueStrings); +}; + GitVisuals.prototype.getCommitUpstreamStatus = function(commit) { if (!this.upstreamBranchSet) { throw new Error("Can't calculate this yet!"); @@ -421,7 +452,7 @@ GitVisuals.prototype.drawTreeFirstTime = function() { /************************ - * Random util functions, adapted from liquidGraph + * Random util functions, some from liquidGraph ***********************/ function blendHueStrings(hueStrings) { // assumes a sat of 0.7 and brightness of 1 @@ -451,43 +482,19 @@ function blendHueStrings(hueStrings) { totalSat = totalSat / length; totalBright = totalBright / length; - var hue = Math.atan2(y, x) / (Math.PI * 2); + var hue = Math.atan2(y, x) / (Math.PI * 2); // could fail on 0's if (hue < 0) { hue = hue + 1; } return 'hsb(' + String(hue) + ',' + String(totalSat) + ',' + String(totalBright) + ')'; } -function constructPathStringFromCoords(points,wantsToClose) { - var pathString = "M" + String(Math.round(points[0].x)) + "," + String(Math.round(points[0].y)); - var lp = points[0]; - - _.each(points, function(point) { - var s = " L" + String(Math.round(point.x)) + "," + String(Math.round(point.y)); - pathString = pathString + s; - }); - - if (wantsToClose) { - pathString = pathString + " Z"; - } - return pathString; -}; - function randomHueString() { var hue = Math.random(); var str = 'hsb(' + String(hue) + ',0.7,1)'; return str; }; -function randomGradient() { - var hue = Math.random()*0.8; - var color1 = 'hsb(' + String(hue) + ',0.7,1)'; - var color2 = 'hsb(' + String(hue + 0.2) + ',0.9,1)'; - - var gradient = String(Math.round(Math.random()*180)) + '-' + color1 + '-' + color2; - return gradient; -}; - function cuteSmallCircle(paper, x, y, options) { var options = options || {}; var wantsSameColor = options.sameColor;