正剧开始:
星历2016年03月01日 09:32:04, 银河系厄尔斯星球中华帝国江南行省。
[工程师阿伟]正在和[机器小伟]一起研究[几何图形初步]。
关于正方体的展开图有哪些,这里有一张图:
下面这题还真是难,阿伟和小伟都不能确定答案。
这个图怎么画呢?
<span style="font-size:18px;">function myDraw() { var config = new PlotConfiguration(); config.init(); config.setPreference(); config.setSector(1,1,1,1); //config.axis3D(0, 0, 0, 180); var r = 100; var array = shape.nStar(0, 0, r, 5); var array2 = shape.nEdge(0, 0, r, 5); var tmp = [].concat(array); shape.fillDraw(tmp, 'pink'); tmp = [].concat(array2); shape.strokeDraw(tmp, 'pink'); var r0 = Math.cos(Math.PI/5*2) / Math.cos(Math.PI/5) * r; var array3 = shape.nEdge(0, 0, r0, 5, Math.PI); tmp = [].concat(array3); shape.strokeDraw(tmp, 'white'); } </span>
可见两个五边形的半径是cos(72)/cos(36)的关系。[角度制]
另一个图案是这样的:
<span style="font-size:18px;">function myDraw() { var config = new PlotConfiguration(); config.init(); config.setPreference(); config.setSector(1,1,1,1); //config.axis3D(0, 0, 0, 180); var r = 100; var array = shape.nEdge(0, 0, r, 6); r *= Math.sin(Math.PI/3); var array2 = shape.nEdge(0, 0, r, 3, Math.PI); var tmp = [].concat(array); shape.strokeDraw(tmp, '#0088CC'); tmp = [].concat(array2); shape.fillDraw(tmp, 'pink'); var array3 = shape.nEdge(0, 0, r, 3); tmp = [].concat(array3); shape.fillDraw(tmp, '#0088CC'); tmp = [].concat(array3); shape.strokeDraw(tmp, 'white'); } </span>
[人叫板老师]的知识点讲解完毕,下面该[机器小伟]来画图了。
小伟先画了一个圆柱:
<span style="font-size:18px;">//三维点 function point3D(x0, y0, z0) { //canvas中y轴坐标向下为正,与笛卡尔坐标系相反 //所以此处先取反 // z0 = z0 /2; x0 = x0 - z0*0.707; y0 = y0 + z0*0.707; return [x0, y0]; } /** * @usage 绘制三维的椭圆,平行于xz | yz | xz平面,其余情况忽略 * @author mw * @date 2016年02月21日 星期日 08:29:00 * @param 以[x,y,z]三维点为中心,r为半径的椭圆 * @return 椭圆上点按顺时针排列的数组,已转为二维视图坐标 * */ function ellipse(xyz, r, face) { var center = new Array(); center = xyz; var retArray = new Array(); var angle = 0; var x, y, z; face = face ? face : 'xz'; //圆周细分为36点 for (var i = 0; i < 36; i++) { if (face == 'xz') { //平行xz平面 x = center[0] + r * Math.cos(angle-Math.PI/9); y = -center[1]; z = center[2] + r * Math.sin(angle-Math.PI/9); } else if (face == 'yz') { //平行yz平面 x = center[0]; y = -center[1] + r * Math.sin(angle-Math.PI/9); z = center[2] + r * Math.cos(angle-Math.PI/9); } else { //平行xy平面 x = center[0] + r * Math.cos(angle-Math.PI/9); y = -center[1] + r * Math.sin(angle-Math.PI/9); z = center[2]; } retArray.push(point3D(x, y, z)); angle += Math.PI*2/36; } return retArray; } function myDraw() { var config = new PlotConfiguration(); config.init(); config.setPreference(); config.setSector(1,1,1,1); config.axis3D(0, 0,0, 180); var r = 100; //document.write(ellipse([0, 0, 0], r)); //shape.strokeDraw(ellipse([0, 0, 0], r), 'red'); var top = ellipse([100, r, r], r, 'yz'); var bottom = ellipse([-100, r, r], r, 'yz'); var tmp = [].concat(top); shape.strokeDraw(tmp, 'red'); tmp = [].concat(bottom); shape.strokeDraw(tmp, 'red'); shape.strokeDraw(ellipse([0, r, r], r, 'yz'), 'pink'); //document.write(top[0][0]); var mid = top.length/4; var mid2 = top.length/4*3; plot.setStrokeStyle('red'); plot.beginPath() .moveTo(top[mid2][0], top[mid2][1]) .lineTo(bottom[mid2][0], bottom[mid2][1]) .moveTo(bottom[mid][0], bottom[mid][1]) .lineTo(top[mid][0], top[mid][1]) .closePath() .stroke(); }</span>
再画一个长方体:
<span style="font-size:18px;">//长方体和正方体绘制 function rectBlock(a, b, h) { return [[0,0,0],[0,0,h],[0,b,0],[0,b,h],[a,0,0],[a,0,h],[a,b,0],[a,b,h]]; } function strokeBlock(a, b, h) { //顶点的计算 var array = rectBlock(a, b, h); //把长方体八个顶点进行排序。 var all = shape.xyzSort(array); //长方体表现出来右侧面,上侧面,前侧面,这里的后两个元素是调换了位置的。 //这样才可以连成闭合图形。 var a = [all[4], all[5], all[7], all[6]]; var b = [all[1], all[5], all[7], all[3]]; var h = [all[6], all[7], all[3], all[2]]; var tmp = new Array(); for (var i = 0; i < a.length; i++) { tmp.push(point3D(a[i][0], -a[i][1], a[i][2])); } shape.fillDraw(tmp, 'red'); for (var i = 0; i < b.length; i++) { tmp.push(point3D(b[i][0], -b[i][1], b[i][2])); } shape.strokeDraw(tmp, 'blue'); for (var i = 0; i < h.length; i++) { tmp.push(point3D(h[i][0], -h[i][1], h[i][2])); } shape.fillDraw(tmp, 'green'); } function myDraw() { var config = new PlotConfiguration(); config.init(); config.setPreference(); config.setSector(1,1,1,1); config.axis3D(0, 0, 0, 180); for (var i = 1; i < 4; i++) { for (var j = 1; j < 4; j++) { strokeBlock(100, 60, 50); } } } </span>
再画一个五棱锥
<span style="font-size:18px;">function myDraw() { var config = new PlotConfiguration(); config.init(); config.setPreference(); config.setSector(1,1,1,1); config.axis3D(0, 0, 0, 180); var r = 100; var array = shape.nEdge(0, 0, 100, 5); var array3D = []; //shape.strokeDraw(array); for (var i = 0; i < array.length; i++) { array3D.push(point3D(array[i][0], 0, array[i][1])); } //shape.strokeDraw(array3D); var tmp = [].concat(array3D); shape.strokeDraw(tmp, 'red'); var pointA = point3D(0, -r, 0); plot.setStrokeStyle('red'); for (var i = 0; i < array.length; i++) { plot.beginPath() .moveTo(array3D[i][0], array3D[i][1]) .lineTo(pointA[0], pointA[1]) .closePath() .stroke(); } } </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) { style = style ? style : 'black'; plot.save() .setFillStyle(style); var a = new Array(); a = array[0]; if (a.length != 2) { while (array.length > 0) { shape.fillCircle(array.shift(), array.shift(), 5); } } else { while (array.length > 0) { a = array.shift(); shape.fillCircle(a[0], a[1], 5); } } plot.restore(); } //连接成折线 this.multiLineDraw = function(array,style) { style = style ? style : 'black'; 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(), array.shift()); while (array.length > 2) { plot.lineTo(array.shift(), array.shift()); } plot.lineTo(array[0], array[1]) .moveTo(array[0], array[1]); plot.closePath() .stroke(); } } else { if (array.length > 2) { a = array.shift(); plot.beginPath() .moveTo(a[0], a[1]); while (array.length > 0) { a = array.shift(); plot.lineTo(a[0], a[1]); } plot.moveTo(a[0], a[1]); plot.closePath() .stroke(); } } plot.restore(); } this.fillDraw = function(array, style) { style = style ? style : 'black'; plot.save() .setFillStyle(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(), array.shift()); while (array.length > 0) { plot.lineTo(array.shift(), array.shift()); } plot.closePath() .fill(); } } else { if (array.length > 2) { a = array.shift(); plot.beginPath() .moveTo(a[0], a[1]); while (array.length > 0) { a = array.shift(); plot.lineTo(a[0], a[1]); } plot.closePath() .fill(); } } plot.restore(); } this.strokeDraw = function(array,style) { style = style ? style : 'black'; 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(), array.shift()); while (array.length > 0) { plot.lineTo(array.shift(), array.shift()); } plot.closePath() .stroke(); } } else { if (array.length > 2) { a = array.shift(); plot.beginPath() .moveTo(a[0], a[1]); while (array.length > 0) { a = array.shift(); plot.lineTo(a[0], a[1]); } plot.closePath() .stroke(); } } plot.restore(); } /** * @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); retArray.push(r0 * Math.sin(angle) + yOffset); retArray.push(r * Math.cos(angle - scale * perAngle) + xOffset); retArray.push(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 + r/2, y0 - r/2); 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(); } /** * @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; } /** * @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(); } return { fillRect:fillRect, strokeRect:strokeRect, fillCircle:fillCircle, strokeCircle:strokeCircle, strokeEllipse:strokeEllipse, fillEllipse:fillEllipse, //绘制点阵列 pointDraw:pointDraw, multiLineDraw:multiLineDraw, strokeDraw:strokeDraw, fillDraw:fillDraw, nEdge:nEdge, nStar:nStar, paraline:paraline, paraquad:paraquad, trapezoid:trapezoid, //绘制立方体 drawCubic:drawCubic, //三视图 threeView:threeView, xyzSort:xyzSort }; }();
本节到此结束,欲知后事如何,请看下回分解。