剧情提要:
[机器小伟]在[工程师阿伟]的陪同下进入了筑基初期的修炼,
这次要修炼的目标是[几何图形初步]。
![](https://img-blog.csdn.net/20160301103712872)
![](https://img-blog.csdn.net/20160301104432141)
[机器小伟]在[工程师阿伟]的陪同下进入了筑基初期的修炼,
这次要修炼的目标是[几何图形初步]。
正剧开始:
星历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
};
}();
本节到此结束,欲知后事如何,请看下回分解。