足球碳分子的搭建

效果图如下:




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;
	}
	
}





posted @ 2015-04-18 21:45  Anzhongliu  阅读(319)  评论(0编辑  收藏  举报