1
0
Fork 0

GraphViewer: New zooming-behaviour implemented, still open: FishEye

This commit is contained in:
Michael Hackstein 2013-05-06 11:36:52 +02:00
parent 169dcfeb66
commit e70af5ba25
3 changed files with 132 additions and 83 deletions

View File

@ -41,10 +41,10 @@ function ZoomManager(width, height, g, nodeShaper, edgeShaper, config) {
if (g === undefined || g.node === undefined || g.node().tagName !== "G") { if (g === undefined || g.node === undefined || g.node().tagName !== "G") {
throw("A group has to be given."); throw("A group has to be given.");
} }
if (nodeShaper === undefined || nodeShaper.activate === undefined) { if (nodeShaper === undefined || nodeShaper.activateLabel === undefined) {
throw("The Node shaper has to be given."); throw("The Node shaper has to be given.");
} }
if (edgeShaper === undefined || edgeShaper.activate === undefined) { if (edgeShaper === undefined || edgeShaper.activateLabel === undefined) {
throw("The Edge shaper has to be given."); throw("The Edge shaper has to be given.");
} }
@ -54,9 +54,12 @@ function ZoomManager(width, height, g, nodeShaper, edgeShaper, config) {
nodeRadius, nodeRadius,
labelToggle, labelToggle,
currentZoom, currentZoom,
currentTranslation,
currentLimit, currentLimit,
currentDistortion, currentDistortion,
currentDistortionRadius, currentDistortionRadius,
baseDist,
baseDRadius,
size = width * height, size = width * height,
zoom, zoom,
@ -73,6 +76,12 @@ function ZoomManager(width, height, g, nodeShaper, edgeShaper, config) {
} }
return Math.floor(size / div); return Math.floor(size / div);
}, },
calcDistortionValues = function () {
currentDistortion = baseDist / currentZoom - 0.99999999; // Always > 0
currentDistortionRadius = baseDRadius / currentZoom;
},
parseConfig = function (conf) { parseConfig = function (conf) {
if (conf === undefined) { if (conf === undefined) {
conf = {}; conf = {};
@ -82,40 +91,32 @@ function ZoomManager(width, height, g, nodeShaper, edgeShaper, config) {
fontMin = conf.minFont || 6, fontMin = conf.minFont || 6,
rMax = conf.maxRadius || 25, rMax = conf.maxRadius || 25,
rMin = conf.minRadius || 1; rMin = conf.minRadius || 1;
baseDist = conf.focusZoom || 1;
baseDRadius = conf.focusRadius || 100;
fontSize = fontMax; fontSize = fontMax;
nodeRadius = rMax; nodeRadius = rMax;
labelToggle = 0; labelToggle = fontMin / fontMax;
currentDistortion = 0;
currentDistortionRadius = 100;
currentLimit = calcNodeLimit();
currentZoom = 1; currentZoom = 1;
calcDistortionValues();
currentLimit = calcNodeLimit();
zoom = d3.behavior.zoom() zoom = d3.behavior.zoom()
.scaleExtent([rMin/rMax, 1]) .scaleExtent([rMin/rMax, 1])
.on("zoom", function() { .on("zoom", function() {
// TODO: Still to be implemented currentZoom = d3.event.scale;
currentZoom = d3.event.scale; currentLimit = calcNodeLimit();
currentLimit = calcNodeLimit(); nodeShaper.activateLabel(currentZoom >= labelToggle);
edgeShaper.activateLabel(currentZoom >= labelToggle);
//curTrans = $.extend({}, d3.event.translate); calcDistortionValues();
/* currentTranslation = $.extend({}, d3.event.translate);
//curTrans[0] /= curZoom; g.attr("transform",
//curTrans[1] /= curZoom; "translate(" + currentTranslation + ")"
//console.log("here", d3.event.translate, d3.event.scale); + " scale(" + currentZoom + ")");
g.attr("transform",
"translate(" + d3.event.translate + ")"
+ " scale(" + d3.event.scale + ")");
if (d3.event.scale < stopLabel) {
test.remove();
}
/*
fisheye
.distortion(1/d3.event.scale * fe_dist - 1);
*/
//.radius(1/d3.event.scale * fe_radius);
}); });
}; };
@ -140,8 +141,12 @@ function ZoomManager(width, height, g, nodeShaper, edgeShaper, config) {
self.mouseMoveHandle = function() { self.mouseMoveHandle = function() {
// TODO // TODO
var focus = d3.mouse(this); var focus = d3.mouse(this);
focus[0] += curTrans[0]; focus[0] -= currentTranslation[0];
focus[1] += curTrans[1]; focus[0] /= currentZoom;
focus[1] -= currentTranslation[1];
focus[1] /= currentZoom;
fisheye.focus(focus); fisheye.focus(focus);
node.each(function(d) { d.fisheye = fisheye(d); }) node.each(function(d) { d.fisheye = fisheye(d); })

View File

@ -57,7 +57,7 @@ var helper = helper || {};
var evt = document.createEvent("MouseEvents"), var evt = document.createEvent("MouseEvents"),
testee = document.getElementById(objectId); testee = document.getElementById(objectId);
evt.initMouseEvent("DOMMouseScroll", true, true, window, evt.initMouseEvent("DOMMouseScroll", true, true, window,
-10, 0, 0, 0, 0, false, false, false, false, 0, null); 10, 0, 0, 0, 0, false, false, false, false, 0, null);
testee.dispatchEvent(evt); testee.dispatchEvent(evt);
}; };
@ -65,7 +65,7 @@ var helper = helper || {};
var evt = document.createEvent("MouseEvents"), var evt = document.createEvent("MouseEvents"),
testee = document.getElementById(objectId); testee = document.getElementById(objectId);
evt.initMouseEvent("DOMMouseScroll", true, true, window, evt.initMouseEvent("DOMMouseScroll", true, true, window,
10, 0, 0, 0, 0, false, false, false, false, 0, null); -10, 0, 0, 0, 0, false, false, false, false, 0, null);
testee.dispatchEvent(evt); testee.dispatchEvent(evt);
}; };

View File

@ -1,6 +1,6 @@
/*jslint indent: 2, nomen: true, maxlen: 100, white: true plusplus: true */ /*jslint indent: 2, nomen: true, maxlen: 100, white: true plusplus: true */
/*global beforeEach, afterEach, jasmine */ /*global beforeEach, afterEach, jasmine */
/*global describe, it, expect */ /*global describe, it, expect, spyOn */
/*global window, eb, loadFixtures, document */ /*global window, eb, loadFixtures, document */
/*global $, _, d3*/ /*global $, _, d3*/
/*global helper*/ /*global helper*/
@ -49,6 +49,22 @@
simulateZoomIn = function () { simulateZoomIn = function () {
helper.simulateScrollDownMouseEvent("svg"); helper.simulateScrollDownMouseEvent("svg");
},
lastNodeShaperCall = function() {
return nodeShaperMock.activateLabel.mostRecentCall.args[0];
},
lastEdgeShaperCall = function() {
return edgeShaperMock.activateLabel.mostRecentCall.args[0];
},
labelsAreInvisible = function () {
return lastNodeShaperCall() === false;
},
labelsAreVisible = function () {
return lastNodeShaperCall() === true;
}; };
beforeEach(function () { beforeEach(function () {
@ -162,7 +178,7 @@
}); });
describe('default values', function() { describe('checking the value propagation', function() {
var fontMax, var fontMax,
fontMin, fontMin,
@ -172,7 +188,9 @@
nodeMaxNoLabel, nodeMaxNoLabel,
nodeMinLabel, nodeMinLabel,
nodeMin, nodeMin,
distRBase; distRBase,
minScale,
toggleScale;
beforeEach(function() { beforeEach(function() {
@ -187,6 +205,8 @@
radMax = 25; radMax = 25;
radMin = 1; radMin = 1;
distRBase = 100; distRBase = 100;
minScale = radMin / radMax;
toggleScale = fontMin / fontMax;
nodeMax = Math.floor(w * h / labelSize(fontMax)); nodeMax = Math.floor(w * h / labelSize(fontMax));
nodeMinLabel = Math.floor(w * h / labelSize(fontMin)); nodeMinLabel = Math.floor(w * h / labelSize(fontMin));
nodeMaxNoLabel = Math.floor(w * h / circleSize((radMax - radMin) / 2 + radMin)); nodeMaxNoLabel = Math.floor(w * h / circleSize((radMax - radMin) / 2 + radMin));
@ -199,6 +219,32 @@
expect(manager.getDistortionRadius()).toEqual(distRBase); expect(manager.getDistortionRadius()).toEqual(distRBase);
}); });
it('should trigger the activateLabel function on each zoom-in event', function() {
simulateZoomIn();
expect(nodeShaperMock.activateLabel).toHaveBeenCalled();
expect(edgeShaperMock.activateLabel).toHaveBeenCalled();
});
it('the zoom-out event should decrease the scale', function() {
var oldSF = manager.scaleFactor();
simulateZoomOut();
expect(manager.scaleFactor()).toBeLessThan(oldSF);
});
it('the zoom-in event should increase the scale', function() {
simulateZoomOut();
simulateZoomOut();
var oldSF = manager.scaleFactor();
simulateZoomIn();
expect(manager.scaleFactor()).toBeGreaterThan(oldSF);
});
it('should trigger the activateLabel function on each zoom-out event', function() {
simulateZoomOut();
expect(nodeShaperMock.activateLabel).toHaveBeenCalled();
expect(edgeShaperMock.activateLabel).toHaveBeenCalled();
});
it('should not be possible to zoom in if max-zoom is reached', function() { it('should not be possible to zoom in if max-zoom is reached', function() {
var oldNL = manager.getNodeLimit(), var oldNL = manager.getNodeLimit(),
oldD = manager.getDistortion(), oldD = manager.getDistortion(),
@ -209,6 +255,7 @@
expect(manager.getDistortion()).toEqual(oldD); expect(manager.getDistortion()).toEqual(oldD);
expect(manager.getDistortionRadius()).toEqual(oldDR); expect(manager.getDistortionRadius()).toEqual(oldDR);
expect(manager.scaleFactor()).not.toBeLessThan(oldSF); expect(manager.scaleFactor()).not.toBeLessThan(oldSF);
}); });
it('should be possible to zoom-out until labels are removed', function() { it('should be possible to zoom-out until labels are removed', function() {
@ -217,7 +264,7 @@
oldD, oldD,
oldDR, oldDR,
loopCounter = 0; loopCounter = 0;
while (manager.getFontSize() > fontMin && manager.getFontSize() !== null) { do {
oldNL = manager.getNodeLimit(); oldNL = manager.getNodeLimit();
oldD = manager.getDistortion(); oldD = manager.getDistortion();
oldDR = manager.getDistortionRadius(); oldDR = manager.getDistortionRadius();
@ -226,18 +273,14 @@
expect(manager.getNodeLimit()).not.toBeLessThan(oldNL); expect(manager.getNodeLimit()).not.toBeLessThan(oldNL);
expect(manager.getDistortion()).toBeGreaterThan(oldD); expect(manager.getDistortion()).toBeGreaterThan(oldD);
expect(manager.getDistortionRadius()).toBeGreaterThan(oldDR); expect(manager.getDistortionRadius()).toBeGreaterThan(oldDR);
expect(manager.scaleFactor()).not.toBeLessThan(oldSF); expect(manager.scaleFactor()).toBeLessThan(oldSF);
loopCounter++; loopCounter++;
if (loopCounter === 1000) { if (loopCounter === 1000) {
this.fail(new Error('The minimal font-size should have been reached')); this.fail(new Error('The minimal font-size should have been reached'));
break; break;
} }
} } while (labelsAreVisible());
if (manager.getFontSize() === null) { simulateZoomIn();
simulateZoomIn();
}
expect(manager.getFontSize()).toBeCloseTo(fontMin, 6);
expect(manager.getRadius()).toBeCloseTo((radMax-radMin) / 2 + radMin, 6);
expect(manager.getNodeLimit()).toBeCloseTo(nodeMinLabel, 6); expect(manager.getNodeLimit()).toBeCloseTo(nodeMinLabel, 6);
//expect(manager.getDistortion()).toBeCloseTo(0, 6); //expect(manager.getDistortion()).toBeCloseTo(0, 6);
}); });
@ -246,50 +289,49 @@
beforeEach(function() { beforeEach(function() {
var loopCounter = 0; var loopCounter = 0;
while (manager.getFontSize() > fontMin && manager.getFontSize() !== null) { do {
simulateZoomOut(); simulateZoomOut();
loopCounter++; loopCounter++;
if (loopCounter === 1000) { if (loopCounter === 1000) {
this.fail(new Error('The minimal font-size should have been reached')); this.fail(new Error('The minimal font-size should have been reached'));
break; break;
} }
} } while (labelsAreVisible());
if (manager.getFontSize() === null) { simulateZoomIn();
simulateZoomIn();
}
}); });
it('should be able to zoom-in again', function() { it('should be able to zoom-in again', function() {
var oldFS, var oldNL,
oldR,
oldNL,
oldD, oldD,
oldDR,
oldSF,
loopCounter = 0; loopCounter = 0;
while (manager.getFontSize() < fontMax) { while (manager.scaleFactor() < 1) {
oldFS = manager.getFontSize();
oldR = manager.getRadius();
oldNL = manager.getNodeLimit(); oldNL = manager.getNodeLimit();
oldD = manager.getDistortion(); oldD = manager.getDistortion();
oldDR = manager.getDistortionRadius();
oldSF = manager.scaleFactor();
simulateZoomIn(); simulateZoomIn();
expect(manager.getFontSize()).toBeGreaterThan(oldFS);
expect(manager.getRadius()).toBeGreaterThan(oldR);
expect(manager.getNodeLimit()).not.toBeGreaterThan(oldNL); expect(manager.getNodeLimit()).not.toBeGreaterThan(oldNL);
expect(manager.getDistortion()).toBeLessThan(oldD); expect(manager.getDistortion()).toBeLessThan(oldD);
expect(manager.getDistortionRadius()).toBeLessThan(oldDR);
expect(manager.scaleFactor()).toBeGreaterThan(oldSF);
loopCounter++; loopCounter++;
if (loopCounter === 1000) { if (loopCounter === 1000) {
this.fail(new Error('The maximal font-size should have been reached')); this.fail(new Error('The maximal font-size should have been reached'));
break; break;
} }
} }
expect(manager.getFontSize()).toEqual(fontMax); expect(manager.scaleFactor()).toEqual(1);
expect(manager.getRadius()).toEqual(radMax);
expect(manager.getNodeLimit()).toEqual(nodeMax); expect(manager.getNodeLimit()).toEqual(nodeMax);
expect(manager.getDistortion()).toBeCloseTo(0, 6); expect(manager.getDistortion()).toBeCloseTo(0, 6);
expect(manager.getDistortionRadius()).toEqual(distRBase);
}); });
it('should return null for font-size if further zoomed out', function() { it('should remove the labels if further zoomed out', function() {
simulateZoomOut(); simulateZoomOut();
expect(manager.getFontSize()).toEqual(null); expect(lastNodeShaperCall()).toBeFalsy();
expect(lastEdgeShaperCall()).toBeFalsy();
}); });
it('should significantly increase the node limit if further zoomed out', function() { it('should significantly increase the node limit if further zoomed out', function() {
@ -298,28 +340,29 @@
}); });
it('should be able to zoom-out until minimal node radius is reached', function() { it('should be able to zoom-out until minimal node radius is reached', function() {
var oldR, var oldNL,
oldNL,
oldD, oldD,
oldDR,
oldSF,
loopCounter = 0; loopCounter = 0;
while (manager.getRadius() > radMin) { while (manager.scaleFactor() > minScale) {
oldR = manager.getRadius();
oldNL = manager.getNodeLimit(); oldNL = manager.getNodeLimit();
oldD = manager.getDistortion(); oldD = manager.getDistortion();
oldDR = manager.getDistortionRadius();
oldSF = manager.scaleFactor();
simulateZoomOut(); simulateZoomOut();
expect(manager.getFontSize()).toEqual(null);
expect(manager.getRadius()).toBeLessThan(oldR);
expect(manager.getNodeLimit()).not.toBeLessThan(oldNL); expect(manager.getNodeLimit()).not.toBeLessThan(oldNL);
expect(manager.getDistortion()).toBeGreaterThan(oldD); expect(manager.getDistortion()).toBeGreaterThan(oldD);
expect(manager.getDistortionRadius()).toBeGreaterThan(oldDR);
expect(manager.scaleFactor()).toBeLessThan(oldSF);
loopCounter++; loopCounter++;
if (loopCounter === 1000) { if (loopCounter === 1000) {
this.fail(new Error('The minimal font-size should have been reached')); this.fail(new Error('The minimal font-size should have been reached'));
break; break;
} }
} }
expect(manager.getRadius()).toEqual(radMin);
expect(manager.getNodeLimit()).toEqual(nodeMin); expect(manager.getNodeLimit()).toEqual(nodeMin);
//expect(manager.getDistortion()).toBeCloseTo(0, 6); expect(manager.scaleFactor()).toEqual(minScale);
}); });
}); });
@ -328,7 +371,7 @@
beforeEach(function() { beforeEach(function() {
var loopCounter = 0; var loopCounter = 0;
while (manager.getRadius() > radMin) { while (manager.scaleFactor() > minScale) {
simulateZoomOut(); simulateZoomOut();
loopCounter++; loopCounter++;
if (loopCounter === 2000) { if (loopCounter === 2000) {
@ -339,38 +382,39 @@
}); });
it('should not be able to further zoom out', function() { it('should not be able to further zoom out', function() {
var oldR = manager.getRadius(), var oldNL = manager.getNodeLimit(),
oldNL = manager.getNodeLimit(), oldD = manager.getDistortion(),
oldD = manager.getDistortion(); oldDR = manager.getDistortionRadius(),
oldSF = manager.scaleFactor();
simulateZoomOut(); simulateZoomOut();
expect(manager.getRadius()).toEqual(oldR);
expect(manager.getNodeLimit()).toEqual(oldNL); expect(manager.getNodeLimit()).toEqual(oldNL);
expect(manager.getDistortion()).toEqual(oldD); expect(manager.getDistortion()).toEqual(oldD);
expect(manager.getDistortionRadius()).toEqual(oldDR);
expect(manager.scaleFactor()).toEqual(oldSF);
}); });
it('should be able to zoom-in again', function() { it('should be able to zoom-in again', function() {
var oldR, var oldNL,
oldNL,
oldD, oldD,
oldDR,
oldSF,
loopCounter = 0; loopCounter = 0;
while (manager.getFontSize() === null) { while (labelsAreInvisible()) {
oldR = manager.getRadius();
oldNL = manager.getNodeLimit(); oldNL = manager.getNodeLimit();
oldD = manager.getDistortion(); oldD = manager.getDistortion();
oldDR = manager.getDistortionRadius();
oldSF = manager.scaleFactor();
simulateZoomIn(); simulateZoomIn();
expect(manager.getRadius()).toBeGreaterThan(oldR);
expect(manager.getNodeLimit()).not.toBeGreaterThan(oldNL); expect(manager.getNodeLimit()).not.toBeGreaterThan(oldNL);
expect(manager.getDistortion()).toBeLessThan(oldD); expect(manager.getDistortion()).toBeLessThan(oldD);
expect(manager.getDistortionRadius()).toBeLessThan(oldDR);
expect(manager.scaleFactor()).toBeGreaterThan(oldSF);
loopCounter++; loopCounter++;
if (loopCounter === 1000) { if (loopCounter === 1000) {
this.fail(new Error('The minimal font-size should have been reached')); this.fail(new Error('The minimal font-size should have been reached'));
break; break;
} }
} }
expect(manager.getFontSize()).toBeCloseTo(fontMin, 6);
expect(manager.getRadius()).toBeCloseTo((radMax-radMin) / 2 + radMin, 6);
expect(manager.getNodeLimit()).toBeCloseTo(nodeMinLabel, 6);
//expect(manager.getDistortion()).toBeCloseTo(0, 6);
}); });
}); });