|
<html xmlns="http://www.w3.org/1999/xhtml"> |
|
<head> |
|
<title>D3 Visualizing Rotation</title> |
|
<meta charset="utf-8" /> |
|
</head> |
|
<script src="http://d3js.org/d3.v3.min.js"></script> |
|
<script src="http://d3js.org/colorbrewer.v1.min.js"></script> |
|
<style> |
|
body, html { |
|
width:100%; |
|
height:100%; |
|
} |
|
#vizcontainer { |
|
width:100%; |
|
height:100%; |
|
} |
|
svg { |
|
width: 100%; |
|
height: 100%; |
|
} |
|
</style> |
|
<body onload="touchDemo()"> |
|
|
|
<div id="vizcontainer"> |
|
<svg></svg> |
|
</div> |
|
<footer> |
|
<script> |
|
function touchDemo() { |
|
var initialD = []; |
|
var initialRotate; |
|
|
|
d3.select("svg").on("touchstart", touchBegin); |
|
d3.select("svg").on("touchmove", touchStatus); |
|
|
|
var touchColor = d3.scale.linear().domain([0, 10]).range(["pink", "darkred"]) |
|
|
|
var graphicsG = d3.select("svg").append("g").attr("id", "graphics").attr("transform", "translate(250,250)"); |
|
|
|
graphicsG.append("rect").attr("width", 250).attr("height", 50).attr("x", 50).attr("y", 50) |
|
.style("fill", "lightgray").style("stroke", "gray").style("stroke-width", "1px"); |
|
|
|
graphicsG.append("rect").attr("width", 100).attr("height", 400).attr("x", 350).attr("cy", 400) |
|
.style("fill", "lightgray").style("stroke", "gray").style("stroke-width", "1px"); |
|
|
|
graphicsG.append("rect").attr("width", 400).attr("height", 50).attr("x", 50).attr("cy", 400) |
|
.style("fill", "lightgray").style("stroke", "gray").style("stroke-width", "1px"); |
|
|
|
function touchBegin() { |
|
d3.event.preventDefault(); |
|
d3.event.stopPropagation(); |
|
initialD = d; |
|
initialRotate = d3.transform(d3.select("#graphics").attr("transform")).rotate; |
|
|
|
d = d3.touches(this); |
|
if (d.length == 3) { |
|
d3.select("svg").selectAll("line").remove(); |
|
d3.select("svg").selectAll("text").remove(); |
|
d3.select("svg").append("line") |
|
.attr("class", "initial") |
|
.style("stroke", function(d,i) {return i == 0 ? "red" : "black"}) |
|
.style("stroke-width", function(d,i) {return i == 0 ? "20px" : "5px"}) |
|
|
|
} |
|
|
|
} |
|
function touchStatus() { |
|
d3.event.preventDefault(); |
|
d3.event.stopPropagation(); |
|
d = d3.touches(this); |
|
d3.select("svg").selectAll("circle").data(d).enter().append("circle").attr("r", 75).style("fill", function(d, i) { |
|
return touchColor(i) |
|
}); |
|
|
|
d3.select("svg").selectAll("circle").data(d).exit().remove(); |
|
|
|
d3.select("svg").selectAll("circle").attr("cx", function(d) { |
|
return d[0] |
|
}).attr("cy", function(d) { |
|
return d[1] |
|
}); |
|
|
|
var l = []; |
|
if (d.length > 1) { |
|
for (x in d) { |
|
for (y in d) { |
|
if (y != x) { |
|
var lineObj = { |
|
source: d[x], |
|
target: d[y] |
|
}; |
|
l.push(lineObj); |
|
} |
|
} |
|
} |
|
d3.select("svg").selectAll("line.status").data(l).enter().append("line").attr("class", "status") |
|
.style("stroke", function(d,i) {return i == 0 ? "red" : "black"}) |
|
.style("stroke-width", function(d,i) {return i == 0 ? "20px" : "5px"}); |
|
|
|
d3.select("svg").selectAll("line.status").attr("x1", function(d) { |
|
return d.source[0] |
|
}).attr("y1", function(d) { |
|
return d.source[1] |
|
}).attr("x2", function(d) { |
|
return d.target[0] |
|
}).attr("y2", function(d) { |
|
return d.target[1] |
|
}) |
|
|
|
} |
|
d3.select("svg").selectAll("line.status").data(l).exit().remove(); |
|
|
|
if (d.length == 3) { |
|
var slope1 = (initialD[0][1] - initialD[1][1]) / (initialD[0][0] - initialD[1][0]); |
|
var slope2 = (d[0][1] - d[1][1]) / (d[0][0] - d[1][0]); |
|
var lineAdjust = [d[0][0] - (initialD[0][0] - initialD[1][0]), d[0][1] - (initialD[0][1] - initialD[1][1])] |
|
|
|
d3.select("text.newslope").remove(); |
|
d3.select("text.firstslope").remove(); |
|
|
|
d3.select("svg").append("text") |
|
.attr("x", (d[0][0] + lineAdjust[0]) / 2) |
|
.attr("y", (d[0][1] + lineAdjust[1]) / 2) |
|
.attr("class", "firstslope") |
|
.style("color", "darkred") |
|
.style("font-weight", 900) |
|
.text("Initial Slope: " + d3.format(".2f")(slope1)); |
|
|
|
|
|
d3.select("line.initial") |
|
.attr("x1", d[0][0]) |
|
.attr("x2", lineAdjust[0]) |
|
.attr("y1", d[0][1]) |
|
.attr("y2", lineAdjust[1]) |
|
|
|
d3.select("svg").append("text") |
|
.attr("x", (d[0][0] + d[1][0]) / 2) |
|
.attr("y", (d[0][1] + d[1][1]) / 2) |
|
.attr("class", "newslope") |
|
.style("color", "darkred") |
|
.style("font-weight", 900) |
|
.text("Changed Slope: " + d3.format(".2f")(slope2)); |
|
|
|
//One way to calculate the angle |
|
var angle = Math.atan((slope1 - slope2)/(1 + slope1*slope2)) * 180/Math.PI; |
|
|
|
//Another way to calculate the angle, which also provides the necessary angles for a d3.svg.angle |
|
|
|
var arcAngle1 = Math.atan2(d[0][1] - lineAdjust[1],d[0][0] - lineAdjust[0]); |
|
var arcAngle2 = Math.atan2((d[0][1] - d[1][1]),(d[0][0] - d[1][0])); |
|
|
|
var newArc = d3.svg.arc().innerRadius(100).outerRadius(200).startAngle(arcAngle1 - (Math.PI / 2)).endAngle(arcAngle2 - (Math.PI / 2)); |
|
|
|
var altAngle = (arcAngle2 - arcAngle1) * (180/Math.PI) |
|
|
|
d3.select("svg").selectAll("g.arc").remove(); |
|
d3.select("svg").insert("g","g").attr("class","arc").attr("transform", "translate("+d[0][0]+","+d[0][1]+")").append("path").attr("class", "arc").attr("d", newArc).style("fill", "#B2B2CC") |
|
|
|
d3.select("g.arc").append("text").text(d3.format(".2f")(altAngle) + " degrees").attr("transform", "translate(" +newArc.centroid()[0] +","+ newArc.centroid()[1]+")").style("font-size", "24px").style("font-weight", 900) |
|
|
|
var newRotate = initialRotate - altAngle; |
|
|
|
d3.select("#graphics").attr("transform", "translate(250,250) rotate(" +newRotate +")") |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
</script> |
|
</footer> |
|
</body> |
|
</html> |