Spermatozoa is an example where two path elements and an elipse element form a tadpole and move in unison. In this example, there are a total of a hundred tadpoles and the challenge is to create layers for the selected tadpoles. Because they are constantly moving without a specific direction, the layers need to be updated continuously to stay on top of the original elements. The VisDock toolkit has been integrated into the Spermatozoa example built with D3.js (found here) by M. Bostock. We can improve this example by allowing users to select the entire tadpoles rather than just the tails. For more information about VisDock, please cick on the link.
xxxxxxxxxx
<meta charset="utf-8">
<style>
ellipse {
fill: #000;
}
path {
fill: none;
stroke: black;
stroke-linecap: round;
}
.mid {
stroke-width: 4px;
}
.tail {
stroke-width: 2px;
}
</style>
<body>
<link href="https://rawgithub.com/VisDockHub/NewVisDock/master/master/visdock.css" rel="stylesheet" type="text/css"/>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://rawgithub.com/visdockhub/newvisdock/master/master/visdock.js"></script>
<script src="https://rawgithub.com/visdockhub/newvisdock/master/master/2d.js"></script>
<script src="https://rawgithub.com/visdockhub/newvisdock/master/master/intersectionutilities.js"></script>
<script src="https://rawgithub.com/visdockhub/newvisdock/master/master/visdock.utils.js"></script>
<script>
var width = 960,
height = 500;
VisDock.init("body", {width: 1000, height: 700});
var viewport = VisDock.getViewport();
AnnotatedByData.layerTypes = ["ellipse"];
var n = 100,
m = 12,
degrees = 180 / Math.PI;
var spermatozoa = d3.range(n).map(function() {
var x = Math.random() * width,
y = Math.random() * height;
return {
vx: Math.random() * 2 - 1,
vy: Math.random() * 2 - 1,
path: d3.range(m).map(function() { return [x, y]; }),
count: 0
};
});
var svg = viewport;
var g = svg.selectAll("g")
.data(spermatozoa)
.enter().append("g");
var head = g.append("ellipse")
.attr("rx", 6.5)
.attr("ry", 4);
g.append("path")
.datum(function(d) { return d.path.slice(0, 3); })
.attr("class", "mid");
var id = 0;
g.append("path")
.datum(function(d) { return d.path; })
.attr("class", "tail")
.attr("id", function(){
return id++;
});
var tail = g.selectAll("path");
d3.timer(function() {
VisDock.startChrome();
for (var i = -1; ++i < n;) {
var spermatozoon = spermatozoa[i],
path = spermatozoon.path,
dx = spermatozoon.vx,
dy = spermatozoon.vy,
x = path[0][0] += dx,
y = path[0][1] += dy,
speed = Math.sqrt(dx * dx + dy * dy),
count = speed * 10,
k1 = -5 - speed / 3;
// Bounce off the walls.
if (x < 0 || x > width) spermatozoon.vx *= -1;
if (y < 0 || y > height) spermatozoon.vy *= -1;
// Swim!
for (var j = 0; ++j < m;) {
var vx = x - path[j][0],
vy = y - path[j][1],
k2 = Math.sin(((spermatozoon.count += count) + j * 3) / 300) / speed;
path[j][0] = (x += dx / speed * k1) - dy * k2;
path[j][1] = (y += dy / speed * k1) + dx * k2;
speed = Math.sqrt((dx = vx) * dx + (dy = vy) * dy);
}
}
head.attr("transform", headTransform);
tail.attr("d", tailPath);
VisDock.updateLayers();
AnnotatedByData.update();
VisDock.finishChrome();
});
function headTransform(d) {
return "translate(" + d.path[0] + ")rotate(" + Math.atan2(d.vy, d.vx) * degrees + ")";
}
function tailPath(d) {
return "M" + d.join("L");
}
VisDock.eventHandler = {
getHitsPolygon : function(points, inclusive) {
var shapebound = new createPolygon(points);
return shapebound.intersectPath(d3.selectAll(".tail")[0], inclusive)
},
getHitsLine : function(points, inclusive) {
var shapebound = new createLine(points);
return shapebound.intersectPath(d3.selectAll(".tail")[0], inclusive)
},
getHitsEllipse : function(points, inclusive) {
var shapebound = new createEllipse(points);
return shapebound.intersectPath(d3.selectAll(".tail")[0], inclusive)
},
setColor : function(hits) {
for (var i = 0; i < hits.length; i++) {
VisDock.utils.addPathLayer(hits[i], "fill: none; stroke: " + VisDock.color[num - 1] + "; opacity: 1; pointer-events: none", num - 1);
}
},
changeColor : function(color, query, index) {
for (var i = 0; i < query.length; i++) {
var vis = query[i].attr("style").split("opacity: ")[1].split(";")[0]
query[i][0][0].setAttributeNS(null, "style", "stroke: " + color + "; fill:none; opacity: " + vis)
}
},
changeVisibility : function(vis, query, index) {
for (var i = 0; i < query.length; i++) {
var color = query[i].attr("style").split("stroke: ")[1].split(";")[0]
query[i][0][0].setAttributeNS(null, "style", "fill: " + color + "; opacity: " + vis)
}
},
removeColor : function(hits, index) {
for (var i = 0; i < hits.length; i++) {
hits[i].remove();
}
},
QueryClick : function(query, index) {
}
}
BirdView.init(viewport, 1000, 700)
d3.select(self.frameElement).style("width", "1000px")
d3.select(self.frameElement).style("height", "700px")
</script>
Modified http://d3js.org/d3.v3.min.js to a secure url
Modified http://rawgithub.com/VisDockHub/NewVisDock/master/master/visdock.js to a secure url
Modified http://rawgithub.com/VisDockHub/NewVisDock/master/master/2D.js to a secure url
Modified http://rawgithub.com/VisDockHub/NewVisDock/master/master/IntersectionUtilities.js to a secure url
Modified http://rawgithub.com/VisDockHub/NewVisDock/master/master/visdock.utils.js to a secure url
https://d3js.org/d3.v3.min.js
https://rawgithub.com/VisDockHub/NewVisDock/master/master/visdock.js
https://rawgithub.com/VisDockHub/NewVisDock/master/master/2D.js
https://rawgithub.com/VisDockHub/NewVisDock/master/master/IntersectionUtilities.js
https://rawgithub.com/VisDockHub/NewVisDock/master/master/visdock.utils.js