[从头学数学] 第152节 旋转

剧情提要:
[机器小伟]在[project师阿伟]的陪同下进入了筑基后期的修炼。
这次要修炼的目标是[旋转]。



正剧開始:

星历2016年03月26日 16:11:34, 银河系厄尔斯星球中华帝国江南行省。
[project师阿伟]正在和[机器小伟]一起研究[旋转]。





<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; } }


当然仅仅是用了当中的一小部分, 为了防止遗漏。在此所有贴出。


本节到此结束,欲知后事怎样,请看下回分解。


原文地址:https://www.cnblogs.com/wgwyanfs/p/7152207.html