// Fractal3D.java // Implements a fractal 2D cloud. import java.awt.*; import java.util.*; public class Fractal3D extends Cloud { public Fractal3D(double od) { int i; data=new DataSet2D(3,3); data.SetSortOrder(DataSet2D.SORT_Y); for(i=0; i<3; i++) { data.AddElement(new Point2D(od,(double)i/2.0)); } match_data=new double[3]; for(i=0; i<3; i++) match_data[i]=0.0; od_grid=new double[GRID_SIZE][GRID_SIZE][GRID_SIZE]; MakeFractalGrid(); } public int Dimension() { return(3); } // The only requirement to use this ScatterAt function is that when the // last component of the vector is zero you are out of the cloud. public RTVector ScatterAt(RTVector r,RTVector dir) { RTVector ret=new RTVector(r),next; double tau,p; double density1,density2; dir.Normalize(); // First calculate an optical depth to use. p=Math.random(); if(p<1e-3) { if(dir.Get(ret.Dimension()-1)<0.0) { ret.Set(ret.Dimension()-1,0.0); } else { ret.Set(ret.Dimension()-1,1.01); } return(ret); } tau=-Math.log(p); // Now figure out where that is in the cloud. while(tau>0.0) { density1=this.OpticalDensity(ret); next=ret.Add(dir.Scale(0.01)); density2=this.OpticalDensity(next); if(tau<(density1+density2)*0.01) { ret=ret.Add(dir.Scale(0.01*tau/((density1+density2)*0.01))); if((!IsInCloud(ret)) && (dir.Get(ret.Dimension()-1)<0.0)) { ret.Set(ret.Dimension()-1,0.0); } return(ret); } else { tau-=(density1+density2)*0.01; ret=next; } if(!IsInCloud(ret)) { if(dir.Get(ret.Dimension()-1)<0.0) ret.Set(ret.Dimension()-1,0.0); return(ret); } } return(ret); } public double OpticalDepth() { int i; double ret=0; for(i=0; i=GRID_SIZE) xbin=GRID_SIZE-1; if(xbin<0) xbin=0; ybin=(int)(r.Get(1)*GRID_SIZE); if(ybin>=GRID_SIZE) ybin=GRID_SIZE-1; if(ybin<0) ybin=0; zbin=(int)(r.Get(2)*GRID_SIZE); if(zbin>=GRID_SIZE) zbin=GRID_SIZE-1; if(zbin<0) zbin=0; return(od_grid[zbin][xbin][ybin]); } public boolean IsInCloud(RTVector r) { return((r.Get(1)<=1.0) && (r.Get(1)>0.0) && (r.Get(0)<=1.0) && (r.Get(0)>0.0) && (r.Get(2)<=1.0) && (r.Get(2)>0.0)); } public boolean IsBelowCloud(RTVector r) { return(r.Get(2)<=0.0); } public RTVector RandomStartLocation(RTVector dir) { RTVector ret=new RTVector(2); ret.Set(0,0.5); ret.Set(1,0.5); ret.Set(2,1.0); return(ret); } public void DrawCloud(Panel p) { Graphics g=p.getGraphics(); int i,j,x1,y1,x2,y2; float white; MakeFractalGrid(); x1=0; for(i=0; i0) return(null); return(data); } private void MakeFractalGrid() { int i,j,k; boolean flag=false; double offset; int mid=GRID_SIZE/2,far=GRID_SIZE-1; for(i=0; i<3; i++) { try { if(data.GetElement(i).x!=match_data[i]) flag=true; match_data[i]=data.GetElement(i).x; } catch(DataSet.DataException e) {} } if(flag) { od_grid[0][0][0]=match_data[0]; od_grid[far][0][0]=match_data[0]; od_grid[0][far][0]=match_data[2]; od_grid[far][far][0]=match_data[2]; od_grid[0][0][far]=match_data[0]; od_grid[far][0][far]=match_data[0]; od_grid[0][far][far]=match_data[2]; od_grid[far][far][far]=match_data[2]; od_grid[mid][mid][mid]=match_data[1]; offset=(match_data[0]+match_data[1]+match_data[2])/2.0; od_grid[mid][0][mid]=(od_grid[0][0][0]+od_grid[far][0][0]+od_grid[0][0][far]+od_grid[far][0][far])/4.0+offset*(Math.random()-0.5); od_grid[mid][far][mid]=(od_grid[0][far][0]+od_grid[far][far][0]+od_grid[0][far][far]+od_grid[far][far][far])/4.0+offset*(Math.random()-0.5); od_grid[0][mid][mid]=(od_grid[0][0][0]+od_grid[0][far][0]+od_grid[0][0][far]+od_grid[0][far][far])/4.0+offset*(Math.random()-0.5); od_grid[far][mid][mid]=(od_grid[far][0][0]+od_grid[far][far][0]+od_grid[far][0][far]+od_grid[far][far][far])/4.0+offset*(Math.random()-0.5); od_grid[mid][mid][0]=(od_grid[0][0][0]+od_grid[0][far][0]+od_grid[far][0][0]+od_grid[far][far][0])/4.0+offset*(Math.random()-0.5); od_grid[mid][mid][far]=(od_grid[0][0][far]+od_grid[0][far][far]+od_grid[far][0][far]+od_grid[far][far][far])/4.0+offset*(Math.random()-0.5); RecursiveGridFill(0,0,0,mid,mid,mid,offset/2); RecursiveGridFill(0,0,mid,mid,mid,far,offset/2); RecursiveGridFill(0,mid,0,mid,far,mid,offset/2); RecursiveGridFill(0,mid,mid,mid,far,far,offset/2); RecursiveGridFill(mid,0,0,far,mid,mid,offset/2); RecursiveGridFill(mid,0,mid,far,mid,far,offset/2); RecursiveGridFill(mid,mid,0,far,far,mid,offset/2); RecursiveGridFill(mid,mid,mid,far,far,far,offset/2); for(i=0; i=x2-1) return; od_grid[midx][midy][midz]=(od_grid[x1][y1][z1]+od_grid[x1][y1][z2]+od_grid[x2][y2][z1]+od_grid[x2][y2][z2]+od_grid[x1][y2][z1]+od_grid[x1][y2][z2]+od_grid[x2][y1][z1]+od_grid[x2][y1][z2])/8.0+offset*(Math.random()-0.5); od_grid[midx][y1][midz]=(od_grid[x1][y1][z1]+od_grid[x1][y1][z2]+od_grid[x2][y1][z1]+od_grid[x2][y1][z2])/4.0+offset*(Math.random()-0.5); od_grid[midx][y2][midz]=(od_grid[x1][y2][z1]+od_grid[x1][y2][z2]+od_grid[x2][y2][z1]+od_grid[x2][y2][z2])/4.0+offset*(Math.random()-0.5); od_grid[x1][midy][midz]=(od_grid[x1][y1][z1]+od_grid[x1][y1][z2]+od_grid[x1][y2][z1]+od_grid[x1][y2][z2])/4.0+offset*(Math.random()-0.5); od_grid[x2][midy][midz]=(od_grid[x2][y1][z1]+od_grid[x2][y1][z2]+od_grid[x2][y2][z1]+od_grid[x2][y2][z2])/4.0+offset*(Math.random()-0.5); od_grid[midx][midy][z1]=(od_grid[x1][y1][z1]+od_grid[x2][y1][z1]+od_grid[x2][y2][z1]+od_grid[x2][y1][z1])/4.0+offset*(Math.random()-0.5); od_grid[midx][midy][z2]=(od_grid[x1][y1][z2]+od_grid[x2][y1][z2]+od_grid[x2][y2][z2]+od_grid[x2][y1][z2])/4.0+offset*(Math.random()-0.5); RecursiveGridFill(x1,y1,z1,midx,midy,midz,offset/2.0); RecursiveGridFill(x1,y1,midz,midx,midy,z2,offset/2.0); RecursiveGridFill(x1,midy,z1,midx,y2,midz,offset/2.0); RecursiveGridFill(x1,midy,midz,midx,y2,z2,offset/2.0); RecursiveGridFill(midx,y1,z1,x2,midy,midz,offset/2.0); RecursiveGridFill(midx,y1,midz,x2,midy,z2,offset/2.0); RecursiveGridFill(midx,midy,z1,x2,y2,midz,offset/2.0); RecursiveGridFill(midx,midy,midz,x2,y2,z2,offset/2.0); } private DataSet2D data; private double match_data[]; private final int GRID_SIZE=33; private double od_grid[][][]; }