|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
|
|
body { |
|
position: relative; |
|
width: 960px; |
|
height: 500px; |
|
} |
|
|
|
#background { |
|
width: 960px; |
|
height: 500px; |
|
background: url(mosque.jpg); |
|
} |
|
|
|
svg { |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
} |
|
|
|
.line { |
|
stroke: #000; |
|
stroke-opacity: .5; |
|
stroke-width: 1px; |
|
stroke-linecap: square; |
|
} |
|
|
|
.handle { |
|
fill: none; |
|
pointer-events: all; |
|
stroke: #fff; |
|
stroke-width: 2px; |
|
cursor: move; |
|
} |
|
|
|
#buttons { |
|
position: absolute; |
|
top: 20px; |
|
left: 20px; |
|
z-index: 2; |
|
} |
|
|
|
button { |
|
display: block; |
|
width: 10em; |
|
} |
|
|
|
button:focus { |
|
outline: none; |
|
} |
|
|
|
</style> |
|
<div id="background"></div> |
|
<div id="buttons"> |
|
<button data-targets="[[492,329],[542,330],[569,434],[424,424]]">Floor</button> |
|
<button data-targets="[[-28,287],[74,288],[72,413],[-31,404]]">Near Wall</button> |
|
<button data-targets="[[-194,282],[-95,282],[-100,354],[-200,365]]">Far Wall</button> |
|
<button data-targets="[[0,0],[400,0],[400,400],[0,400]]">Reset</button> |
|
</div> |
|
<script src="//d3js.org/d3.v3.min.js"></script> |
|
<script src="numeric-solve.min.js"></script> |
|
<script> |
|
|
|
var margin = {top: 50, right: 280, bottom: 50, left: 280}, |
|
width = 960 - margin.left - margin.right, |
|
height = 500 - margin.top - margin.bottom; |
|
|
|
var transform = ["", "-webkit-", "-moz-", "-ms-", "-o-"].reduce(function(p, v) { return v + "transform" in document.body.style ? v : p; }) + "transform"; |
|
|
|
var sourcePoints = [[0, 0], [width, 0], [width, height], [0, height]], |
|
targetPoints = [[0, 0], [width, 0], [width, height], [0, height]]; |
|
|
|
d3.select("body").selectAll("svg") |
|
.data(["transform", "flat"]) |
|
.enter().append("svg") |
|
.attr("id", function(d) { return d; }) |
|
.attr("width", width + margin.left + margin.right) |
|
.attr("height", height + margin.top + margin.bottom) |
|
.append("g") |
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); |
|
|
|
var svgTransform = d3.select("#transform") |
|
.style(transform + "-origin", margin.left + "px " + margin.top + "px 0"); |
|
|
|
var svgFlat = d3.select("#flat"); |
|
|
|
svgTransform.select("g").append("image") |
|
.attr("xlink:href", "sailboat.png") |
|
.attr("width", width) |
|
.attr("height", height); |
|
|
|
svgTransform.select("g").selectAll(".line--x") |
|
.data(d3.range(0, width + 1, 40)) |
|
.enter().append("line") |
|
.attr("class", "line line--x") |
|
.attr("x1", function(d) { return d; }) |
|
.attr("x2", function(d) { return d; }) |
|
.attr("y1", 0) |
|
.attr("y2", height); |
|
|
|
svgTransform.select("g").selectAll(".line--y") |
|
.data(d3.range(0, height + 1, 40)) |
|
.enter().append("line") |
|
.attr("class", "line line--y") |
|
.attr("x1", 0) |
|
.attr("x2", width) |
|
.attr("y1", function(d) { return d; }) |
|
.attr("y2", function(d) { return d; }); |
|
|
|
var handle = svgFlat.select("g").selectAll(".handle") |
|
.data(targetPoints) |
|
.enter().append("circle") |
|
.attr("class", "handle") |
|
.attr("transform", function(d) { return "translate(" + d + ")"; }) |
|
.attr("r", 7) |
|
.call(d3.behavior.drag() |
|
.origin(function(d) { return {x: d[0], y: d[1]}; }) |
|
.on("drag", dragged)); |
|
|
|
d3.selectAll("button") |
|
.datum(function(d) { return JSON.parse(this.getAttribute("data-targets")); }) |
|
.on("click", clicked) |
|
.call(transformed); |
|
|
|
function clicked(d) { |
|
d3.transition() |
|
.duration(750) |
|
.tween("points", function() { |
|
var i = d3.interpolate(targetPoints, d); |
|
return function(t) { |
|
handle.data(targetPoints = i(t)).attr("transform", function(d) { return "translate(" + d + ")"; }); |
|
transformed(); |
|
}; |
|
}); |
|
} |
|
|
|
function dragged(d) { |
|
d3.select(this).attr("transform", "translate(" + (d[0] = d3.event.x) + "," + (d[1] = d3.event.y) + ")"); |
|
transformed(); |
|
} |
|
|
|
function transformed() { |
|
for (var a = [], b = [], i = 0, n = sourcePoints.length; i < n; ++i) { |
|
var s = sourcePoints[i], t = targetPoints[i]; |
|
a.push([s[0], s[1], 1, 0, 0, 0, -s[0] * t[0], -s[1] * t[0]]), b.push(t[0]); |
|
a.push([0, 0, 0, s[0], s[1], 1, -s[0] * t[1], -s[1] * t[1]]), b.push(t[1]); |
|
} |
|
|
|
var X = solve(a, b, true), matrix = [ |
|
X[0], X[3], 0, X[6], |
|
X[1], X[4], 0, X[7], |
|
0, 0, 1, 0, |
|
X[2], X[5], 0, 1 |
|
].map(function(x) { |
|
return d3.round(x, 6); |
|
}); |
|
|
|
svgTransform.style(transform, "matrix3d(" + matrix + ")"); |
|
} |
|
|
|
</script> |