import java.lang.Math.*;
import java.awt.Graphics;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.awt.BorderLayout;
import java.awt.GraphicsConfiguration;
import java.awt.event.WindowAdapter;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.geometry.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.behaviors.mouse.*;
import com.sun.j3d.utils.behaviors.keyboard.*;
import com.sun.j3d.utils.behaviors.picking.*;
public class molecule1 extends Applet implements ActionListener, Runnable {
Thread timer;
Transform3D globalTransform3D;
Transform3D globalTransform3DBackup;
TransformGroup globalTransformGroup;
SharedGroup shared;
boolean timerFlag=true;
Group group;
double radius=0.2;
float angle = 0.0f;
int currentLink=0, currentVert=0;
int prevLink=0, prevVert=0;
boolean activateState=true;
Button activateB = new Button("Stop/Move");
Button normalB = new Button("Normal");
Button rotateBX = new Button("RotateX");
Button rotateBY = new Button("RotateY");
Button rotateBZ = new Button("RotateZ");
class Links_c {
double x0, y0, z0; // 起始坐标
int numVert; // 需要创建的分子数
boolean dx,dy,dz;
// 如果dx是true,则向X方向扩展;否则向-X方向扩展。dy,dz类似。
double probability;
// 按dx,dy,dz规定的方向扩展的概率,取值范围必须在[0, 1]之间。
// probability取值为0.5时,最易聚簇; 为1或0时,最易散开
// 只不过为1时,是按指定的方向散开;为0时,是按相反的方向散开
public void init(double ix0,double iy0,double iz0,int inumVert,
boolean idx,boolean idy,boolean idz, double iprobability)
{
x0=ix0; y0=iy0; z0=iz0; numVert=inumVert;
dx=idx; dy=idy; dz=idz; probability=iprobability;
}
};
Links_c links[];
int numLink;
Point3d pointPosition[][];
Transform3D pointTransform3D[][];
TransformGroup pointTransformGroup[][];
// 以一种随机和加方向性控制的方式,生成数条分子链的每个分子的坐标
//
private void createVert()
{
double x,y,z, p;
boolean dx,dy,dz;
// 此处应该和links一起改,否则会出错
numLink=3;
links =new Links_c[numLink];
for (int i=0; i<numLink; i++)
links[i] = new Links_c();
// 此处应该和numLink一起改,否则会出错
links[0].init(-0.5, -0.5, 0.2, 28, false, false, true, 0.6);
links[1].init(0.6, -0.6, 0, 22, true, true, false, 0.8);
links[2].init(0, 0.5, -0.2, 26, false, true, false, 0.65);
pointPosition = new Point3d[numLink][];
for (int l=0; l<numLink; l++)
{
pointPosition[l]=new Point3d[links[l].numVert];
for (int i=0; i<links[l].numVert; i++)
pointPosition[l][i]=new Point3d();
pointPosition[l][0].x=links[l].x0;
pointPosition[l][0].y=links[l].y0;
pointPosition[l][0].z=links[l].z0;
p=links[l].probability;
dx=links[l].dx;
dy=links[l].dy;
dz=links[l].dz;
for (int i=1;i<links[l].numVert;i++)
{
x=Math.random()*radius;
x=sign(x,p,dx);
y=Math.sqrt(radius*radius-x*x) * Math.random();
// Y的值不能也在0-radius范围内随意取值:
// 因为在X、Y平面上,当X、Y都取radius时,
// 点(X,Y)就跑到了圆x^2+y^2=r^2的外面了。
// 所以X、Y坐标必须满足这个约束。
y=sign(y,p,dy);
z=Math.sqrt(radius*radius-x*x-y*y);
z=sign(z,p,dz);
pointPosition[l][i].x=pointPosition[l][i-1].x+x;
pointPosition[l][i].y=pointPosition[l][i-1].y+y;
pointPosition[l][i].z=pointPosition[l][i-1].z+z;
}
}
}
// 依次改变所有分子链中每个分子的坐标(每次改一个)
private void changeAVert()
{
double x,y,z, p;
boolean dx,dy,dz;
int l=currentLink;
int i=currentVert;
prevLink=l;
prevVert=i;
i++;
if (i==links[l].numVert) // 本条链结束,下次从下条链开始
{
i=0;
l++;
}
if (l==numLink) // 所有链结束,重新开始一轮
{
l=0;
}
currentLink=l;
currentVert=i;
p=links[l].probability;
dx=links[l].dx;
dy=links[l].dy;
dz=links[l].dz;
if (i==0)
{
pointPosition[l][0].x=links[l].x0;
pointPosition[l][0].y=links[l].y0;
pointPosition[l][0].z=links[l].z0;
} else
{
x=Math.random()*radius;
x=sign(x,p,dx);
y=Math.sqrt(radius*radius-x*x) * Math.random();
// Y的值不能也在0-radius范围内随意取值:
// 因为在X、Y平面上,当X、Y都取radius时,
// 点(X,Y)就跑到了圆x^2+y^2=r^2的外面了。
// 所以X、Y坐标必须满足这个约束。
y=sign(y,p,dy);
z=Math.sqrt(radius*radius-x*x-y*y);
z=sign(z,p,dz);
pointPosition[l][i].x=pointPosition[l][i-1].x+x;
pointPosition[l][i].y=pointPosition[l][i-1].y+y;
pointPosition[l][i].z=pointPosition[l][i-1].z+z;
}
}
private double sign(double v, double p, boolean direction)
{
double ret;
if (p>=Math.random()) // 以给定的概率遵照扩展方向的约定
{
if (direction)
ret=v;
else
ret=-v;
} else // 落在规定的概率之外,则与扩展的方向相反
{
if (direction)
ret=-v;
else
ret=v;
}
return ret;
}
// 将一个坐标系中的任意物体旋向(x,y,z)指定的任意方向(坐标原点不变)
//
// 输入:给定旧坐标系中的一点A,它的坐标为(x,y,z)。
// 输出:两个坐标系之间的转换矩阵(OXYZ-->OX"Y"Z")
// 要求:旋转后OA为新坐标系中的Y"轴。(如此旋转后,原来与Y轴平行的物体,
// 将与Y"轴平行,其他位置的物体也将作相应的非扭曲旋转)
// 旋转方法:
// 假设OA在ZOX平面的投影为OA'。ZOA'夹角为angle1。则第一步旋转为:
// 以Y轴为转轴,将ZOX平面旋转angle1度,此时坐标系变为OX'YZ'(A'
// 处于Z'轴上了);
// 由于X'垂直于YOZ'平面(X'亦垂直于OA及OA')。假设YOA的夹角为angle2,
// 则第二步旋转为:以X'为轴,将YOZ'平面旋转angle2度,则此时Y轴与OA
// 轴重合,记为Y",另外记新的Z坐标为Z",还有X"(是与X'重合的)。
// 经过上述两次坐标轴旋转,坐标系中的物体也就转向了指定的任意方向。
private Transform3D transForm(double x, double y,double z)
{
double r = Math.sqrt(x*x+y*y+z*z);
double angle1 = Math.atan(x/z);
double angle2 = Math.acos(y/r);
//
// angle1 调整旋转的角度:
//
// Angle1是从+Z向Z'旋转的实际角度(范围:0-360度),Z'是(x,y,z)点在XOZ平面的投影线。
// Z X Angle1
// ------------------------------------
// +(or 0) +(or 0) angle1 (angle1此时为正)
// +(or 0) - 2*PI+angle1 (angle1此时为负)
// - +(or 0) PI+angle1 (angle1此时为负)
// - - PI+angle1 (angle1此时为正)
double Angle1,Angle2;
if (z>=0)
{
if (x>=0)
Angle1=angle1;
else
Angle1=2*Math.PI+angle1;
} else
{
if (x>=0)
Angle1=Math.PI+angle1;
else
Angle1=Math.PI+angle1;
}
//
// angle2 调整旋转的角度:
//
// Angle2 是从+Y向Z'旋转的实际角度(范围:0-180度)
// Y Angle2
// ---------------------------------
// +(or 0) angle2 (angle2此时为正)
// - angle2 (angle2此时为负)
if (y>=0)
Angle2=angle2;
else
Angle2=angle2;
Transform3D rot1 = new Transform3D();
Transform3D rot2 = new Transform3D();
rot1.rotY(Angle1);
rot2.rotX(Angle2);
Transform3D newTransform = new Transform3D();
newTransform.mul(rot1);
newTransform.mul(rot2);
return newTransform;
}
private Group moleculeLinks()
{
group=new Group();
createVert();
shared = new SharedGroup();
shared.addChild(aMolecule());
pointTransform3D = new Transform3D[numLink][];
pointTransformGroup = new TransformGroup[numLink][];
for (int l=0; l<numLink; l++)
{
pointTransform3D[l]=new Transform3D[links[l].numVert];
pointTransformGroup[l]=new TransformGroup[links[l].numVert];
for (int i=0;i<links[l].numVert; i++)
{
if (i==links[l].numVert-1)
{
pointTransform3D[l][i] = transForm(
pointPosition[l][i-1].x - pointPosition[l][i].x,
pointPosition[l][i-1].y - pointPosition[l][i].y,
pointPosition[l][i-1].z - pointPosition[l][i].z
);
} else
{
pointTransform3D[l][i] = transForm(
pointPosition[l][i+1].x - pointPosition[l][i].x,
pointPosition[l][i+1].y - pointPosition[l][i].y,
pointPosition[l][i+1].z - pointPosition[l][i].z
);
}
pointTransform3D[l][i].setTranslation(new Vector3d(
pointPosition[l][i].x, pointPosition[l][i].y, pointPosition[l][i].z));
pointTransformGroup[l][i] = new TransformGroup(pointTransform3D[l][i]);
pointTransformGroup[l][i].setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
pointTransformGroup[l][i].addChild(new Link(shared));
group.addChild(pointTransformGroup[l][i]);
}
}
return group;
}
private void moleculeActivate()
{
int l=prevLink;
int i=prevVert;
if (i==links[l].numVert-1)
{
pointTransform3D[l][i] = transForm(
pointPosition[l][i-1].x - pointPosition[l][i].x,
pointPosition[l][i-1].y - pointPosition[l][i].y,
pointPosition[l][i-1].z - pointPosition[l][i].z
);
} else
{
pointTransform3D[l][i] = transForm(
pointPosition[l][i+1].x - pointPosition[l][i].x,
pointPosition[l][i+1].y - pointPosition[l][i].y,
pointPosition[l][i+1].z - pointPosition[l][i].z
);
}
pointTransform3D[l][i].setTranslation(new Vector3d(pointPosition[l][i].x,
pointPosition[l][i].y, pointPosition[l][i].z));
pointTransformGroup[l][i].setTransform( pointTransform3D[l][i] );
changeAVert(); // 先显示,再改变坐标,可以不用记住改变之前的位置
}
public BranchGroup createSceneGraph(Canvas3D c) {
BranchGroup objRoot = new BranchGroup();
BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
// 顶板和底板颜色及材质
Appearance ap0 = new Appearance();
Material mat0 = new Material();
mat0.setDiffuseColor(new Color3f(0.5f,0.1f,0.5f));
ap0.setMaterial(mat0);
// 上下垂直坐标轴的颜色及材质
Appearance ap2 = new Appearance();
Material mat2 = new Material();
mat2.setDiffuseColor(new Color3f(0/255f,0/255f,180/255f));
ap2.setMaterial(mat2);
// 左右水平标轴的颜色及材质
Appearance ap3 = new Appearance();
Material mat3 = new Material();
mat3.setDiffuseColor(new Color3f(0/255f,100/255f,125/255f));
ap3.setMaterial(mat3);
// 垂直屏幕的坐标轴的颜色及材质
Appearance ap4 = new Appearance();
Material mat4 = new Material();
mat4.setDiffuseColor(new Color3f(100/255f,0/255f,125/255f));
ap4.setMaterial(mat4);
// 背景
Color3f bgColor = new Color3f(20/255f, 100/255f, 70/255f);
Background bg = new Background(bgColor);
bg.setApplicationBounds(bounds);
objRoot.addChild(bg);
// 灯光
// Set up the global lights
Color3f lColor1 = new Color3f(1f, 1f, 1f);
Vector3f lDir1 = new Vector3f(-1.0f, -1.0f, -1.0f);
DirectionalLight lgt1 = new DirectionalLight(lColor1, lDir1);
lgt1.setInfluencingBounds(bounds);
objRoot.addChild(lgt1);
Color3f DirectionalColor = new Color3f(1.f, 1.f, 1.f);
Vector3f vec = new Vector3f( 0.f, 1.f, -1.0f );
DirectionalLight DirectionalLight= new DirectionalLight(DirectionalColor,vec);
DirectionalLight.setInfluencingBounds(bounds);
objRoot.addChild(DirectionalLight);
globalTransform3D = new Transform3D();
globalTransform3D.rotX(0.3);
globalTransform3DBackup=new Transform3D(globalTransform3D);
globalTransformGroup = new TransformGroup(globalTransform3D);
globalTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
globalTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
objRoot.addChild(globalTransformGroup);
// 顶板
Transform3D t = new Transform3D();
t.setTranslation(new Vector3f(0f,1.5f,0f));
TransformGroup gb1 = new TransformGroup(t);
Box box = new Box(1.5f,0.02f,1.2f,ap0);
gb1.addChild(box);
globalTransformGroup.addChild(gb1);
// 底板
t = new Transform3D();
t.setTranslation(new Vector3f(0f,-1.5f,0f));
TransformGroup gb2 = new TransformGroup(t);
box = new Box(1.5f,0.02f,1.2f,ap0);
gb2.addChild(box);
globalTransformGroup.addChild(gb2);
// 中心垂直轴
globalTransformGroup.addChild(new Cylinder(0.03f, 3f, ap2));
// 从右到左坐标轴
t = new Transform3D();
t.rotZ(Math.PI/2);
TransformGroup gb3 = new TransformGroup(t);
gb3.addChild(new Cylinder(0.03f, 3f, ap3));
globalTransformGroup.addChild(gb3);
t = new Transform3D();
t.rotZ(Math.PI/2);
t.setTranslation(new Vector3f(-1.5f,0f,0f));
TransformGroup gb4 = new TransformGroup(t);
gb4.addChild(new Cone(0.05f, 0.12f, Primitive.GENERATE_NORMALS, ap3));
globalTransformGroup.addChild(gb4);
// 从内到外坐标轴
t = new Transform3D();
t.rotX(Math.PI/2);
TransformGroup gb5 = new TransformGroup(t);
gb5.addChild(new Cylinder(0.03f, 3f, ap4));
globalTransformGroup.addChild(gb5);
t = new Transform3D();
t.rotX(Math.PI/2);
t.setTranslation(new Vector3f(0f,0f,1.5f));
TransformGroup gb6 = new TransformGroup(t);
gb6.addChild(new Cone(0.05f, 0.12f, Primitive.GENERATE_NORMALS, ap4));
globalTransformGroup.addChild(gb6);
globalTransformGroup.addChild(moleculeLinks());
MouseRotate behavior = new MouseRotate();
behavior.setTransformGroup(globalTransformGroup);
behavior.setSchedulingBounds(bounds);
objRoot.addChild(behavior);
MouseZoom behavior2 = new MouseZoom();
behavior2.setTransformGroup(globalTransformGroup);
behavior2.setSchedulingBounds(bounds);
objRoot.addChild(behavior2);
MouseTranslate behavior3 = new MouseTranslate();
behavior3.setTransformGroup(globalTransformGroup);
behavior3.setSchedulingBounds(bounds);
objRoot.addChild(behavior3);
KeyNavigatorBehavior key=new KeyNavigatorBehavior(globalTransformGroup);
key.setSchedulingBounds(bounds);
objRoot.addChild(key);
objRoot.compile();
return objRoot;
}
Group aMolecule(){
// 分子小球颜色及材质
Appearance ap11 = new Appearance();
Material mat11 = new Material();
mat11.setDiffuseColor(new Color3f(0.7f,0.8f,0.2f));
ap11.setMaterial(mat11);
// 分子杆
Appearance ap12 = new Appearance();
Material mat12 = new Material();
mat12.setDiffuseColor(new Color3f(0.3f,0.2f,0.4f));
ap12.setMaterial(mat12);
Group group = new Group();
Transform3D t1 = new Transform3D();
TransformGroup g1 = new TransformGroup(t1);
g1.addChild(new Sphere(0.08f,ap11));
t1=new Transform3D();
t1.setTranslation(new Vector3f(0f,0.14f,0f));
TransformGroup g2 = new TransformGroup(t1);
g2.addChild(new Cylinder(0.03f,0.12f,ap11));
group.addChild(g1);
group.addChild(g2);
return group;
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == activateB){
if (activateState)
{
stop();
activateState=false;
} else
{
start();
activateState=true;
}
}
if (e.getSource() == normalB){
globalTransform3D.set(globalTransform3DBackup);
globalTransformGroup.setTransform(globalTransform3D);
}
if (e.getSource() == rotateBX){
angle += Math.toRadians(-15.0);
globalTransform3D.rotX(angle);
globalTransformGroup.setTransform(globalTransform3D);
}
if (e.getSource() == rotateBY){
angle += Math.toRadians(30.0);
globalTransform3D.rotY(angle);
globalTransformGroup.setTransform(globalTransform3D);
}
if (e.getSource() == rotateBZ){
angle += Math.toRadians(30.0);
globalTransform3D.rotZ(angle);
globalTransformGroup.setTransform(globalTransform3D);
}
}
public void start() {
timerFlag=true;
timer = new Thread(this);
timer.start();
}
public void stop() {
timerFlag=false;
timer.interrupt();
timer = null;
}
public void destroy() {
}
public void run() {
while (timer != null && timerFlag) {
try{
if (timerFlag) moleculeActivate();
Thread.sleep(180);
} catch (InterruptedException e) {return;}
}
}
public molecule1() {
setLayout(new BorderLayout());
Canvas3D c = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
add("Center", c);
Panel p = new Panel();
p.add(activateB);
p.add(normalB);
p.add(rotateBX);
p.add(rotateBY);
p.add(rotateBZ);
add("South", p);
activateB.addActionListener(this);
normalB.addActionListener(this);
rotateBX.addActionListener(this);
rotateBY.addActionListener(this);
rotateBZ.addActionListener(this);
Viewer viewer = new Viewer(c);
Vector3d viewpoint = new Vector3d(0.0, 0.0, 7.0); //初始观察点位置
Transform3D t = new Transform3D();
t.set(viewpoint);
ViewingPlatform v = new ViewingPlatform( );
v.getViewPlatformTransform().setTransform(t);
BranchGroup scene = createSceneGraph(c);
SimpleUniverse u = new SimpleUniverse(v, viewer);
u.getViewingPlatform();
u.addBranchGraph(scene);
}
public static void main(String[] args) {
new MainFrame(new molecule1(), 800, 540);
}
}