Skip to content

Commit

Permalink
More work on visualisation. Added initial implementation of zoom
Browse files Browse the repository at this point in the history
  • Loading branch information
atruskie committed Feb 6, 2015
1 parent 7dda4bb commit 5c18802
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 73 deletions.
98 changes: 95 additions & 3 deletions src/app/d3Bindings/eventDistribution/distributionDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ angular
xAxis,
xScale,
yScale,
zoom,
// 6 hours
zoomLimitSeconds = 6 * 60 * 60,
laneLinesGroup,
laneLabelsGroup,
mainItemsGroup,
Expand All @@ -39,7 +42,7 @@ angular
mainWidth = 1000,
mainHeight = 256,
laneHeight = 120,
lanePaddingDomain = 0.125;
lanePaddingDomain = 0.1;

// exports
this.updateData = updateData;
Expand Down Expand Up @@ -117,6 +120,11 @@ angular
});

chart.style("height", svgHeight() + "px");

if (zoom) {
zoom.size([mainWidth, mainHeight]);
updateZoom();
}
}

function createMain() {
Expand All @@ -127,6 +135,15 @@ angular
.classed("main", true)
.translate([margin.left, margin.top]);

// zoom behaviour
zoom = d3.behavior.zoom()
//.scaleExtent([that.minimum, that.maximum])
.size([mainWidth, mainHeight])
.on("zoomstart", onZoomStart)
.on("zoom", onZoom)
.on("zoomend", onZoomEnd);
zoom(main);

// group for separator lines between lanes/categories
laneLinesGroup = main.append("g").classed("laneLinesGroup", true);

Expand All @@ -150,10 +167,17 @@ angular
}

function updateScales() {
that.visibleExtent = that.visibleExtent || [that.minimum, that.maximum];

xScale = d3.time.scale()
.domain(that.visibleExtent || [that.minimum, that.maximum])
.domain(that.visibleExtent)
.range([0, mainWidth]);

// update the zoom behaviour
zoom.x(xScale);
zoom.scaleExtent(getZoomFactors([that.minimum, that.maximum], that.visibleExtent, zoomLimitSeconds));
updateZoom();

yScale = d3.scale.linear()
.domain([0, getLaneLength()])
.range([0, mainHeight]);
Expand Down Expand Up @@ -255,6 +279,75 @@ angular

}

function onZoomStart() {

console.debug("DistributionDetail:zoomStart:",d3.event.translate, d3.event.scale);
}

function onZoom() {
// the xScale is automatically updated
// now just rerender everything


// prevent translating off the edge of our data (i.e. clamp the zoom)
var domain = null;
if (xScale) {
var t = zoom.translate(),
tx = t[0],
ty = t[1];
//if (t[0] < xScale(that.minimum)) {
// zoom.translate([xScale(that.minimum), t[1] - t[0]]);
//}
//
//if (t[1] > xScale(that.maximum)) {
// zoom.translate([t[1] - t[0], xScale(that.maximum)]);
//}

//tx = Math.min(tx, 0);
//tx = Math.max(tx, mainWidth - xScale.range()[1]);
//zoom.translate([tx, ty]);

domain = xScale.domain();
}

console.debug("DistributionDetail:zoom:", d3.event.translate, d3.event.scale, domain);

// updates the public visible extent field
that.visibleExtent = domain;

// redraw elements and axes
extentUpdateMain();
}

function onZoomEnd() {
console.debug("DistributionDetail:zoomEnd:", d3.event.translate, d3.event.scale);
}

function updateZoom() {
//zoom.event(main);
}

function getZoomFactors(fullExtent, visibleExtent, limitSeconds) {
var fullDifference = (+fullExtent[1]) - (+fullExtent[0]),
visibleDifference = (+visibleExtent[1]) - (+visibleExtent[0]);
var limit = limitSeconds * 1000;

/*
[0, 1] adjusts zoom to be wider than visible extent (zoom out)
(1, 1) is zoomScale where zoom == visibleExtent
[1, ∞] adjusts zoom to be narrower than visible extent (zoom in)
after zoom changes, the visible extent also changes
*/

var scaleLower = 1 / (fullDifference / visibleDifference),
scaleUpper = visibleDifference / limit;

console.debug("DistributionDetail:getZoomFactors:", scaleLower, scaleUpper);

return [scaleLower, scaleUpper];
}

function isRectVisible(d) {
return dataFunctions.getLow(d) < that.visibleExtent[1] &&
dataFunctions.getHigh(d) > that.visibleExtent[0];
Expand Down Expand Up @@ -292,7 +385,6 @@ angular
restrict: "EA",
scope: false,
require: "^^eventDistribution",
controller: "distributionController",
link: function ($scope, $element, attributes, controller, transcludeFunction) {
var element = $element[0];
controller.detail = new DistributionDetail(
Expand Down
5 changes: 2 additions & 3 deletions src/app/d3Bindings/eventDistribution/distributionOverview.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ angular
bottom: 5 + xAxisHeight,
left: 120
},
laneHeight = 60,
lanePaddingDomain = 0.125,
laneHeight = 30,
lanePaddingDomain = 0.1,
labelRectPadding = 5,
container = d3.select(target);

Expand Down Expand Up @@ -306,7 +306,6 @@ angular
return {
restrict: "EA",
scope: false,
controller: "distributionController",
require: "^^eventDistribution",
link: function ($scope, $element, attributes, controller, transcludeFunction) {
var element = $element[0];
Expand Down
89 changes: 54 additions & 35 deletions src/app/d3Bindings/eventDistribution/distributionVisualisation.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ angular
yScale,

visibleExtent = [],
generatedTiles = [];
visibleTiles = [];

// exports
that.items = [];
Expand All @@ -63,6 +63,8 @@ angular

generateTiles();

filterTiles();

updateElements();

}
Expand All @@ -73,7 +75,7 @@ angular

updateScales();

generateTiles();
filterTiles();

updateElements();
}
Expand Down Expand Up @@ -130,19 +132,31 @@ angular
function generateTiles() {
if (that.items && that.items.length > 0) {
// need to generate a series of tiles that can show the data in that.items
var f = isItemVisible.bind(null, visibleExtent),
g = isInCategory.bind(null, that.category),
h = and.bind(null, g, f);

var filteredItems = that.items.filter(h);

generatedTiles = filteredItems.reduce(splitIntoTiles, []);
}
else {
generatedTiles = [];
that.items.forEach(function(current) {
// warning: to future self: this is creating cyclic references
// as each tile keeps a reference to current
current.tiles = splitIntoTiles(current);
});
}
}

function filterTiles() {
// item filter
var f = isItemVisible.bind(null, visibleExtent),
g = isInCategory.bind(null, that.category),
h = and.bind(null, g, f);

// tile filter
var l = isTileVisible.bind(null, visibleExtent);

visibleTiles = that.items
.filter(h)
.reduce(function (previous, current) {
var t = current.tiles.filter(l);
return previous.concat(t);
}, []);
}

function createElements() {
// this example has an associated html template...
// most of the creation is not necessary
Expand All @@ -151,31 +165,28 @@ angular
}

function updateElements() {
function left(d, i) {
return xScale(d.offset) + "px";
}

var style = {
top: function(d, i) {
return i + "px";
},
left: function (d, i) {
return xScale(d.offset) + "px";
},
top: 0,
left: left,
width: tileSizePixels + "px"
};

function getOffset(d) {
return d.offset.toISOString();
}
function getKey(d) {
return d.offset.toISOString() + dataFunctions.getCategory(d.source);
}


// update old tiles
var tileElements = tiles.selectAll(".tile")
.data(generatedTiles, getKey);
.data(visibleTiles, function (d) {
return d.key;
});

tileElements.style(style)
.classed("tile", true)
.append("div")
.text(getOffset);
// update old tiles
tileElements.style("left", left);

// add new tiles
tileElements.enter()
Expand Down Expand Up @@ -212,14 +223,20 @@ angular

function isItemVisible(visibleExtent, d) {
return dataFunctions.getLow(d) < visibleExtent[1] &&
dataFunctions.getHigh(d) > visibleExtent[0];
dataFunctions.getHigh(d) >= visibleExtent[0];
}

function and(a, b, d) {
return a(d) && b(d);
}

function splitIntoTiles(previous, current, i) {
function isTileVisible(visibleExtent, d) {
return d &&
d.offset < visibleExtent[1] &&
d.offsetEnd >= visibleExtent[0];
}

function splitIntoTiles(current, i) {
// coerce just in case (d3 does this internally)
var low = new Date(dataFunctions.getLow(current)),
high = new Date(dataFunctions.getHigh(current));
Expand All @@ -232,14 +249,17 @@ angular
// use d3's in built range functionality to generate steps
var steps = [];
while (offset <= niceHigh) {
var nextOffset = d3.time.second.offset(offset, tileSizeSeconds);
steps.push({
offset: offset,
source: current
});
offset = d3.time.second.offset(offset, tileSizeSeconds);
offset: offset,
offsetEnd: nextOffset,
source: current,
key: offset.toISOString() + dataFunctions.getId(current)
});
offset = nextOffset;
}

return previous.concat(steps);
return steps;
}


Expand All @@ -256,7 +276,6 @@ angular
return {
restrict: "EA",
scope: false,
controller: "distributionController",
require: "^^eventDistribution",
templateUrl: paths.site.files.d3Bindings.eventDistribution.distributionVisualisation,
link: function ($scope, $element, attributes, controller, transcludeFunction) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
meta data tracks
</div>
<div class="imageTrack">
false color spectrogram
<div class="tiles">
<!-- there should be at most 25 tiles -->
<div></div>
Expand All @@ -18,4 +17,7 @@
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<svg height="256px" width="60px" >
<image xlink:href="assets/temp/tiles/tile_0.png" x="0" y="0" height="256px" width="60px" />
</svg>
</div>
Loading

0 comments on commit 5c18802

Please sign in to comment.