|  | <!DOCTYPE html> | 
        
          |  | <html lang="en"> | 
        
          |  | <head> | 
        
          |  | <meta charset="UTF-8"> | 
        
          |  | <title>Examples using matrix transforms in SVG</title> | 
        
          |  | <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script> | 
        
          |  | <style> | 
        
          |  | .square { | 
        
          |  | stroke: black; | 
        
          |  | stroke-width: 1; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | text { | 
        
          |  | font-family: monospace; | 
        
          |  | font-size: 9pt; | 
        
          |  | } | 
        
          |  | line { | 
        
          |  | stroke-width: 1; | 
        
          |  | stroke: blue; | 
        
          |  | } | 
        
          |  | </style> | 
        
          |  | </head> | 
        
          |  | <body> | 
        
          |  | <script> | 
        
          |  | var svg = d3.select("body").append("svg").attr({ | 
        
          |  | class: "planes", | 
        
          |  | width: 900, | 
        
          |  | height: 800, | 
        
          |  | viewBox: "-100 -100 900 800" | 
        
          |  | }); | 
        
          |  |  | 
        
          |  | var colors = d3.scale.category10([3, 3]); | 
        
          |  | var shapeSide = 60; | 
        
          |  |  | 
        
          |  | function makeShape(parent) { | 
        
          |  | var face = parent.append("g") | 
        
          |  | .attr("viewBox", "0 0 "+shapeSide+" "+shapeSide+""); | 
        
          |  | // make a colorful square made up of 9 smaller squares to use as the reference object | 
        
          |  | var side = shapeSide/3; | 
        
          |  | for (var i = 0; i < 3; i++) { | 
        
          |  | for (var j = 0; j < 3; j++) { | 
        
          |  | face.append("rect") | 
        
          |  | .attr("class", "square") | 
        
          |  | .attr({width: side, height: side}) | 
        
          |  | .attr("transform", "translate(" + (j * side) + "," + (i * side) + ")") | 
        
          |  | .style("opacity", .5) | 
        
          |  | .style("fill", colors([j, i])); | 
        
          |  | } | 
        
          |  | }; | 
        
          |  | return face; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | function makePlane(parent) { | 
        
          |  | var plane = parent.append("g") | 
        
          |  | .attr("viewBox", "-100 -100 200 200") | 
        
          |  | plane.append("line").attr({x1: 0, y1: -100, x2: 0, y2: 100}); | 
        
          |  | plane.append("line").attr({x1: -100, y1: 0, x2: 100, y2: 0}); | 
        
          |  | var text = plane.append("text") | 
        
          |  | .attr("x", 10) | 
        
          |  | .attr("y", -100) | 
        
          |  | .attr("class", "label") | 
        
          |  | text.append("tspan") | 
        
          |  | .attr("class","line-1") | 
        
          |  | .text("⎡ 1 0 ⎤") | 
        
          |  | text.append("tspan").attr("dy", 11).attr("x", 10) | 
        
          |  | .attr("class","line-2") | 
        
          |  | .text("⎣ 0 1 ⎦"); | 
        
          |  | text.append("tspan").attr("dy", 20).attr("x", 10) | 
        
          |  | .attr("class","line-3") | 
        
          |  | .text("dx = 0, dy = 0"); | 
        
          |  |  | 
        
          |  | return plane; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // distribute 9 planes on the view, to demonstrate the transformations | 
        
          |  | for (var i = 0; i < 3; i++) { | 
        
          |  | for (var j = 0; j < 3; j++) { | 
        
          |  | var m = 50; | 
        
          |  | var dx = (200 + m) * j + m, dy = (200 + m) * i + m; | 
        
          |  | var plane = makePlane(svg); | 
        
          |  | var shape = makeShape(plane); | 
        
          |  | shape.attr("id", "shape_" + i + "_" + j) | 
        
          |  | .append("circle").attr("r", 2).attr("fill", "red").attr("stroke", "black");; | 
        
          |  | plane.attr("id", "plane_" + i + "_" + j) | 
        
          |  | .attr("transform", "translate("+dx+", "+dy+")"); | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | function label(selector, line1, line2, line3, delay) { | 
        
          |  | var text = d3.select(selector) | 
        
          |  | .selectAll("text"); | 
        
          |  | text.select(".line-1").transition().delay(delay).text(line1); | 
        
          |  | text.select(".line-2").transition().delay(delay).text(line2); | 
        
          |  | text.select(".line-3").transition().delay(delay).text(line3); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Transformation matrix | 
        
          |  | var a = 1, b = 0, c = 0, d = 1, | 
        
          |  | dx = 0, dy = 0; // translation | 
        
          |  | var matrix = [a, b, c, d, dx, dy]; | 
        
          |  |  | 
        
          |  | // 0,0 - no transforms | 
        
          |  | d3.select("#shape_0_0") | 
        
          |  | .attr("transform", "matrix("+matrix+")") | 
        
          |  | label("#plane_0_0", "⎡ 1 0 ⎤", "⎣ 0 1 ⎦", "dx = 0, dy = 0", 0); | 
        
          |  |  | 
        
          |  | d3.select("#plane_0_0") | 
        
          |  | .append("text") | 
        
          |  | .text("square").attr("x", -100).attr("y", 30) | 
        
          |  | .append("tspan") | 
        
          |  | .text("side = 60").attr("x", -100).attr("dy", 15); | 
        
          |  |  | 
        
          |  |  | 
        
          |  | // 0,1 - translate x, then y | 
        
          |  | dx = 25; | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_0_1") | 
        
          |  | .transition() | 
        
          |  | .transition().delay(1000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_0_1", "⎡ 1 0 ⎤", "⎣ 0 1 ⎦", "dx = 25, dy = 0", 700); | 
        
          |  | dy = 50; | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_0_1") | 
        
          |  | .transition().delay(2000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_0_1", "⎡ 1 0 ⎤", "⎣ 0 1 ⎦", "dx = 25, dy = 50", 1700); | 
        
          |  |  | 
        
          |  |  | 
        
          |  |  | 
        
          |  | // 0,2 - translate 10,10 scale x, then both | 
        
          |  | dx = 10, dy = 10, | 
        
          |  | a = .5; | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_0_2") | 
        
          |  | .transition().delay(3000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_0_2", "⎡ 0.5 \u00A0\u00A00 ⎤", "⎣ \u00A0\u00A00 \u00A0\u00A01 ⎦", "dx = 10, dy = 10", 2700); | 
        
          |  | d = .5; | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_0_2") | 
        
          |  | .transition().delay(4000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_0_2", "⎡ 0.5 \u00A0\u00A00 ⎤", "⎣ \u00A0\u00A00 0.5 ⎦", "dx = 10, dy = 10", 3700); | 
        
          |  |  | 
        
          |  | // 1,0 - positive y skew 30 degrees | 
        
          |  | dx = 0, dy = 0; | 
        
          |  | a = 1, b = Math.sin(60 * Math.PI/180), c = 0, d = 1; | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_1_0") | 
        
          |  | .transition().delay(5000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_1_0", "⎡ \u00A0\u00A01.0\u00A0\u00A0\u00A0 \u00A0sin(60) ⎤", "⎣ \u00A0\u00A00.0\u00A0\u00A0\u00A0 \u00A0\u00A0\u00A01.0\u00A0\u00A0 ⎦", "dx = 0, dy = 0", 4700); | 
        
          |  |  | 
        
          |  | // 1,0 - positive rotate 30 degrees - step 1 | 
        
          |  | c = -Math.sin(60 * Math.PI/180), d = 1; | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_1_0") | 
        
          |  | .transition().delay(6000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_1_0", "⎡ \u00A0\u00A01.0\u00A0\u00A0\u00A0 \u00A0sin(60) ⎤", "⎣ -sin(60) \u00A0\u00A01.0\u00A0\u00A0\u00A0 ⎦", "dx = 0, dy = 0", 5700); | 
        
          |  |  | 
        
          |  | // 1,0 - positive rotate 30 degrees - step 2 | 
        
          |  | a = Math.cos(60 * Math.PI/180), d = Math.cos(60 * Math.PI/180); | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_1_0") | 
        
          |  | .transition().delay(7000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_1_0", "⎡  \u00A0cos(60)  \u00A0sin(60) ⎤", "⎣ -sin(60)  \u00A0cos(60) ⎦", "dx = 0, dy = 0", 6700); | 
        
          |  |  | 
        
          |  | // 1,1 - rotate 90 - convert to standard cartesian (flip vertical) | 
        
          |  | a = 1, b = 0, c = 0, d = -1; | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_1_1") | 
        
          |  | .transition().delay(8000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_1_1", "⎡  \u00A01  \u00A00 ⎤", "⎣  \u00A00 -1 ⎦", "dx = 0, dy = 0", 7700); | 
        
          |  |  | 
        
          |  | // 1,1 - rotate 180 - invert position (flip diagonally) | 
        
          |  | a = -1, b = 0, c = 0, d = -1; | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_1_1") | 
        
          |  | .transition().delay(9000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_1_1", "⎡ -1  \u00A00 ⎤", "⎣  \u00A00 -1 ⎦", "dx = 0, dy = 0", 8700); | 
        
          |  |  | 
        
          |  | // 1,1 - rotate 270 - flip horizontal | 
        
          |  | a = -1, b = 0, c = 0, d = 1; | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_1_1") | 
        
          |  | .transition().delay(10000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_1_1", "⎡ -1 \u00A00 ⎤", "⎣ \u00A00 \u00A01 ⎦", "dx = 0, dy = 0", 9700); | 
        
          |  |  | 
        
          |  | // 1,2 - center, then rotate, then rotate again | 
        
          |  | a = 1, b = 0, c = 0, d = 1, dx = -30, dy = -30; | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_1_2") | 
        
          |  | .transition().delay(11000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_1_2", "⎡ 1 0 ⎤", "⎣ 0 1 ⎦", "dx = -30, dy = -30", 10700); | 
        
          |  |  | 
        
          |  | d3.select("#plane_1_2") | 
        
          |  | .append("text").attr("class", "note") | 
        
          |  | .text("center").attr("x", -100).attr("y", 30).attr("opacity", 0) | 
        
          |  | .append("tspan") | 
        
          |  | .text("side/2 = 30").attr("x", -100).attr("dy", 15); | 
        
          |  | d3.select("#plane_1_2 .note").transition().delay(10300).attr("opacity", 1); | 
        
          |  |  | 
        
          |  | a = Math.cos(45 * Math.PI/180), | 
        
          |  | b = -Math.sin(45 * Math.PI/180), | 
        
          |  | c = Math.sin(45 * Math.PI/180), | 
        
          |  | d = Math.cos(45 * Math.PI/180); | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_1_2") | 
        
          |  | .transition().delay(12000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_1_2", "⎡  \u00A0cos(45)  -sin(45) ⎤", "⎣ \u00A0sin(45)  \u00A0cos(60) ⎦", "dx = -side/2, dy = -side/2", 11700); | 
        
          |  |  | 
        
          |  | a = Math.cos(150 * Math.PI/180), | 
        
          |  | b = -Math.sin(150 * Math.PI/180), | 
        
          |  | c = Math.sin(150 * Math.PI/180), | 
        
          |  | d = Math.cos(150 * Math.PI/180); | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_1_2") | 
        
          |  | .transition().delay(13000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_1_2", "⎡  \u00A0cos(150)  -sin(150) ⎤", "⎣ \u00A0sin(150)  \u00A0cos(150) ⎦", "dx = -side/2, dy = -side/2", 12700); | 
        
          |  |  | 
        
          |  | // 2,0 - center, change origin, then rotate, then rotate again | 
        
          |  | a = 1, b = 0, c = 0, d = 1, dx = -shapeSide/2, dy = -shapeSide/2; | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_2_0 circle").remove(); | 
        
          |  | d3.select("#plane_2_0") | 
        
          |  | .append("circle").attr("r", 2).attr("fill", "red").attr("stroke", "black"); | 
        
          |  | d3.select("#shape_2_0") | 
        
          |  | .transition().delay(14000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_2_0", "⎡  1 0 ⎤", "⎣ 0 1 ⎦", "dx = -30, dy = -30", 13700); | 
        
          |  |  | 
        
          |  |  | 
        
          |  | // rotation moving center to origin | 
        
          |  | a = Math.cos(30 * Math.PI/180), | 
        
          |  | b = -Math.sin(30 * Math.PI/180), | 
        
          |  | c = Math.sin(30 * Math.PI/180), | 
        
          |  | d = Math.cos(30 * Math.PI/180); | 
        
          |  | var x = -shapeSide/2, y = -shapeSide/2; | 
        
          |  | dx = (a * x + c * y), | 
        
          |  | dy = (d * x + b * y); | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  |  | 
        
          |  | var format = d3.format(".2f"); | 
        
          |  |  | 
        
          |  | d3.select("#shape_2_0") | 
        
          |  | .transition().delay(15000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_2_0", "⎡ \u00A0cos(30) -sin(30) ⎤", "⎣ \u00A0sin(30) \u00A0cos(30) ⎦", "dx = "+format(dx)+", dy = "+format(dy), 14700); | 
        
          |  | d3.select("#plane_2_0") | 
        
          |  | .append("text").attr("class", "note").attr("opacity", 0) | 
        
          |  | .attr("transform", "translate(-100, 120)") | 
        
          |  | .transition().delay(14300).attr("opacity", 1); | 
        
          |  | d3.select("#plane_2_0 .note").append("tspan").text("dx = side/2 * (cos(30) + sin(30))"); | 
        
          |  | d3.select("#plane_2_0 .note").append("tspan").attr("x", 0).attr("dy", 15).text("dy = side/2 * (cos(30) - sin(30))"); | 
        
          |  |  | 
        
          |  | // now rotate it a bit more | 
        
          |  | a = Math.cos(75 * Math.PI/180), | 
        
          |  | b = -Math.sin(75 * Math.PI/180), | 
        
          |  | c = Math.sin(75 * Math.PI/180), | 
        
          |  | d = Math.cos(75 * Math.PI/180); | 
        
          |  | dx = (a * x + c * y), | 
        
          |  | dy = (d * x + b * y); | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_2_0") | 
        
          |  | .transition().delay(16000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_2_0", "⎡ \u00A0cos(75) -sin(75) ⎤", "⎣ \u00A0sin(75) \u00A0cos(75) ⎦", "dx = "+format(dx)+", dy = "+format(dy), 15700); | 
        
          |  |  | 
        
          |  | d3.select("#plane_2_0 .note tspan:nth-child(1)").transition().delay(15300).text("dx = side/2 * (cos(75) + sin(75))"); | 
        
          |  | d3.select("#plane_2_0 .note tspan:nth-child(2)").transition().delay(15300).attr("x", 0).attr("dy", 15).text("dy = side/2 * (cos(75) - sin(75))"); | 
        
          |  |  | 
        
          |  | // 2,1 - skew up | 
        
          |  | // 2,1 - skew up | 
        
          |  | dx = 0, dy = 0; | 
        
          |  | a = 1, b = 0, c = 0, d = 1; | 
        
          |  | b = Math.sin(30 * Math.PI/180) | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_2_1") | 
        
          |  | .transition().delay(17000) | 
        
          |  | .attr("transform", "matrix("+matrix+")"); | 
        
          |  | label("#plane_2_1", "⎡ \u00A0\u00A01.0\u00A0\u00A0\u00A0 \u00A0sin(30) ⎤", "⎣ \u00A0\u00A00.0\u00A0\u00A0\u00A0 \u00A0\u00A0\u00A01.0\u00A0\u00A0 ⎦", "dx = 0, dy = 0", 16700); | 
        
          |  |  | 
        
          |  |  | 
        
          |  | // 2,2 - skew right | 
        
          |  | dx = 0, dy = 0; | 
        
          |  | a = -1, b = 0, c = 0, d = Math.sin(30 * Math.PI/180); | 
        
          |  | b = Math.sin(30 * Math.PI/180), | 
        
          |  | c = 1; | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_2_2") | 
        
          |  | .transition().delay(18000) | 
        
          |  | .attr("transform", "matrix("+matrix+")") | 
        
          |  | .select("circle").remove(); | 
        
          |  | label("#plane_2_1", "⎡ \u00A0-1.0\u00A0\u00A0\u00A0 \u00A0sin(30) ⎤", "⎣ \u00A0\u00A01.0\u00A0\u00A0\u00A0 \u00A0sin(30) ⎦", "dx = 0, dy = 0", 16700); | 
        
          |  |  | 
        
          |  | // 2,2 - make cube - move and skew shapes 0-0 and 2-1 to the 2-2 plane | 
        
          |  | dx = 500, dy = 500 + 60 * 2 * Math.sin(30 * Math.PI/180); | 
        
          |  | a = 1, b = 0, c = 0, d = 1; | 
        
          |  | b = -Math.sin(30 * Math.PI/180); | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_0_0") | 
        
          |  | .transition().delay(18500) | 
        
          |  | .attr("transform", "matrix("+matrix+")") | 
        
          |  | .select("circle").remove(); | 
        
          |  |  | 
        
          |  | dx = 250 - 60 * 2 * Math.sin(30 * Math.PI/180), dy = 30 * 2 * Math.sin(30 * Math.PI/180); | 
        
          |  | a = 1, b = 0, c = 0, d = 1; | 
        
          |  | b = Math.sin(30 * Math.PI/180) | 
        
          |  | matrix = [a, b, c, d, dx, dy]; | 
        
          |  | d3.select("#shape_2_1") | 
        
          |  | .transition().delay(19000) | 
        
          |  | .attr("transform", "matrix("+matrix+")") | 
        
          |  | .select("circle").remove(); | 
        
          |  |  | 
        
          |  |  | 
        
          |  | </script> | 
        
          |  |  | 
        
          |  | </body> | 
        
          |  | </html> |