足球碳分子的搭建
效果图如下:
1、首先给出的是用于搭建正多边形的RegularPolygon类:
import java.util.ArrayList;
import java.util.List;
public class RegularPolygon {
int vCount=0;
int iCount=0;
float length;
int borderCount;
List<Float> verticesList;//多边形的顶点数据
double[][] vectors;//每条便的方向向量,每条边的起点的索引与该边方向向量的索引一致
double[] initVector;//保存初始的方向向量
double[] pivot;//旋转轴
int[] vertices;//记录要绘制的球的索引
int[] borders;//记录要绘制的圆管的索引
List<RegularPolygon> children;
MySurfaceView mv;
public RegularPolygon(MySurfaceView mv,
int borderCount, //圆管的编号
double angle, //旋转的角度
float length, //长度
double[] initPoint, //初始的点
double[] initVector,//初始向量
double[] pivot, //旋转轴
int[] vertices, //绘制球的索引
int[] borders //绘制圆管的索引
){
this.mv=mv;
this.borderCount=borderCount;
this.length=length;
this.vectors = new double[borderCount][3];
this.initVector=initVector;
this.vertices=vertices;
this.borders=borders;
this.pivot = pivot;//父对象的旋转轴,本对象的旋转轴在父对象的旋转轴的基础上旋转得到
children = new ArrayList<RegularPolygon>();
this.verticesList = Utils.getRegularPentagonVertexData(
initPoint, initVector, length,angle,vectors,borderCount,pivot);
}
public void drawSelf(float xOffset,float yOffset)
{
//绘制顶点
for(int i=0;i<vertices.length;i++){
int index = vertices[i];
float x = verticesList.get(3*index);
float y = verticesList.get(3*index+1);
float z = verticesList.get(3*index+2);
MatrixState.pushMatrix();
//移动到顶点的位置,绘制球
MatrixState.translate(x, y, z);
mv.ball.drawSelf();
MatrixState.popMatrix();
}
//绘制圆管
for(int i=0;i<borders.length;i++){
int index = borders[i];
//获取圆管的起点坐标
float x = verticesList.get(3*index);
float y = verticesList.get(3*index+1);
float z = verticesList.get(3*index+2);
//获取圆管的向量
double[] vector = vectors[index];
MatrixState.pushMatrix();
//首先移动到起点
MatrixState.translate(x, y, z);
MatrixState.pushMatrix();
Utils.moveXToSomeVector(vector); //x轴变换到指定向量的坐标系
MatrixState.translate(Constant.LENGTH/2, 0, 0);
mv.stick.drawSelf(); //绘制木棒
MatrixState.popMatrix();
MatrixState.popMatrix();
}
drawChildren( xOffset, yOffset); //绘制
}
public RegularPolygon buildChild(
int borderCount, //圆管的数量
double angle, //旋转角度
int position,
int[] vertices, //球索引
int[] borders //圆管索引
){
double[] initPoint = new double[3];
for(int i=0;i<3;i++){
initPoint[i]=verticesList.get(3*position+i);
}
double[] initVector = vectors[position];
double[] tempPivot = Utils.copyVecor(this.pivot);
RegularPolygon child = new RegularPolygon(this.mv,
borderCount, angle, length, initPoint, initVector,tempPivot,vertices,borders);
children.add(child);
return child;
}
private void drawChildren(float xOffset,float yOffset){
for(int i=0;i<children.size();i++){
RegularPolygon child = children.get(i);
MatrixState.pushMatrix();
child.drawSelf(xOffset, yOffset);
MatrixState.popMatrix();
}
}
}
2、接着给出的是MysurfaceView类中进行初始化工作的onSurfaceCreat方法:
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//设置屏幕背景色RGBA
GLES20.glClearColor(0.9f,0.9f,0.9f, 1.0f);
//启用深度测试
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
//设置为打开背面剪裁
GLES20.glEnable(GLES20.GL_CULL_FACE);
//初始化变换矩阵
MatrixState.setInitStack();
float[] colorValue = {1,0,0,1}; //创建颜色数组
ball = new Ball(MySurfaceView.this,Constant.BALL_R,colorValue);//创建球对象
colorValue = new float[]{1,1,0,1};
stick = new Stick(MySurfaceView.this,Constant.LENGTH,Constant.R,Constant.ANGLE_SPAN,colorValue);//创建圆管对象
double[] initPoint = Utils.getFirstPoint(Constant.LENGTH);//得到第一个五边形左下点的坐标
double[] initVector={1,0,0,1}; //初始化方向向量
double[] zPivot = {0,0,1,1}; //以z轴为旋转轴
int[] vertices = {0,1,2,3,4}; //球的索引
int[] borders = {0,1,2,3,4}; //圆管的索引
first = new RegularPolygon(MySurfaceView.this, 5,72 ,
Constant.LENGTH, initPoint, initVector,zPivot,vertices,borders);//1
vertices = new int[]{2,3,4}; //球的索引
borders = new int[]{1,2,3,4}; //圆管的索引
RegularPolygon rp2 = first.buildChild( 6, -60,1,vertices,borders);//2
vertices = new int[]{2,3,4,5};
borders = new int[]{1,2,3,4,5};
RegularPolygon rp4 = rp2.buildChild( 6, 60,3,vertices,borders);//4
vertices = new int[]{};
borders = new int[]{1,5};
rp4.buildChild( 6, -60,2,vertices,borders);//5
vertices = new int[]{2};
borders = new int[]{1,2};
RegularPolygon rp6 = rp4.buildChild( 5, -72,3,vertices,borders);//6
vertices = new int[]{3,4,5};
borders = new int[]{2,3,4,5};
rp6.buildChild( 6, 60,2,vertices,borders);//7
}3、下面给出的是一些进行旋转及向量计算的工具类Utils:
import java.util.ArrayList;
import java.util.List;
public class Utils {
static boolean first=true;
//将一个向量规格化的方法
public static float[] normalizeVector(float x, float y, float z){
float mod=module(x,y,z);
return new float[]{x/mod, y/mod, z/mod};//返回规格化后的向量
}
//求向量的模的方法
public static float module(float x, float y, float z){
return (float) Math.sqrt(x*x+y*y+z*z);
}
public static double[] nRotate(
double angle, //旋转角度
double n[], //旋转轴
double gVector[] //旋转向量
){
angle = Math.toRadians(angle);
double[][] matrix=//绕任意轴旋转变换矩阵
{
{n[0]*n[0]*(1-Math.cos(angle))+Math.cos(angle),n[0]*n[1]*(1-Math.cos(angle))+n[2]*Math.sin(angle),n[0]*n[2]*(1-Math.cos(angle))-n[1]*Math.sin(angle),0},
{n[0]*n[1]*(1-Math.cos(angle))-n[2]*Math.sin(angle),n[1]*n[1]*(1-Math.cos(angle))+Math.cos(angle),n[1]*n[2]*(1-Math.cos(angle))+n[0]*Math.sin(angle),0},
{n[0]*n[2]*(1-Math.cos(angle))+n[1]*Math.sin(angle),n[1]*n[2]*(1-Math.cos(angle))-n[0]*Math.sin(angle),n[2]*n[2]*(1-Math.cos(angle))+Math.cos(angle),0},
{0,0,0,1}
};
double[] tempDot={gVector[0],gVector[1],gVector[2],gVector[3]};
for(int j=0;j<4;j++)
{
gVector[j]=(tempDot[0]*matrix[0][j]+tempDot[1]*matrix[1][j]+
tempDot[2]*matrix[2][j]+tempDot[3]*matrix[3][j]);
}
return gVector; //返回结果
}
//给出初始点,初始向量,边长,获取正多边形的顶点坐标
public static List<Float> getRegularPentagonVertexData(
double[] initPoint, //起点
double[] initVector, //初始向量(方向向量)
double length, //边长
double angle, //旋转角度
double[][] vectors, //保存上一条边的方向向量
int borderCount,
double[] pivot //旋转轴
){
List<Float> verticesList = new ArrayList<Float>(); //新建一个ArrayList
double[] startPoint=initPoint;//起点
double[] endPoint; //终点坐标
double[] vector = copyVecor(initVector);//复制第一条边的方向向量
int index=0;
double[] vectorS = copyVecor(vector); //将向量复制一份
vectors[index++]=vectorS; //将第一个向量保存
for(int i=0;i<initPoint.length;i++){ //将第一个点的坐标添加到list中
verticesList.add((float) initPoint[i]);
}
while(index<borderCount){ //循环计算其余的点的坐标
endPoint = new double[3];//创建当前线段的终点
//终点坐标等于起点加上长度与方向向量的点积
endPoint[0]=startPoint[0]+length*vector[0];//计算终点x
endPoint[1]=startPoint[1]+length*vector[1];//计算终点y
endPoint[2]=startPoint[2]+length*vector[2];//计算终点z
//如果计算出来的终点等于第一个点,则计算完毕,循环退出
if(
compare(endPoint[0],initPoint[0])==0 //调用compare方法进行比较
&& compare(endPoint[1],initPoint[1])==0
&& compare(endPoint[2],initPoint[2])==0
){
break;
}
for(int i=0;i<endPoint.length;i++){ //将终点的坐标添加到list中
float value = (float) endPoint[i];
if(Math.abs(value)<0.000001){
verticesList.add(new Float(0.0f));
continue;
}else{
verticesList.add((float) endPoint[i]);
}
}
//计算下一条边的方向向量
if(index==1){
vector = nRotate(angle,pivot,vector);//绕父对象的旋转轴生成第二个向量,与父对象在同一平面的
if(!first){//如果不是第一个多边形
double tempAngle = 39*angle/Math.abs(angle);//getDihedralAngle()*(angle/Math.abs(angle));
vector = nRotate(tempAngle,initVector,vector);//将第二个向量绕第一个向量旋转
pivot = nRotate(tempAngle,initVector,pivot);//生成新的旋转轴
}
first=false;
}else{
vector = nRotate(angle,pivot,vector);//将当前的方向向量旋转得到新的方向向量
}
vectorS = copyVecor(vector);//将向量复制一份
vectors[index++]=vectorS;//将新的向量保存
startPoint = endPoint;//将当前线段的终点作为下条线段的起点
}
return verticesList;
}
public static double[] copyVecor(double[] vector){ //复制数组中数组的方法
double[] copy = new double[vector.length];
for(int i=0;i<vector.length;i++){
copy[i]=vector[i];
}
return copy;
}
//比较两个数的方法
public static int compare(double x,double y){
if(Math.abs(x-y)<0.000001){
return 0;
}else if(x-y>0.000001){
return 1;
}else{
return -1;
}
}
//计算第一个点的坐标--五边形的左下角点,使第一个五边形中心为坐标原点
public static double[] getFirstPoint(
float length //正五边形的边长
){
double first[] = new double[3]; //正五边形坐下点坐标数组
first[0]=-length/2; //x坐标值
first[1]=-length/(2*Math.tan(Math.toRadians(36))); //y坐标值
first[2]=0; //由于在xy平面上,z自然为0
return first;
}
//求二面角--套结果公式
public static double getDihedralAngle(){
return Math.toDegrees(Math.acos(Math.sqrt(5)/3));
}
//计算两个向量的夹角--结果为度
public static double getAngleBetweenTwoVector(double[] vector1,double[] vector2){
double angle=0;
double DJ = vector1[0]*vector2[0]+vector1[1]*vector2[1]+vector1[2]*vector2[2];//计算点积
double mode = getMode(vector1)*getMode(vector2);//求向量模的积
double cosa = DJ/mode;
if(compare(cosa,1)==0){
return 0;
}
angle = Math.toDegrees(Math.acos(cosa));
return angle;
}
//求向量的模
public static double getMode(double[] vector){
return Math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]+vector[2]*vector[2]);
}
public static double[] getCJ(double[] v1,double[] v2){//计算叉积--v1叉乘v2
double[] result = new double[3];
result[0]=v1[1]*v2[2]-v1[2]*v2[1];
result[1]=v1[2]*v2[0]-v1[0]*v2[2];
result[2]=v1[0]*v2[1]-v1[1]*v2[0];
return result;
}
//变换坐标系--是x轴变换到指定向量的位置
public static void moveXToSomeVector(double[] vector){
double x[]={1,0,0};
double angle = getAngleBetweenTwoVector(x,vector);//vector与x轴的夹角
//通过x与vector的叉积计算出旋转轴的向量
double pivot[] = getCJ(x,vector); //调用求叉乘的方法
MatrixState.rotate((float)angle, (float)pivot[0], (float)pivot[1],(float)pivot[2]);
}
static List<Float> drawnVertices = new ArrayList<Float>();//已经绘制的点的坐标
public static boolean isExist(float x,float y,float z){
for(int i=0;i<drawnVertices.size()/3;i++){
float tempx = drawnVertices.get(3*i);
float tempy = drawnVertices.get(3*i+1);
float tempz = drawnVertices.get(3*i+2);
double[] tempp=new double[]{tempx,tempy,tempz};
double[] p = new double[]{x,y,z};
if(getDistanceSquare(tempp, p)<=0.2*0.2*4){
return true;
}
}
return false;
}
public static double getDistanceSquare(double[] p1,double[] p2){
return getSquare(p1[0]-p2[0])+getSquare(p1[1]-p2[1])+getSquare(p1[2]-p2[2]);
}
public static double getSquare(double x){
return x*x;
}
}

浙公网安备 33010602011771号