点击浏览该文件
源文件:
点击浏览该文件
UploadFile/2004-4/20044312818100.swf
数据结构及数据定义(代码位于主场景第1帧)
小球,小棍分别用一个数组存储:dot[sm]和gun[sm_gun] ,sm是小球的数目,sm_gun是小棍的数目。 小球:
x,y,z(空间坐标)[初始化时提供值]
vx,vy(屏幕显示坐标)[这两个值由函数"v_xz()"计算得到]
col(颜色,实际上是帧数。小球是个含有若干关键帧的MC,显示时由col决定停在哪一帧上)[初始化时提供值]
r(半径,实际上是为了小棍的"消隐"而设计这个值的,本程序中的小球未使用r)[初始化时提供值] 小棍:
dot_s,dot_e(分别是起点和终点小球的编号)[初始化时提供值]
sx,sy,sz(实际起点的空间坐标)[由函数"gun_js()"根据起点终点小球坐标以及起点小球的半径算得] ex,ey,ez(实际终点的空间坐标)[由函数"gun_js()"根据起点终点小球坐标以及终点小球的半径算得] svx,svy(起点的屏幕显示坐标)[由函数"v_xz()"根据sx,sy,sz的值计算]
evx,evy(终点的屏幕显示坐标)[由函数"v_xz()"根据ex,ey,ez的值计算]
oy(小棍中点处的空间坐标的y值,用于比较深度)[根据起点终点小球的空间坐标y值计算] 下面以甲烷的数据为例:
// 甲烷
function get_data_2() {
// 小球初始化
// 小球数目
sm = 5;
dot = new Array(sm);
for (i=0; i<sm; i++) {
dot[i] = new Object();
dot[i].x = 0;
dot[i].y = 0;
dot[i].z = 0;
dot[i].vx = 0;
dot[i].vy = 0;
dot[i].col = 0;
dot[i].r = 5;
}
// 0
dot[0].x = 50;
dot[0].y = 50;
dot[0].z = 50;
dot[0].col = 1;
// 1
dot[1].x = -50;
dot[1].y = -50;
dot[1].z = 50;
dot[1].col = 2;
// 2
dot[2].x = -50;
dot[2].y = 50;
dot[2].z = -50;
dot[2].col = 3;
// 3
dot[3].x = 50;
dot[3].y = -50;
dot[3].z = -50;
dot[3].col = 4;
// 4
dot[4].x = 0;
dot[4].y = 0;
dot[4].z = 0;
dot[4].col = 10;
// 小球初始化完成
//
// 小棍初始化
// 小棍的数目
sm_gun = 4;
gun = new Array(sm_gun);
for (i=0; i<sm_gun; i++) {
gun[i] = new Object();
gun[i].dot_s = 0;
gun[i].dot_e = 0;
gun[i].sx = 0;
gun[i].sy = 0;
gun[i].sz = 0;
gun[i].ex = 0;
gun[i].ey = 0;
gun[i].ez = 0;
gun[i].oy = 0;
// 屏幕坐标(起点和终点)
gun[i].svx = 0;
gun[i].svy = 0;
gun[i].evx = 0;
gun[i].evy = 0;
}
// 0
gun[0].dot_s = 0;
gun[0].dot_e = 4;
// 1
gun[1].dot_s = 1;
gun[1].dot_e = 4;
// 2
gun[2].dot_s = 2;
gun[2].dot_e = 4;
// 3
gun[3].dot_s = 3;
gun[3].dot_e = 4;
// 小棍初始化完成
}
//
自定义函数和主调程序段:(代码位于主场景第2帧)function get_data_2() {
// 小球初始化
// 小球数目
sm = 5;
dot = new Array(sm);
for (i=0; i<sm; i++) {
dot[i] = new Object();
dot[i].x = 0;
dot[i].y = 0;
dot[i].z = 0;
dot[i].vx = 0;
dot[i].vy = 0;
dot[i].col = 0;
dot[i].r = 5;
}
// 0
dot[0].x = 50;
dot[0].y = 50;
dot[0].z = 50;
dot[0].col = 1;
// 1
dot[1].x = -50;
dot[1].y = -50;
dot[1].z = 50;
dot[1].col = 2;
// 2
dot[2].x = -50;
dot[2].y = 50;
dot[2].z = -50;
dot[2].col = 3;
// 3
dot[3].x = 50;
dot[3].y = -50;
dot[3].z = -50;
dot[3].col = 4;
// 4
dot[4].x = 0;
dot[4].y = 0;
dot[4].z = 0;
dot[4].col = 10;
// 小球初始化完成
//
// 小棍初始化
// 小棍的数目
sm_gun = 4;
gun = new Array(sm_gun);
for (i=0; i<sm_gun; i++) {
gun[i] = new Object();
gun[i].dot_s = 0;
gun[i].dot_e = 0;
gun[i].sx = 0;
gun[i].sy = 0;
gun[i].sz = 0;
gun[i].ex = 0;
gun[i].ey = 0;
gun[i].ez = 0;
gun[i].oy = 0;
// 屏幕坐标(起点和终点)
gun[i].svx = 0;
gun[i].svy = 0;
gun[i].evx = 0;
gun[i].evy = 0;
}
// 0
gun[0].dot_s = 0;
gun[0].dot_e = 4;
// 1
gun[1].dot_s = 1;
gun[1].dot_e = 4;
// 2
gun[2].dot_s = 2;
gun[2].dot_e = 4;
// 3
gun[3].dot_s = 3;
gun[3].dot_e = 4;
// 小棍初始化完成
}
//
gun_js() [为达到消隐的目的,小棍的起点和终点必须是在球的表面上,而不是球的中心。本函数就是根据起点小球和终点小球的空间坐标来计算小棍实际的起点和终点的空间坐标。另外还计算了小棍中点处的空间坐标y值,这个值用于比较深度,也是为消隐作准备]
scale(s) [整体缩放函数,本程序中未使用]
rotate(xa, ya) [旋转函数,xa是绕x轴旋转的角度值,而ya是绕z轴旋转的角度值,并不是y轴,这是变量名书写不当造成的,未做修改]
v_xz(d) [本函数将小球,小棍的空间坐标换算成屏幕显示坐标,也就是将那些坐标点投影到xz平面上,d是视点到xz平面的距离]
get_deeps() [将各小球,各小棍的深度值取出,放到一个新的数组中,并同时记录小球或小棍的编号。由于小球,小棍的编号都是从0开始,为了区分小球和小棍,特将小棍的编号加上1000后再存储。执行此函数后得到一个存有深度值和对应编号的数组"deeps[sm+sm_gun]",后面进行消隐显示时就方便了]
get_v() [消隐显示,从最深度最大者开始,逐个显示小球或小棍。从数组"deeps[sm+sm_gun]"中找出y值最小者,即深度最大者,并记录相应的编号,然后根据编号是>=1000还是<1000即可判断它是小棍还是小球,然后提取相应编号的小棍或小球的屏幕显示坐标值,对于小球而言,还须提取其颜色值和深度值,以便于显示不同颜色,不同大小,不同模糊程度。]
cl_all() [清除所有用于显示小球和小棍的MC,本函数在按钮中用到]
stop();
// 原点坐标
ox = 200;
oy = 200;
// 取得点集
get_data(3);
// 小棍的起点,终点,深度计算
function gun_js() {
var i, a, b, ar, br, dxy, dxz;
var ax, ay, az, bx, by, bz, dx, dy, dz;
for (i=0; i<sm_gun; i++) {
a = gun[i].dot_s;
ar = dot[a].r;
ax = dot[a].x;
ay = dot[a].y;
az = dot[a].z;
//
b = gun[i].dot_e;
br = dot[b].r;
bx = dot[b].x;
by = dot[b].y;
bz = dot[b].z;
//
dx = bx-ax;
dy = by-ay;
dz = bz-az;
// dxy,dxz分别是a,b投影到xy,xz面上后的距离
dxy = Math.sqrt(dx*dx+dy*dy);
dxz = Math.sqrt(dx*dx+dz*dz);
// 绘图时的起点坐标
gun[i].sx = (dxy-br)*dx/dxy+ax;
gun[i].sy = (dxy-br)*dy/dxy+ay;
gun[i].sz = (dxz-br)*dz/dxz+az;
// 绘图时的终点坐标
gun[i].ex = -(dxy-ar)*dx/dxy+bx;
gun[i].ey = -(dxy-ar)*dy/dxy+by;
gun[i].ez = -(dxz-ar)*dz/dxz+bz;
// 小棍中点处的y值,用以比较深度
gun[i].oy = (ay+by)/2;
}
}
//
// 缩放函数,s为缩放系数
function scale(s) {
var i;
for (i=0; i<sm; i++) {
dot[i].x *= s;
dot[i].y *= s;
dot[i].z *= s;
}
}
// 物体旋转,xa,ya均为角度值
function rotate(xa, ya) {
var i, k;
var rad = Math.PI/180;
xa *= rad;
ya *= rad;
var sin_xa = Math.sin(xa);
var cos_xa = Math.cos(xa);
var sin_ya = Math.sin(ya);
var cos_ya = Math.cos(ya);
var px, py, pz, tempz;
for (i=0; i<sm; i++) {
px = dot[i].x;
py = dot[i].y;
pz = dot[i].z;
tempz = (py*cos_ya)-(px*sin_ya);
dot[i].x = (py*sin_ya)+(px*cos_ya);
dot[i].y = (pz*sin_xa)+(tempz*cos_xa);
dot[i].z = (pz*cos_xa)-(tempz*sin_xa);
}
}
// 将物体投影到xz平面,d为视点到投影面的距离
function v_xz(d) {
var i, k;
// 处理小球坐标
for (i=0; i<sm; i++) {
k = 1-dot[i].y/d;
dot[i].vx = dot[i].x/k;
dot[i].vy = -dot[i].z/k;
}
//
// 处理小棍坐标
for (i=0; i<sm_gun; i++) {
// 小棍起点
k = 1-gun[i].sy/d;
gun[i].svx = gun[i].sx/k;
gun[i].svy = -gun[i].sz/k;
// 小棍终点
k = 1-gun[i].ey/d;
gun[i].evx = gun[i].ex/k;
gun[i].evy = -gun[i].ez/k;
}
}
// 提取深度值,即y值(包括小球和小棍)
function get_deeps() {
var i, sm_all = sm+sm_gun;
deeps = new Array(sm);
// 提取小球的y值并记录其编号
for (i=0; i<sm; i++) {
deeps[i] = new Object();
deeps[i].num = i;
deeps[i].y = dot[i].y;
}
// 提取小棍的y值并记录其编号
for (i=sm; i<sm_all; i++) {
deeps[i] = new Object();
// 为节约空间,将小棍的编号+1000用以与小球相区分,而不再另作标记
deeps[i].num = (i-sm)+1000;
deeps[i].y = gun[i-sm].oy;
}
}
//
// 从最大深度开始,显示每个小球或小棍
function get_v() {
// 根据各小球坐标值计算棍的坐标值(包括起点和终点)
gun_js();
// 透视(将物体投影到xz平面,视点离zx面为200像素).
v_xz(200);
// 提取深度值,即y值,放入数组vdot中
get_deeps();
var i, a, j = 5;
var min_y, min_n, min_m;
// 当还有1个以上未显示时
while (deeps.length>0) {
// 先假设第一个就是最小y值(即最大深度)
min_y = deeps[0].y;
min_n = 0;
min_m = deeps[0].num;
// 找出未显示的球或棍中,y值最小的
for (i=1; i<deeps.length; i++) {
if (deeps[i].y<min_y) {
min_y = deeps[i].y;
min_n = i;
min_m = deeps[i].num;
}
}
// 显示找到的y值最小的球或棍(编号小于1000的都是球,否则就是棍)
if (min_m<1000) {
a = this.attachMovie("dot", "dot"+min_m, j++);
a._x = dot[min_m].vx+ox;
a._y = dot[min_m].vy+oy;
a._xscale = 100-(200-dot[min_m].y)/10;
a._yscale = a._xscale;
// 制造雾气效果
a.wuqi._alpha = (200-dot[min_m].y)/5;
// dot[i][5]保存的是颜色信息
a.gotoAndStop(dot[min_m].col);
} else {
min_m -= 1000;
// 创建一个空MC,并画一直线作为小棍
a = this.createEmptyMovieClip("gun"+min_m, j++);
a.lineStyle(3, 0xaaaaaa, 50);
a.moveTo((gun[min_m].svx+ox), (gun[min_m].svy+oy));
a.lineTo((gun[min_m].evx+ox), (gun[min_m].evy+oy));
}
// 从vdot中删去最小点,以便于下一轮寻找min点
deeps.splice(min_n, 1);
}
}
//
//
// 清除所有小球和小棍
function cl_all() {
var i,a;
for (i=0; i<sm; i++) {
a = this["dot"+i];
removeMovieClip(a);
}
for (i=0; i<sm_gun; i++) {
a = this["gun"+i];
removeMovieClip(a);
}
}
//
// 主调程序段:
this.onEnterFrame = function() {
// 绕x轴的转角
theta_x = (_ymouse-oy)/20;
// 绕z轴的转角
theta_z = (_xmouse-ox)/20;
// 旋转物体
rotate(theta_x, theta_z);
// 显示
get_v();
};
//
可能不少人觉得上面的代码很吓人,不要急。// 原点坐标
ox = 200;
oy = 200;
// 取得点集
get_data(3);
// 小棍的起点,终点,深度计算
function gun_js() {
var i, a, b, ar, br, dxy, dxz;
var ax, ay, az, bx, by, bz, dx, dy, dz;
for (i=0; i<sm_gun; i++) {
a = gun[i].dot_s;
ar = dot[a].r;
ax = dot[a].x;
ay = dot[a].y;
az = dot[a].z;
//
b = gun[i].dot_e;
br = dot[b].r;
bx = dot[b].x;
by = dot[b].y;
bz = dot[b].z;
//
dx = bx-ax;
dy = by-ay;
dz = bz-az;
// dxy,dxz分别是a,b投影到xy,xz面上后的距离
dxy = Math.sqrt(dx*dx+dy*dy);
dxz = Math.sqrt(dx*dx+dz*dz);
// 绘图时的起点坐标
gun[i].sx = (dxy-br)*dx/dxy+ax;
gun[i].sy = (dxy-br)*dy/dxy+ay;
gun[i].sz = (dxz-br)*dz/dxz+az;
// 绘图时的终点坐标
gun[i].ex = -(dxy-ar)*dx/dxy+bx;
gun[i].ey = -(dxy-ar)*dy/dxy+by;
gun[i].ez = -(dxz-ar)*dz/dxz+bz;
// 小棍中点处的y值,用以比较深度
gun[i].oy = (ay+by)/2;
}
}
//
// 缩放函数,s为缩放系数
function scale(s) {
var i;
for (i=0; i<sm; i++) {
dot[i].x *= s;
dot[i].y *= s;
dot[i].z *= s;
}
}
// 物体旋转,xa,ya均为角度值
function rotate(xa, ya) {
var i, k;
var rad = Math.PI/180;
xa *= rad;
ya *= rad;
var sin_xa = Math.sin(xa);
var cos_xa = Math.cos(xa);
var sin_ya = Math.sin(ya);
var cos_ya = Math.cos(ya);
var px, py, pz, tempz;
for (i=0; i<sm; i++) {
px = dot[i].x;
py = dot[i].y;
pz = dot[i].z;
tempz = (py*cos_ya)-(px*sin_ya);
dot[i].x = (py*sin_ya)+(px*cos_ya);
dot[i].y = (pz*sin_xa)+(tempz*cos_xa);
dot[i].z = (pz*cos_xa)-(tempz*sin_xa);
}
}
// 将物体投影到xz平面,d为视点到投影面的距离
function v_xz(d) {
var i, k;
// 处理小球坐标
for (i=0; i<sm; i++) {
k = 1-dot[i].y/d;
dot[i].vx = dot[i].x/k;
dot[i].vy = -dot[i].z/k;
}
//
// 处理小棍坐标
for (i=0; i<sm_gun; i++) {
// 小棍起点
k = 1-gun[i].sy/d;
gun[i].svx = gun[i].sx/k;
gun[i].svy = -gun[i].sz/k;
// 小棍终点
k = 1-gun[i].ey/d;
gun[i].evx = gun[i].ex/k;
gun[i].evy = -gun[i].ez/k;
}
}
// 提取深度值,即y值(包括小球和小棍)
function get_deeps() {
var i, sm_all = sm+sm_gun;
deeps = new Array(sm);
// 提取小球的y值并记录其编号
for (i=0; i<sm; i++) {
deeps[i] = new Object();
deeps[i].num = i;
deeps[i].y = dot[i].y;
}
// 提取小棍的y值并记录其编号
for (i=sm; i<sm_all; i++) {
deeps[i] = new Object();
// 为节约空间,将小棍的编号+1000用以与小球相区分,而不再另作标记
deeps[i].num = (i-sm)+1000;
deeps[i].y = gun[i-sm].oy;
}
}
//
// 从最大深度开始,显示每个小球或小棍
function get_v() {
// 根据各小球坐标值计算棍的坐标值(包括起点和终点)
gun_js();
// 透视(将物体投影到xz平面,视点离zx面为200像素).
v_xz(200);
// 提取深度值,即y值,放入数组vdot中
get_deeps();
var i, a, j = 5;
var min_y, min_n, min_m;
// 当还有1个以上未显示时
while (deeps.length>0) {
// 先假设第一个就是最小y值(即最大深度)
min_y = deeps[0].y;
min_n = 0;
min_m = deeps[0].num;
// 找出未显示的球或棍中,y值最小的
for (i=1; i<deeps.length; i++) {
if (deeps[i].y<min_y) {
min_y = deeps[i].y;
min_n = i;
min_m = deeps[i].num;
}
}
// 显示找到的y值最小的球或棍(编号小于1000的都是球,否则就是棍)
if (min_m<1000) {
a = this.attachMovie("dot", "dot"+min_m, j++);
a._x = dot[min_m].vx+ox;
a._y = dot[min_m].vy+oy;
a._xscale = 100-(200-dot[min_m].y)/10;
a._yscale = a._xscale;
// 制造雾气效果
a.wuqi._alpha = (200-dot[min_m].y)/5;
// dot[i][5]保存的是颜色信息
a.gotoAndStop(dot[min_m].col);
} else {
min_m -= 1000;
// 创建一个空MC,并画一直线作为小棍
a = this.createEmptyMovieClip("gun"+min_m, j++);
a.lineStyle(3, 0xaaaaaa, 50);
a.moveTo((gun[min_m].svx+ox), (gun[min_m].svy+oy));
a.lineTo((gun[min_m].evx+ox), (gun[min_m].evy+oy));
}
// 从vdot中删去最小点,以便于下一轮寻找min点
deeps.splice(min_n, 1);
}
}
//
//
// 清除所有小球和小棍
function cl_all() {
var i,a;
for (i=0; i<sm; i++) {
a = this["dot"+i];
removeMovieClip(a);
}
for (i=0; i<sm_gun; i++) {
a = this["gun"+i];
removeMovieClip(a);
}
}
//
// 主调程序段:
this.onEnterFrame = function() {
// 绕x轴的转角
theta_x = (_ymouse-oy)/20;
// 绕z轴的转角
theta_z = (_xmouse-ox)/20;
// 旋转物体
rotate(theta_x, theta_z);
// 显示
get_v();
};
//
其实我在一年前对3d的东东也是一无所知,后来看到有人用Flash搞分子结构式,于是我也开始构思,谁让我是搞化学的呢,看了些图形学的书,可是数学基础不好,看了也不太明了,直到前几天,总算勉强把程序搞出来了,至今都还有些地方不太明了,最开始只有小球,不知道怎么解决小棍的消隐问题,后来又慢慢改进,扩充功能,最后就变成这个样子了。最开始时照着书上的方法用矩阵变换做,后来觉得矩阵计算效率低,就直接用变换公式。对于消隐,书上是将深度进行排序,而我是找出深度最大值,用完后即刻删除,好在Flash里面的"数组"不是什么真数组,元素是可以删除的。另外数据的结构也是经过了多次修改和扩充,现在觉得有些太浪费空间,也懒得管了。
综上,这个程序不是一步到位的,是走了很多弯路才实现的,看不懂的人不要急,先理解大致思路,然后逐步细分,各个击破。
扩充功能
1。提供3种显示方式:V1--鼠标跟随,V2--绕Z轴匀速旋转,V3--固定不动
2。可以进行编辑操作:点击小球可选中小球,点击右下角的空白按钮可取消选择。在输入框中输入数据,然后点击一个操作按钮:dx,dy,dz分别是将小球的x,y,z值加上输入值;X,Y,Z分别是将小球的x,y,z值直接变为输入值;Rx,Rz则是分别将输入值作为x,z轴的旋转角。
点击浏览该文件 源文件:
点击浏览该文件优化之后
对消隐部分作了根本性的改变,使执行效率有大大提高。另外,小棍也添加了雾化效果。 源文件(2004版)中有很详细的注释。
点击浏览该文件
点击浏览该文件
