Before going through this post you might like to read my previous post, to understand the general things.
Last time, I have discussed about some of the general theory needed to draw a
three dimensional Sphere on the computer screen. Today, I am going to
demonstrate the actual drawing of the sphere on the screen. The complete working
code to draw the sphere will be provided in this post.
For working with Three D graphics, You need a good grasp of two
main concepts:
- Point and its operations ( translations)
- Concept of projections in space geometry
First of all, you need every thing about the point. There are different types
of coordinate systems you could work on, and each system has its own points and
its meaning. In two dimensional geometry you have X and Y Coordinates, where as
there is another Z coordinate in the three dimensional space
coordinate(Cartesian system). The point has different operations like
reflection, rotation, translation, etc. This class contains all the basic
properties of Point that you need.
/**
*
* @author Rajan Prasad Upadhyay
* class RPoint.java
*/
public class RPoint {
public double x;
public double y;
public double z;
public RPoint(){}
public RPoint(double x,double y,double z){
this.x=x;
this.y=y;
this.z=z;
}
public void viewPoint(){
System.out.println("("+x+","+y+","+z+")");
}
public int getX(){
int i=(int)Math.rint(x);
return i;
}
public int getY(){
int i=(int)Math.rint(y);
return i;
}
public int getZ(){
int i=(int)Math.rint(z);
return i;
}
public RPoint getRotateAlongTheta(int radius,double theta,RPoint center){
double tx=x-center.x,ty=y-center.y,tz=z-center.z;
double tx1,ty1,tz1;
tx1=tx*Math.cos(Math.toRadians(theta))-tz*Math.sin(Math.toRadians(theta));
tz1=tx*Math.sin(Math.toRadians(theta))+tz*Math.cos(Math.toRadians(theta));
tx=tx1+center.x;
tz=tz1+center.z;
RPoint p=new RPoint(tx,y,tz);
return p;
}
public RPoint getRotateAlongPhi(int radius,double phi, RPoint center){
double tx=x-center.x,ty=y-center.y,tz=z-center.z;
double tx1,ty1,tz1;
tx1=tx*Math.cos(Math.toRadians(phi))-tz*Math.sin(Math.toRadians(phi));
ty1=tx*Math.sin(Math.toRadians(phi))+tz*Math.cos(Math.toRadians(phi));
tx=tx1+center.x;
ty=ty1+center.y;
RPoint p=new RPoint(tx,ty,z);
return p;
}
//unit vectors
public double getL(){
double a=x/Math.sqrt(x*x+y*y+z*z);
return a;
}
public double getM(){
double a=y/Math.sqrt(x*x+y*y+z*z);
return a;
}
public double getN(){
double a=z/Math.sqrt(x*x+y*y+z*z);
return a;
}
//project coordiantes functions
public int getViewX(int cz){
int a=(int)Math.rint(cz*x/z);
return a;
}
public int getViewY(int cz){
int a=(int)Math.rint((cz)*y/z);
return a;
}
public int getViewZ(int cz){
int a=(cz);
return a;
}
//3d translations functions
public RPoint translate( RPoint p){
RPoint a=new RPoint(x-p.x,y=p.y,z-p.z);
return a;
}
public RPoint inverseTranslate( RPoint p){
RPoint a=new RPoint(x+p.x,y+p.y,z+p.z);
return a;
}
public void RotateAlongTheta( double theta, RPoint center){
double tx=(x-center.x)*Math.cos(Math.toRadians(theta))-(z-center.z)*Math.sin(Math.toRadians(theta));
double tz=(x-center.x)*Math.sin(Math.toRadians(theta))+(z-center.z)*Math.cos(Math.toRadians(theta));
x=tx+center.x;
z=tz+center.z;
}
public void RotateAlongPhi( double phi, RPoint center){
double tz=(z-center.z)*Math.cos(Math.toRadians(-phi))-(y-center.y)*Math.sin(Math.toRadians(-phi));
double ty=(z-center.z)*Math.sin(Math.toRadians(-phi))+(y-center.y)*Math.cos(Math.toRadians(-phi));
y=ty+center.y;
z=tz+center.z;
}
public RPoint RotateAlongX(int theta, RPoint center){
//x does not change
double tz=(z-center.z)*Math.cos(Math.toRadians(-theta))-(y-center.y)*Math.sin(Math.toRadians(-theta));
double ty=(z-center.z)*Math.sin(Math.toRadians(-theta))+(y-center.y)*Math.cos(Math.toRadians(-theta));
return new RPoint(x,ty+center.y,tz+center.z);
}
public RPoint RotateAlongY( int theta, RPoint center){
//ie rotating along y axix , clockwise
double tx=(x-center.x)*Math.cos(Math.toRadians(theta))-(z-center.z)*Math.sin(Math.toRadians(theta));
double tz=(x-center.x)*Math.sin(Math.toRadians(theta))+(z-center.z)*Math.cos(Math.toRadians(theta));
return new RPoint(tx+center.x,y,tz+center.z);
}
public RPoint RotateAlongZ(int theta, RPoint center){
//z constant
double tx=(x-center.x)*Math.cos(Math.toRadians(theta))-(y-center.y)*Math.sin(Math.toRadians(theta));
double ty=(x-center.x)*Math.sin(Math.toRadians(theta))+(y-center.y)*Math.cos(Math.toRadians(theta));
return new RPoint(tx+center.x,ty+center.y,z);
}
//prospective view coordinate giving functions
public void RevolveAlongY(double theta, RPoint sample){
double tx=(x-sample.x)*Math.cos(Math.toRadians(theta))-(z-sample.z)*Math.sin(Math.toRadians(theta));
double tz=(x-sample.x)*Math.sin(Math.toRadians(theta))+(z-sample.z)*Math.cos(Math.toRadians(theta));
x=tx+sample.x;
z=tz+sample.z;
}
public void RevolveAlongX(double theta, RPoint sample){
}
public RPoint getPerspective( RPoint viewPoint){
double xp=x-(-z)/(viewPoint.z-z)*(x-viewPoint.x);
double yp=y-(-z)/(viewPoint.z-z)*(y-viewPoint.y);
double zp=0;
return new RPoint(xp,yp,zp);
}
public RPoint rotateAlongY(double angle){
double x2=x*Math.cos(Math.toRadians(angle))-z*Math.sin(Math.toRadians(angle));
double y2=x*Math.sin(Math.toRadians(angle))+z*Math.cos(Math.toRadians(angle));
return new RPoint(x2,y2,z);
}
}
Then on top of the RPoint, You could generate another object with the
properties of a sphere. Sphere has a center, radius, and lots of points through
its surface. It can rotate, translate etc.
/**
*
* @author Rajan Prasad Upadhyay
* class RSphere.java
*/
import java.awt.Color;
import java.awt.Graphics;
public class RSphere {
RPoint center;
RPoint viewPoint=new RPoint(600,400,2000);
int radius;
public Graphics g;
int delphi;//used to determine the number of rows in mat[][] ie point matrix
int deltheta;//determine the num of cols in the mat
int numOfRows;
int numOfCols;
RPoint mat[][];//matrix of points// actual world coordinates
RPoint mat1[][];//to hold projected coordinates
RPoint mat2[][];//matrix according to view points angle with the z axis;;
int angle=0;
public RSphere(){
center=new RPoint(0,0,0);
radius=50;
deltheta=20;
delphi=20;
numOfRows=180/delphi;
numOfCols=360/deltheta;
}
public void setGraphics(Graphics gh){
if(gh != null){
this.g = gh;
}else{
System.out.println("Your graphics context is null.");
}
}
public void setColor(Color c){
g.setColor(c);
}
public void set_Center( RPoint p){
//this funciton should be called at the beginning only
center=p;
}
public void set_viewer(int x,int y,int z){
viewPoint=new RPoint(x,y,z);
}
public void set_Center(int x,int y,int z){
center=new RPoint(x,y,z);
}
public void setRadius(int r){
radius=r;
}
public void initialize(){
numOfRows = 180/delphi+1;
numOfCols = 360/deltheta+1;
mat = new RPoint[numOfRows][numOfCols];
mat1 = new RPoint[numOfRows][numOfCols];
mat2 = new RPoint[numOfRows][numOfCols];
System.out.print("center=");
center.viewPoint();
System.out.println("radius="+this.radius);
double theta = 0;
double phi = 90;
for(int row = 0; row<numOfRows; row++){
for(int col = 0;col < numOfCols; col++){
//creating the matrix
mat[row][col] = new RPoint(
center.x+radius*Math.cos(Math.toRadians(theta))*Math.cos(Math.toRadians(phi))
,center.y+radius*Math.sin(Math.toRadians(phi))
,center.z+radius*Math.sin(Math.toRadians(theta))*Math.cos(Math.toRadians(phi))
);
mat1[row][col] = mat[row][col].getPerspective(viewPoint);
theta += deltheta;
theta %= 360;
}
phi -= delphi;
theta = 0;
}
}//initialize
public void drawLatitudes(){
int cz = -20;
for(int i=0;i<numOfRows;i++){
for(int j=0;j<(numOfCols-1);j++){
g.drawLine(mat[i][j].getX(), mat[i][j].getY(), mat[i][j+1].getX(), mat[i][j+1].getY());
}
}
}
public void drawLongitudes(){
int cz=0;
for(int i=0;i<numOfRows-1;i++){
for(int j=0;j<numOfCols;j++){
g.drawLine(mat[i][j].getX(), mat[i][j].getY(), mat[i+1][j].getX(), mat[i+1][j].getY());
}
}
}
public void rotateAlongX(int angle){
for(int i=0;i<numOfRows;i++){
for(int j=0;j<numOfCols;j++){
mat[i][j]=mat[i][j].RotateAlongX(angle,center);//perfect
}
}
}
public void rotateAlongY(int angle){
for(int i=0;i<numOfRows;i++){
for(int j=0;j<numOfCols;j++){
mat[i][j]= mat[i][j].RotateAlongY(angle,center);//perfect
}
}
}
public void rotateAlongZ(int angle){
for(int i=0;i<numOfRows;i++){
for(int j=0;j<numOfCols;j++){
mat[i][j]=mat[i][j].RotateAlongZ(angle,center);//perfect
}
}
}
public void updatePerspective(){
for(int i=0;i<numOfRows;i++){
for(int j=0;j<numOfCols;j++){
mat1[i][j]=mat[i][j].getPerspective(viewPoint);
}
}
}
public void calculatePrespective(){
for(int i=0;i<numOfRows;i++){
for(int j=0;j<numOfCols;j++){
mat1[i][j]=mat[i][j].getPerspective(viewPoint);
}
}
}
public void prospectiveView(){
calculatePrespective();
viewTheLines(mat1);
}
public void viewTheLines( RPoint mat1[][]){
//longitute
for(int i=0;i<numOfRows-1;i++){
for(int j=0;j<numOfCols;j++){
g.drawLine(mat1[i][j].getX(), mat1[i][j].getY(), mat1[i+1][j].getX(), mat1[i+1][j].getY());
}
}
//latitude
for(int i=0;i<numOfRows;i++){
for(int j=0;j<(numOfCols-1);j++){
g.drawLine(mat1[i][j].getX(), mat1[i][j].getY(), mat1[i][j+1].getX(), mat1[i][j+1].getY());
}
}
}
}
and finally to display the frame we have
/**
*
* @author Rajan Prasad Upadhyay
* class RFrame.java
*/
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class RFrame extends JFrame{
RSphere earth = new RSphere();
RPoint earthCenter=new RPoint(600,400,-1500);
int viewAngle = 0;
Graphics g;
private void arrangeGraphics(){
earth.set_Center(earthCenter);
earth.setRadius(200);
earth.initialize();
g = this.getGraphics();
if(g != null){
earth.setGraphics(g);
}else{
System.out.println("The g is null, Frame.java");
}
if(earth.g != null){
earth.setColor(Color.blue);
}else{
System.out.println("Graphics is null");
}
}
public RFrame(){
initComponents();
arrangeGraphics();
earth.drawLatitudes();
earth.drawLongitudes();
}
private void initComponents(){
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
this.setMinimumSize(new Dimension(1000,800));
addKeyListener(new java.awt.event.KeyAdapter() {
public void keyPressed(java.awt.event.KeyEvent evt) {
formKeyPressed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 483, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 320, Short.MAX_VALUE)
);
pack();
}
private void formKeyPressed(java.awt.event.KeyEvent evt){
RPoint p = earth.center;
if(evt.getKeyCode()==KeyEvent.VK_LEFT){
g.clearRect(0, 20, this.getWidth(), this.getHeight());
earth.rotateAlongZ(5);
earth.drawLatitudes();
earth.drawLongitudes();
}if(evt.getKeyCode()==KeyEvent.VK_SPACE){
g.clearRect(0, 20, this.getWidth(), this.getHeight());
earth.rotateAlongY(1);
earth.drawLatitudes();
earth.drawLongitudes();
}
}
@Override
public void paint(Graphics g){
earth.drawLatitudes();
earth.drawLongitudes();
}
public static void main(String [] args){
RFrame f = new RFrame();
f.setVisible(true);
}
}
Now try running the code and press the Space and Left arrow button on the key-board. Have fun.