[从头学数学] 第152节 旋转
Posted mwsister
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[从头学数学] 第152节 旋转相关的知识,希望对你有一定的参考价值。
剧情提要:[机器小伟]在[工程师阿伟]的陪同下进入了筑基后期的修炼,
这次要修炼的目标是[旋转]。
正剧开始:
星历2016年03月26日 16:11:34, 银河系厄尔斯星球中华帝国江南行省。
[工程师阿伟]正在和[机器小伟]一起研究[旋转]。
<span style="font-size:18px;">//旋转 var r = 20; var r0 = 5*r; config.setSector(1,1,1,1); config.graphpaper2D(0, 0, r); config.axis2D(0, 0, 180, 1); var a = shape.nEdge(100, 100, r0, 3, 0); var transform = new Transform(); var b = transform.rotate(a, Math.PI); shape.strokeDraw(a, 'red'); shape.strokeDraw(b, 'blue'); </span>
<span style="font-size:18px;">//三角形的旋转 var r = 20; var r0 = 5*r; config.setSector(1,1,1,1); config.graphPaper2D(0, 0, r); config.axis2D(0, 0, 180, 1); var triangle = new Triangle(); var transform = new Transform(); var a = transform.translate(triangle.know2edges([100, 50]), -100, 0); var b = transform.rotate(a, Math.PI/2); shape.strokeDraw(a, 'red'); shape.strokeDraw(b, 'blue');</span>
<span style="font-size:18px;"> //五角星旋转72度后重合 var r = 20; var r0 = 5*r; config.setSector(1,1,1,1); config.graphPaper2D(0, 0, r); config.setSector(1,2,1,1); config.axis2D(0, 0, 180, 1); var triangle = new Triangle(); var transform = new Transform(); var a = shape.nStar(0, 0, r0, 5, -Math.PI/2); shape.strokeDraw([].concat(a), 'red'); config.setSector(1,2,1,2); config.axis2D(0, 0, 180, 1); var b = transform.rotate(a, Math.PI*0.4); shape.strokeDraw([].concat(b), 'blue'); //正三角形旋转120度后重合 var r = 20; var r0 = 5*r; config.setSector(1,1,1,1); config.graphPaper2D(0, 0, r); config.setSector(1,2,1,1); config.axis2D(0, 0, 180, 1); var triangle = new Triangle(); var transform = new Transform(); var a = shape.nEdge(0, 0, r0, 3, Math.PI); shape.strokeDraw([].concat(a), 'red'); config.setSector(1,2,1,2); config.axis2D(0, 0, 180, 1); var b = transform.rotate(a, Math.PI/3*2); shape.strokeDraw([].concat(b), 'blue');</span>
<span style="font-size:18px;"> //三角形的中心对称 var r = 20; var r0 = 5*r; config.setSector(1,1,1,1); config.graphPaper2D(0, 0, r); config.axis2D(0, 0, 180); var triangle = new Triangle(); var transform = new Transform(); var a = transform.scale(triangle.know3edges([4, 5, 6]), 30, 30); shape.strokeDraw([].concat(a), 'red'); var b = transform.rotate(a, Math.PI); shape.strokeDraw([].concat(b), 'blue');</span>
<span style="font-size:18px;"> //点的中心对称 var r = 20; var r0 = 5*r; config.setSector(1,1,1,1); config.graphPaper2D(0, 0, r); config.axis2D(0, 0, 180); var triangle = new Triangle(); var transform = new Transform(); var points = [[4,0],[0,-3],[2,1],[-1,2],[-3,-4]]; var a = transform.scale(points, r, r); shape.pointDraw([].concat(a), 'red'); var b = transform.flipXY(a); shape.pointDraw([].concat(b), 'blue');</span>
<span style="font-size:18px;"> //例2 三角形的中心对称 var r = 50; var r0 = 5*r; config.setSector(1,1,1,1); config.graphPaper2D(0, 0, r); config.axis2D(0, 0, 180); var triangle = new Triangle(); var transform = new Transform(); var points = [[-4,1],[-1,-1],[-3,2]]; var a = transform.scale(points, r, r); shape.pointDraw([].concat(a), 'red'); shape.strokeDraw([].concat(a), 'pink'); var b = transform.flipXY(a); shape.pointDraw([].concat(b), 'blue'); shape.strokeDraw([].concat(b), '#0033AA');</span>
本节用到的工具:
/** * @usage 常用形状类 * @author mw * @date 2015年11月29日 星期日 10:21:18 * @param * @return * */ var shape = function Shape() { //以给定点为中点的矩形 this.strokeRect = function(x, y, w, h) { w = Math.abs(w); h = Math.abs(h); return plot.strokeRect(x-w/2, y-h/2, w, h); } //以给定点为中点的矩形 this.fillRect = function(x, y, w, h) { w = Math.abs(w); h = Math.abs(h); return plot.fillRect(x-w/2, y-h/2, w, h); } /** * @usage 绘制点阵列 * @author mw * @date 2016年02月21日 星期日 15:16:47 * @param * @return * */ this.pointDraw = function(array, style, scale, lable, showLable) { //已经考虑到y轴坐标的取反问题,只需传入原始坐标数组即可 style = style ? style : 'black'; scale = scale ? scale : 1; lable = lable ? lable : 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; showLable = showLable ? showLable : 1; var x = y = index = 0; plot.save() .setFillStyle(style); var a = new Array(); a = array[0]; //y坐标取反是因为canvas中y坐标以向下为正,与笛卡尔坐标系相反 if (a.length != 2) { //坐标是流水模式,既x1, y1, x2, y2,... while (array.length > 0) { x = array.shift()*scale; y = -array.shift()*scale; shape.fillCircle(x, y, 5); plot.fillText(lable[index++], x+5, y+10, 20); } } else { //坐标是有序对模式,即[x1, y1], [x2, y2], ... while (array.length > 0) { a = array.shift(); x = a[0]*scale; y = -a[1]*scale; shape.fillCircle(x, y, 5); plot.fillText(lable[index++], x+5, y+10, 20); } } plot.restore(); } //连接成折线 this.multiLineDraw = function(array,style, scale) { //已经考虑到y轴坐标的取反问题,只需传入原始坐标数组即可 style = style ? style : 'black'; scale = scale ? scale : 1; plot.save() .setStrokeStyle(style); var a = new Array(); a = array[0]; if (a.length != 2) { if (array.length > 2 && array.length % 2 == 0) { plot.beginPath() .moveTo(array.shift()*scale, -array.shift()*scale); while (array.length > 2) { plot.lineTo(array.shift()*scale, -array.shift()*scale); } plot.lineTo(array[0]*scale, -array[1]*scale) .moveTo(array[0]*scale, -array[1]*scale); plot.closePath() .stroke(); } } else { if (array.length > 2) { a = array.shift(); plot.beginPath() .moveTo(a[0]*scale, -a[1]*scale); while (array.length > 0) { a = array.shift(); plot.lineTo(a[0]*scale, -a[1]*scale); } plot.moveTo(a[0]*scale, -a[1]*scale); plot.closePath() .stroke(); } else { var a = array.shift(); var b = array.shift(); plot.beginPath() .moveTo(a[0]*scale, -a[1]*scale) .lineTo(b[0]*scale, -b[1]*scale) .closePath() .stroke(); } } plot.restore(); } this.fillDraw = function(array, style, scale) { //已经考虑到y轴坐标的取反问题,只需传入原始坐标数组即可 style = style ? style : 'black'; scale = scale ? scale : 1; plot.save() .setFillStyle(style); var a = array[0]; if (a.length != 2) { if (array.length > 2 && array.length % 2 == 0) { plot.beginPath() .moveTo(array.shift()*scale, -array.shift()*scale); while (array.length > 0) { plot.lineTo(array.shift()*scale, -array.shift()*scale); } plot.closePath() .fill(); } } else { if (array.length > 2) { a = array.shift(); plot.beginPath() .moveTo(a[0]*scale, -a[1]*scale); while (array.length > 0) { a = array.shift(); plot.lineTo(a[0]*scale, -a[1]*scale); } plot.closePath() .fill(); } } plot.restore(); } this.strokeDraw = function(array,style, scale) { //已经考虑到y轴坐标的取反问题,只需传入原始坐标数组即可 style = style ? style : 'black'; scale = scale ? scale : 1; plot.save() .setStrokeStyle(style); var a = array[0]; if (a.length != 2) { if (array.length > 2 && array.length % 2 == 0) { plot.beginPath() .moveTo(array.shift()*scale, -array.shift()*scale); while (array.length > 0) { plot.lineTo(array.shift()*scale, -array.shift()*scale); } plot.closePath() .stroke(); } } else { if (array.length > 2) { a = array.shift(); plot.beginPath() .moveTo(a[0]*scale, -a[1]*scale); while (array.length > 0) { a = array.shift(); plot.lineTo(a[0]*scale, -a[1]*scale); } plot.closePath() .stroke(); } } plot.restore(); } this.angleDraw = function(array, style, scale, vertexLabel) { //vertexLabel是顶点编号顺序字符串 ABC,... style = style ? style : 'black'; //array是一个存放二维坐标点序列的数组 var a0 = new Array(); a0 = [].concat(array); scale = scale ? scale : 1; var len = a0.length; if (scale != 1 && scale > 0) { for (var i = 0; i < len; i++) { for (var j = 0; j < 2; j++) { a0[i][j]*=scale; } } } //进行环状排序,这样传入的array就可以任意顺序放置坐标点。 var a = this.angularSort(a0); //分两次绘点和连线 var tmp = [].concat(a); this.pointDraw(tmp, style); tmp = [].concat(a); this.strokeDraw(tmp, style); var d1, d2, d3, angle; var x1,y1, x2, y2, x3, y3; var s; //坐标点编号 var s0 = vertexLabel ? vertexLabel : 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; //标记边的长度 var edgeLong = 0; var measure = 0; //为每个点利用余弦定理求角 for (var i = 0; i < len; i++) { if (i == 0) { x1 = a[len-1][0]; y1 = a[len-1][1]; x3 = a[i+1][0]; y3 = a[i+1][1]; } else if (i == len-1) { x1 = a[i-1][0]; y1 = a[i-1][1]; x3 = a[0][0]; y3 = a[0][1]; } else { x1 = a[i-1][0]; y1 = a[i-1][1]; x3 = a[i+1][0]; y3 = a[i+1][1]; } x2 = a[i][0]; y2 = a[i][1]; d1 = (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2); d2 = (x2-x3)*(x2-x3)+(y2-y3)*(y2-y3); d3 = (x1-x3)*(x1-x3)+(y1-y3)*(y1-y3); angle = Math.acos((d1+d2-d3)/(2*Math.sqrt(d1*d2)))/Math.PI*180; s = angle.toFixed(2)+'°'; //document.write(s+'<p>'); //标注角度和顶点编号 plot.setFillStyle('purple'); plot.fillText(s, x2, -y2-5, 100); plot.setFillStyle(style); plot.fillText(s0[i], x2, -y2+20, 20); edgeLong = (Math.sqrt(d1)/scale).toFixed(2); measure = plot.measureText(edgeLong); plot.setFillStyle('blue'); plot.fillText(edgeLong, (x1+x2-measure)/2, -(y1+y2)/2+20, measure); } //由于处理会改变原矩阵,看以下操作能否恢复原矩阵 if (scale != 1 && scale > 0) { for (var i = 0; i < len; i++) { for (var j = 0; j < 2; j++) { a0[i][j]/=scale; } } } } /** * @usage 以顶点递推方式绘制正多边形 #1 * @author mw * @date 2015年12月01日 星期二 09:42:33 * @param (x, y)图形中心坐标,r 外接圆半径 edge 边数 * @return * */ this.nEdge = function(x, y, r, edge, angle0) { edge = edge ? edge : 5; angle0 = angle0 ? angle0 : 0; var retArray = new Array(); var perAngle = Math.PI * 2 / edge; var a = r * Math.sin(perAngle / 2); var angle = -angle0; var xOffset = r * Math.sin(perAngle / 2 - angle0); var yOffset = r * Math.cos(perAngle / 2 - angle0); var x1 = x-xOffset; var y1 = y+yOffset; for (var i=0; i < edge; i++) { retArray.push([x1, y1]); x1 = x1 + 2 * a * Math.cos(angle); y1 = y1 + 2 * a * Math.sin(angle); angle -= perAngle; } return retArray; } /** * @usage 空心星形 #2 #201 #202 * @author mw * @date 2015年12月01日 星期二 10:06:13 * @param * @return * */ this.nStar = function(x, y, r, edge, angle0, arg1, arg0) { edge = edge ? edge : 5; angle0 = angle0 ? angle0 : Math.PI/2; var retArray=new Array(); var perAngle = Math.PI * 2 / edge; var r0 = arg0 ? arg0 * r : r / (2 * (1 + Math.cos(perAngle))); var scale = arg1 ? arg1 : 0.5; var angle = 0.5 * perAngle - angle0 * scale / 0.5; var xOffset = x; var yOffset = y; for (var i =0; i< edge; i++) { retArray.push([r0 * Math.cos(angle) + xOffset,r0 * Math.sin(angle) + yOffset] ); retArray.push([r * Math.cos(angle - scale * perAngle) + xOffset, r * Math.sin(angle - scale * perAngle) + yOffset]); angle -= perAngle; } return retArray; } /** * @usage 平行线, 平行四边形, 梯形 * @author mw * @date 2016年01月24日 星期日 11:14:43 * @param * @return * */ /* 平行线 Parallel lines 平行四边形 Parallel quadrilateral 梯形 trapezoid */ this.paraline = function(x, y, r, rot) { rot = rot ? -rot : 0; y = y ? -y : 0; plot.beginPath() .moveTo(x, y) .lineTo(x + r * Math.cos(rot), y + r*Math.sin(rot)) .moveTo(x, y + r/ 10) .lineTo(x + r * Math.cos(rot), y+r/10 + r*Math.sin(rot)) .closePath() .stroke(); }; this.paraquad = function(x, y, rot, a, b, angle) { angle = angle ? Math.abs(angle) : 0; rot = rot ? rot : 0; //参数说明: //平行四边形的两条边a, b, 以及它们之间的夹角angle //这个平行四边形的起始点(x, y), 以及整个图形与x轴的夹角rot var retArray = new Array(); retArray.push([x, -y]); retArray.push([x + a * Math.cos(rot), -(y + a * Math.sin(rot))]); retArray.push([x + a * Math.cos(rot)+ b * Math.cos(rot+angle), -(y + a * Math.sin(rot)+ b * Math.sin(rot+angle))]); retArray.push([x + b * Math.cos(rot+angle), -(y + b * Math.sin(rot+angle))]); return retArray; } this.trapezoid = function(x, y, rot, a, b, angle) { angle = angle ? Math.abs(angle) : 0; rot = rot ? rot : 0; //参数说明: //等腰梯形的下底边a,腰b, 以及它们之间的夹角angle //假设下底 > 上底,那么上底 = (a - b * Math.cos(angle)*2)/2 //这个平行四边形的起始点(x, y), 以及整个图形与x轴的夹角rot var c = (a - b * Math.cos(angle)*2)/2; var retArray = new Array(); if (c < 0) { //说明给的条件不对 //缺省画上底是下底一半的梯形 } else { retArray.push([x, -y]); retArray.push([x + a * Math.cos(rot), -(y + a * Math.sin(rot))]); retArray.push([x + b * Math.cos(rot+angle)+2*c * Math.cos(rot), -(y + b * Math.sin(rot+angle)+2*c*Math.sin(rot))]); retArray.push([x + b * Math.cos(rot+angle), -(y + b * Math.sin(rot+angle))]); } return retArray; } /** * @usage 绘制圆形 * @author mw * @date 2015年11月27日 星期五 12:11:38 * @param * @return * */ this.strokeCircle = function(x, y, r) { plot.beginPath() .arc(x, y, r, 0, 2*Math.PI, true) .closePath() .stroke(); } this.fillCircle = function(x, y, r) { plot.beginPath() .arc(x, y, r, 0, 2*Math.PI, true) .closePath() .fill(); } //绘制椭圆 this.strokeEllipse = function(x, y, a, b, rotate) { //关键是bezierCurveTo中两个控制点的设置 //0.5和0.6是两个关键系数(在本函数中为试验而得) var ox = 0.5 * a, oy = 0.6 * b; var rot = rotate ? -rotate : 0; plot.save() .rotate(rot) .translate(x, y) .beginPath() //从椭圆纵轴下端开始逆时针方向绘制 .moveTo(0, b) .bezierCurveTo(ox, b, a, oy, a, 0) .bezierCurveTo(a, -oy, ox, -b, 0, -b) .bezierCurveTo(-ox, -b, -a, -oy, -a, 0) .bezierCurveTo(-a, oy, -ox, b, 0, b) .closePath() .stroke() .restore(); } //绘制椭圆 this.fillEllipse = function(x, y, a, b, rotate) { //关键是bezierCurveTo中两个控制点的设置 //0.5和0.6是两个关键系数(在本函数中为试验而得) var ox = 0.5 * a, oy = 0.6 * b; var rot = rotate ? -rotate : 0; plot.save() .rotate(rot) .translate(x, y) .beginPath() //从椭圆纵轴下端开始逆时针方向绘制 .moveTo(0, b) .bezierCurveTo(ox, b, a, oy, a, 0) .bezierCurveTo(a, -oy, ox, -b, 0, -b) .bezierCurveTo(-ox, -b, -a, -oy, -a, 0) .bezierCurveTo(-a, oy, -ox, b, 0, b) .closePath() .fill() .restore(); } /** * @usage 绘制正方体 * @author mw * @date 2016年02月01日 星期一 08:40:27 * @param * @return * */ this.drawCubic = function(x0, y0, z0, r, style, style2, style3) { plot.save(); x0*=r; y0*=-r; z0*=r; z0 = z0 /2; x0 = x0 - z0*0.707; y0 = y0 + z0*0.707; z0 = 0; plot.translate(x0, y0); style = style ? style : 'black'; style2 = style2 ? style2 : style; style3 = style3 ? style3 : style; //左下角[x0, y0,边长r shape.fillDraw(shape.nEdge(0, 0,0.707*r, 4, 0), style); //顶面 shape.fillDraw(shape.paraquad(-0.5*r, 0.5*r, 0, r, r/2, Math.PI/4), style2); shape.strokeDraw(shape.paraquad(-0.5*r, 0.5*r, 0, r, r/2, Math.PI/4), 'white'); //右侧面 shape.fillDraw(shape.paraquad(0.5*r, -0.5*r, Math.PI/4, r/2, r, Math.PI/4), style3); shape.strokeDraw(shape.paraquad(0.5*r, -0.5*r, Math.PI/4, r/2, r, Math.PI/4), 'white'); plot.restore(); } this.point3D = function(x0, y0, z0) { //canvas中y轴坐标向下为正,与笛卡尔坐标系相反 //所以此处先取反 // z0 = z0 /2; x0 = x0 - z0*0.707; y0 = y0 + z0*0.707; return [x0, y0]; } //左视投影,此时x坐标是无所谓的 this.pointLeft = function(x0, y0, z0) { return [z0, y0]; } //右视投影 this.pointRight = function(x0, y0, z0) { return [-z0, y0]; } //俯视投影 this.pointTop = function(x0, y0, z0) { return [x0, -z0]; } //仰视投影 this.pointBottom = function(x0, y0, z0) { return [x0, z0]; } //主视投影 this.pointFront = function(x0, y0, z0) { return [x0, y0]; } //后视投影 this.pointBack = function(x0, y0, z0) { return [-x0, y0]; } this.strokeCubic = function(x0, y0, z0, r, style) { plot.save(); x0 *= r; y0 *= r; z0 *= r; r *= 0.5; var array = [[-r, -r], [-r, r], [r, r], [r, -r]]; var top = []; var left = []; var front = []; var x, y, z; //存放临时点 var p = []; for (var i = 0; i < 4; i++) { x = (x0+array[i][0]); y = y0+r; z = (z0+array[i][1]); p = this.point3D(x, y, z); top.push([p[0], -p[1]]); } for (var i = 0; i < 4; i++) { x = x0+r; y = (y0+array[i][0])+2*r; z = z0+array[i][1]; p = this.point3D(x, y, z); left.push([p[0], -p[1]]); } for (var i = 0; i < 4; i++) { x = x0+array[i][0]; y = (y0+array[i][1])+2*r; z = z0+r; p = this.point3D(x, y, z); front.push([p[0], -p[1]]); } var tmp = [].concat(top); shape.fillDraw(tmp, style); tmp=[].concat(top); shape.strokeDraw(tmp, '#cccccc'); tmp = [].concat(left); shape.strokeDraw(left, 'black'); tmp = [].concat(front); shape.strokeDraw(front, 'black'); plot.restore(); } /** * @usage 把三维点阵列按照z, y, x优先级由小到大排列 * @author mw * @date 2016年02月23日 星期二 09:38:27 * @param [[x1, y1, z1], [x2,y2, z2], ...] * @return 排序后的[[x, y, z]...] * */ this.xyzSort = function(array) { var arr = new Array(); arr = array; arr.sort(function(a, b) { if (a[2] != b[2]) { return a[2] - b[2]; } else { if (a[1] != b[1]) { return (a[1] - b[1]); } else { return a[0] - b[0]; } } }); //document.write(arr); return arr; } //把给定的坐标点阵列数组[x, y],...按照距离它们的中心点的角度进行排列 //是为了把无序排列的闭合曲线上的点进行有序排列,后续可再经过连线形成 //可填充的闭合曲线 this.angularSort = function(array) { var a = new Array(); a = [].concat(array); var len = a.length, len1 = a[0].length; //不符合处理条件,不进行处理 if (len <= 0 || len1 != 2) return array; //求中心点 var xTotal = 0, yTotal = 0, xCenter = 0, yCenter = 0; for (var i = 0; i < len; i++) { xTotal += a[i][0]; yTotal += a[i][1]; } xCenter = xTotal/len; yCenter = yTotal/len; //求与中心点夹角并排序 var b = new Array(); var x, y, xdiff, ydiff; for (var i = 0; i < len; i++) { x = a[i][0]; y = a[i][1]; xdiff = x-xCenter; ydiff = y-yCenter; if (Math.abs(xdiff)<0.0001) { if (ydiff > 0) { b.push([x, y, Math.PI/2]); } else { b.push([x, y, Math.PI/2*3]); } } else if ( xdiff >= 0 && ydiff > 0) {//第一象限 b.push([x, y, Math.atan(Math.abs(ydiff/xdiff))]); } else if (xdiff < 0 && ydiff >= 0) {//第二象限 b.push([x, y, Math.PI-Math.atan(Math.abs(ydiff/xdiff))]); } else if (xdiff <= 0 && ydiff < 0) {//第三象限 b.push([x, y, Math.PI+Math.atan(Math.abs(ydiff/xdiff))]); } else {//第四象限 b.push([x, y, Math.PI*2-Math.atan(Math.abs(ydiff/xdiff))]); } } b.sort(function(b1, b2) { if (Math.abs(b1[2]-b2[2]) < 0.0001) { //按照与中心点的距离大小排序 var d1 = (b1[0]-xCenter)*(b1[0]-xCenter)+ (b1[1]-yCenter)*(b1[1]-yCenter); var d2 = (b2[0]-xCenter)*(b2[0]-xCenter)+ (b2[1]-yCenter)*(b2[1]-yCenter); return -(d1-d2); } else { return (b1[2]-b2[2]); } }); var retArray = new Array(); for (var i = 0; i < len; i++) { //如果两个点在经过中心点的同一直线上,舍弃这个点 //因为它表示点阵列可能不是单一环,或不是闭合曲线 if (i > 0 && Math.abs(b[i][2]-b[i-1][2]) < 0.0001) continue; retArray.push([b[i][0], b[i][1]]); } return retArray; } /** * @usage 三视图 * @author mw * @date 2016年02月23日 星期二 09:49:23 * @param * @return * */ this.threeView = function(array, style) { var cubic = this.xyzSort(array); plot.save(); plot.setTransform(1, 0, 0, 1, 0, 0) .translate(300, 200); //三维图和三视图 var r = 50; style = style ? style : 'red'; var len = cubic.length; for (var i = 0; i < len; i++) { this.drawCubic(cubic[i][0], cubic[i][1], cubic[i][2], r, style); } var height = 400; r = r/3; plot.setTransform(1, 0, 0, 1, 0, 0); plot.fillText('左视图', 20, 20, 100); plot.fillText('主视图', 20, 20+1*height/3, 100); plot.fillText('俯视图', 20, 20+2*height/3, 100); plot.setFillStyle(style) .setStrokeStyle('white'); //左视图 plot.translate(100, 80); for (var i = 0; i < len; i++) { //y, z两坐标,z坐标变为x坐标 this.fillRect(cubic[i][2]*r, -cubic[i][1]*r, r, r); this.strokeRect(cubic[i][2]*r, -cubic[i][1]*r, r, r); } //主视图 plot.translate(0, 130); for (var i = 0; i < len; i++) { //x, y两坐标 this.fillRect(cubic[i][0]*r, -cubic[i][1]*r, r, r); this.strokeRect(cubic[i][0]*r, -cubic[i][1]*r, r, r); } //俯视图 plot.translate(0, 100); for (var i = 0; i < len; i++) { //x, z两坐标,z坐标变为y坐标 this.fillRect(cubic[i][0]*r, cubic[i][2]*r, r, r); this.strokeRect(cubic[i][0]*r, cubic[i][2]*r, r, r); } plot.restore(); } //绘制球体 this.sphere = function(pos/*[x, y, z]*/, r, style) { plot.save(); var x, y; var p = [].concat(pos); if (p.length == 2) { x = p[0]; y = p[1]; } else if (p.length == 3) { var p1 = shape.point3D(p[0], -p[1], p[2]); x = p1[0]; y = p1[1]; } var r0 = 0.1*r; var grd = plot.createRadialGradient(x, y, r, x+0.3*r, y-0.3*r, r0); grd.addColorStop(0, style); grd.addColorStop(1, 'white'); plot.setFillStyle(grd); shape.fillCircle(x, y, r); plot.restore(); } return { fillRect:fillRect, strokeRect:strokeRect, fillCircle:fillCircle, strokeCircle:strokeCircle, strokeEllipse:strokeEllipse, fillEllipse:fillEllipse, //绘制点阵列 pointDraw:pointDraw, multiLineDraw:multiLineDraw, strokeDraw:strokeDraw, fillDraw:fillDraw, //多边形角度标注 angleDraw:angleDraw, nEdge:nEdge, nStar:nStar, paraline:paraline, paraquad:paraquad, trapezoid:trapezoid, //绘制立方体 drawCubic:drawCubic, strokeCubic:strokeCubic, //绘制球体 sphere:sphere, //三维点映射 point3D:point3D, pointLeft:pointLeft, pointRight:pointRight, pointTop:pointTop, pointBottom:pointBottom, pointFront:pointFront, pointBack:pointBack, //三视图 threeView:threeView, //顶点排序 xyzSort:xyzSort, angularSort:angularSort }; }();
/** * @usage 对点阵列数组进行平移,旋转,缩放,对称等变形 * @author mw * @date 2016年03月20日 星期日 13:24:58 * @param 传入点阵列矩阵 * @return 输出变形后的点阵列矩阵 * */ function Transform() { this.translate = function(array, xOffset, yOffset) { var len = array.length; if (len == 0) { return []; } else { var len1 = array[0].length; if (len1 != 2) { //如果不是点阵列[..., [x,y], [x1,y1], ...]的格式,暂时不加处理 return array; } else { var retArray = new Array(); var x = 0, y = 0; for (var i = 0; i < len; i++) { x = array[i][0] + xOffset; y = array[i][1] + yOffset; retArray.push([x, y]); } } } return retArray; } this.scale = function(array, xScale, yScale) { var len = array.length; if (len == 0) { return []; } else { xScale = xScale ? xScale : 1; yScale = yScale ? yScale : xScale; var len1 = array[0].length; if (len1 != 2) { //如果不是点阵列[..., [x,y], [x1,y1], ...]的格式,暂时不加处理 return array; } else { var retArray = new Array(); var x = 0, y = 0; for (var i = 0; i < len; i++) { x = array[i][0] * xScale; y = array[i][1] * yScale; retArray.push([x, y]); } } } return retArray; } this.rotate = function(array, angle) { var len = array.length; if (len == 0) { return []; } else { var len1 = array[0].length; if (len1 != 2) { //如果不是点阵列[..., [x,y], [x1,y1], ...]的格式,暂时不加处理 return array; } else { var retArray = new Array(); var x = 0, y = 0; var sinA, cosA; for (var i = 0; i < len; i++) { sinA = Math.sin(angle); cosA = Math.cos(angle); x = array[i][0] * cosA - array[i][1]*sinA; y = array[i][0] * sinA + array[i][1]*cosA; retArray.push([x, y]); } } } return retArray; } this.flipX = function(array) { return this.flipImplement(array, 'X'); } this.flipY = function(array) { return this.flipImplement(array, 'Y'); } this.flipXY = function(array) { return this.flipImplement(array, 'XY'); } //关于直线y=kx轴对称 this.flip = function(array, slope) { //slope为斜率k var mode = slope.toFixed(3); return this.flipImplement(array, mode); } this.flipImplement = function(array, mode) { var len = array.length; if (len == 0) { return []; } else { var len1 = array[0].length; if (len1 != 2) { //如果不是点阵列[..., [x,y], [x1,y1], ...]的格式,暂时不加处理 return array; } else { var retArray = new Array(); var x = 0, y = 0; var sinA, cosA; var m = 0, n = 0; if (mode == 'X') { for (var i = 0; i < len; i++) { //关于X轴对称, x = array[i][0]; y = -array[i][1]; retArray.push([x, y]); } } else if (mode == 'Y') { for (var i = 0; i < len; i++) { //关于Y轴对称, x = -array[i][0]; y = array[i][1]; retArray.push([x, y]); } } else if (mode == 'XY') { for (var i = 0; i < len; i++) { //中心对称 x = -array[i][0]; y = -array[i][1]; retArray.push([x, y]); } } else { //模式为斜率 y = kx中k的字符串 k = parseFloat(mode); for (var i = 0; i < len; i++) { //可扩展 //此处先放大100倍再缩小是因为对于小尺寸 //计算误差太大,而如果尺寸太大, //标注会占用太多地方,造成文字拥挤,无法读取 m = array[i][0]*10000; n = array[i][1]*10000; //x = (m-2*k+2*k*n-m*k*k)/(1+k*k); x = (1-k*k)*m+2*k*(n-1)/(1+k*k); //y = (-n+2*k*m+n*k*k)/(1+k*k); y = (2*k*m-(1-k*k)*n)/(1+k*k); retArray.push([x/10000, y/10000]); } } } } return retArray; } }
function Triangle() { this.edges = []; this.angles = []; //已知三条边 this.know3edges = function(edges) { this.edges = []; this.angles = []; this.edges = edges; //角度为弧度单位 //a边对应角 this.angles.push(Math.acos((edges[1]*edges[1] + edges[2]*edges[2]-edges[0]*edges[0])/(2*edges[1]*edges[2]))); //b边对应角 this.angles.push(Math.acos((edges[0]*edges[0] + edges[2]*edges[2]-edges[1]*edges[1])/(2*edges[0]*edges[2]))); //c边对应角 this.angles.push(Math.acos((edges[0]*edges[0] + edges[1]*edges[1]-edges[2]*edges[2])/(2*edges[0]*edges[1]))); var x0 = 0, y0 = 0; var x1 = x0 + this.edges[0], y1 = y0; var x2 = x0 + this.edges[1] * Math.cos(-this.angles[2]), y2 = y0 + this.edges[1] * Math.sin(-this.angles[2]); var retArray = new Array(); retArray.push([x0, y0]); retArray.push([x1, y1]); retArray.push([x2, y2]); return retArray; } //已知两个角 this.know2angles = function(angles, r) { this.edges = []; this.angles = []; this.angles = [angles[0]/180*Math.PI, angles[1]/180*Math.PI, (180-(angles[0]+angles[1]))/180*Math.PI]; //设其中一边长度为10 r = r > 0 ? r : 10; //A边 this.edges.push(r); var angleA = this.angles[0]; angleB = this.angles[1]; angleC = this.angles[2]; //B边 this.edges.push(Math.sin(angleB)/Math.sin(angleA)*r); //C边 this.edges.push(Math.sin(angleC)/Math.sin(angleA)*r); var x0 = 0, y0 = 0; var x1 = x0 + this.edges[0], y1 = y0; var x2 = x0 + this.edges[1] * Math.cos(-this.angles[2]), y2 = y0 + this.edges[1] * Math.sin(-this.angles[2]); var retArray = new Array(); retArray.push([x0, y0]); retArray.push([x1, y1]); retArray.push([x2, y2]); return retArray; } //已知2条边和夹角 this.know2edges = function(edges, angle) { this.edges = []; this.angles = []; //如果没有指定两边的夹角,默认为90度 angle = angle ? angle : 90; var edgeC = 0; if (angle == -90) { //设定当输入角度为-90时指已知斜边和一条直角边的直角三角形 var swap; if (edges[0] < edges[1]) { swap = edges[0]; edges[0] = edges[1]; edges[1] = swap; } edgeC = Math.sqrt(edges[0]*edges[0]-edges[1]*edges[1]); angle = Math.asin(edgeC/edges[0]); } else { angle = angle ? angle/180*Math.PI : Math.PI/2; edgeC = Math.sqrt(edges[0]*edges[0]+edges[1]*edges[1]-2*edges[0]*edges[1]*Math.cos(angle)); } this.edges = [edges[0], edges[1], edgeC]; var edgeA = this.edges[0], edgeB = this.edges[1]; //角度为弧度单位 //a边对应角 this.angles.push(Math.acos((edgeB*edgeB + edgeC*edgeC-edgeA*edgeA)/(2*edgeB*edgeC))); //b边对应角 this.angles.push(Math.acos((edgeA*edgeA + edgeC*edgeC-edgeB*edgeB)/(2*edgeA*edgeC))); //c边对应角 this.angles.push(angle); var x0 = 0, y0 = 0; var x1 = x0 + this.edges[0], y1 = y0; var x2 = x0 + this.edges[1] * Math.cos(-this.angles[2]), y2 = y0 + this.edges[1] * Math.sin(-this.angles[2]); var retArray = new Array(); retArray.push([x0, y0]); retArray.push([x1, y1]); retArray.push([x2, y2]); return retArray; } //返回角度和边信息的字符串 this.info = function() { var angleLabel = ['C', 'A', 'B']; var edgeLabel = ['ab', 'bc', 'ac']; var s = ''; for (var i = 0; i < 3; i++) { s += edgeLabel[i]+' : '; s += this.edges[i].toFixed(2)+' ; '; } for (var i = 0; i < 3; i++) { s += angleLabel[i]+' : '; s += (this.angles[i]*180/Math.PI).toFixed(2) + ' ; '; } return s; } }
当然只是用了其中的一小部分, 为了防止遗漏,在此全部贴出。
本节到此结束,欲知后事如何,请看下回分解。
以上是关于[从头学数学] 第152节 旋转的主要内容,如果未能解决你的问题,请参考以下文章