Contributor |
|
|
Joined: Fri Aug 13, 2010 2:40 pm Posts: 8374 Location: San Diego, CA
|
Here's the Java source code for the Little Hawk animation posted above:
Code: import java.awt.*; import java.awt.image.*; import java.util.Random;
// This class provides some handy "global" data.
class gdata { static Color bg = new Color(100,140,200); static int x_offset_for_screen = 330; static double delta_angle = Math.PI/128; static int pause_time_ms = 5; }
// This class provides a frame that sets the background and exits properly
class Little_Hawk_Frame extends Frame { public Little_Hawk_Frame ( String s ) { super ( s ); setBackground ( gdata.bg ); } public boolean handleEvent(Event evt) { if (evt.id == Event.WINDOW_DESTROY) { System.exit ( 0 ); } return super.handleEvent(evt); } }
// This is the super class for 3D objects that are drawn to the screen (only the glider in this program) // It's really just a collection of points that can be translated or rotated about an axis // It includes an "extract-values" function to return contiguous subsets of points from the object
class drawn_object { // Coordinates are generic and interpreted by each subclass double x_coords[] = null; double y_coords[] = null; double z_coords[] = null; double ux=0; double uy=0; double uz=1;
public void translate ( double dx, double dy, double dz ) { for (int p=0; p<x_coords.length; p++) { x_coords[p] = x_coords[p] + dx; y_coords[p] = y_coords[p] + dy; z_coords[p] = z_coords[p] + dz; } } public void rotate_about_axis ( double theda, double rx, double ry, double rz ) { // Build the 3D rotation matrix double ct = Math.cos(theda); double st = Math.sin(theda); double rl = Math.sqrt ( (rx*rx) + (ry*ry) + (rz*rz) ); ux = rx / rl; uy = ry / rl; uz = rz / rl; double r[][] = new double[3][3]; r[0][0] = ct + (ux*ux*(1-ct)); r[0][1] = (ux*uy*(1-ct)) - (uz*st); r[0][2] = (ux*uz*(1-ct)) + (uy*st); r[1][0] = r[0][1] + (2*uz*st); r[1][1] = ct + (uy*uy*(1-ct)); r[1][2] = (uy*uz*(1-ct)) - (ux*st); r[2][0] = r[0][2] - (2*uy*st); r[2][1] = r[1][2] + (2*ux*st); r[2][2] = ct + (uz*uz*(1-ct)); // Rotate each point using the 3D rotation matrix double new_x, new_y, new_z; for (int p=0; p<x_coords.length; p++) { new_x = (x_coords[p]*r[0][0]) + (y_coords[p]*r[1][0]) + (z_coords[p]*r[2][0]); new_y = (x_coords[p]*r[0][1]) + (y_coords[p]*r[1][1]) + (z_coords[p]*r[2][1]); new_z = (x_coords[p]*r[0][2]) + (y_coords[p]*r[1][2]) + (z_coords[p]*r[2][2]); x_coords[p] = new_x; y_coords[p] = new_y; z_coords[p] = new_z; }
} public int[] extract_values ( int values[], int first, int last ) { int subset[] = new int[1+last-first]; for (int i=first; i<=last; i++) { subset[i-first] = values[i]; } return subset; } }
// This class contains all the details to draw a glider // It extends (inherits from) drawn_object which give it coordinates, translation and rotation // This class is responsible for partitioning its set of points into objects like wing, control bar, and king post // This class is also responsible for drawing each portion of the object to the screen via its draw_view_z function
class drawn_glider extends drawn_object {
int wing_start = 0; int wing_end = 19; int kingpost_start = 20; int kingpost_end = 21; int controlbar_start = 22; int controlbar_end = 24;
// This is the constructor for a drawn glider
public drawn_glider () { // Define the shape of the glider in 3-D points // For convenience, all points are together in one set of arrays, and draw_view_z "knows" which subsets belong to which parts of the glider double x[] = { 0, 0, 86, 124, 136, 147, 157, 146, 135, 125, 0, -125, -135, -146, -157, -147, -136, -124, -86, 0, /**/ 0, 0, /**/ 0, 30, -30 }; double y[] = { 100, -57, -14, 7, 16, 28, 43, 52, 56, 57, 27, 57, 56, 52, 43, 28, 16, 7, -14, -57, /**/ 0, 0, /**/ 10, -10, -10 }; double z[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /**/ 0, -50, /**/ 0, 50, 50 }; x_coords = x; y_coords = y; z_coords = z; // Rotate the glider so that it will project onto the "z" axis rotate_about_axis ( -0.3, 0, 1, 0 ); rotate_about_axis ( 1.4, 1, 0, 0 ); // Move to the center of the screen translate ( gdata.x_offset_for_screen, 0, 0 ); }
// This function draws the glider into the Graphics object g
public void draw_view_z ( Graphics g, int cx, int cy, int r ) { // This draws the projection onto the x-y plane centered at (cx,cy) with size r // Note that the "x-y plane" is the screen's x-y plane where z is depth of view // This is different from the common aviation use of z as altitude!! // First figure out which side of the sail we see (used for drawing order) boolean view_from_top = true; if (z_coords[kingpost_start] > z_coords[kingpost_end] ) { view_from_top = false; } // Scale the coordinates to the image without z coordinates int n = x_coords.length; int x[] = new int[n]; int y[] = new int[n]; for (int i=0; i<n; i++) { x[i] = cx + ( (int) (r * x_coords[i] / 157) ); y[i] = cy + ( (int) (r * y_coords[i] / 157) ); }
// Pull out the coordinates needed to draw the outline of the wing int wing_x[] = extract_values(x,wing_start,wing_end); int wing_y[] = extract_values(y,wing_start,wing_end); int wing_len = 1+wing_end-wing_start;
// Pull out the coordinates needed to draw the kingpost int kingpost_x[] = extract_values(x,kingpost_start,kingpost_end); int kingpost_y[] = extract_values(y,kingpost_start,kingpost_end); int kingpost_len = 1+kingpost_end-kingpost_start;
// Pull out the coordinates needed to draw the control bar int controlbar_x[] = extract_values(x,controlbar_start,controlbar_end); int controlbar_y[] = extract_values(y,controlbar_start,controlbar_end); int controlbar_len = 1+controlbar_end-controlbar_start;
// Draw the glider from back to front in the view so the foreground overwrites the background // This allows the sail to properly obscure what's behind it and not what's in front of it
if (view_from_top) {
// Draw the control bar g.setColor ( new Color(255, 255, 255) ); g.drawPolygon ( controlbar_x, controlbar_y, controlbar_len ); // Fill the sail g.setColor ( new Color(220, 180, 180) ); g.fillPolygon ( wing_x, wing_y, wing_len );
// Draw the outline of the sail g.setColor ( new Color(255, 255, 255) ); g.drawPolygon ( wing_x, wing_y, wing_len ); // Draw the kingpost g.setColor ( new Color(255, 255, 255) ); g.drawLine ( kingpost_x[0], kingpost_y[0], kingpost_x[1], kingpost_y[1] ); } else {
// Draw the kingpost g.setColor ( new Color(255, 255, 255) ); g.drawLine ( kingpost_x[0], kingpost_y[0], kingpost_x[1], kingpost_y[1] ); // Fill the sail g.setColor ( new Color(255, 0, 0) ); g.fillPolygon ( wing_x, wing_y, wing_len );
// Draw the outline of the sail g.setColor ( new Color(255, 255, 255) ); g.drawPolygon ( wing_x, wing_y, wing_len ); // Draw the control bar g.setColor ( new Color(255, 255, 255) ); g.drawPolygon ( controlbar_x, controlbar_y, controlbar_len ); }
}
double get_z () { return (z_coords[0]); } }
// This is the cloud class, it has a constructor and a draw function
class cloud { Random rand = null; public int center_x = 200; public int center_y = 50; public int width = 180; public int height = 50; public double rel_size = 1; public Color c = new Color (200,200,200); public int num_ovals = 5; public double dx[] = null; public double dy[] = null; public double dw[] = null; public double dh[] = null; public cloud () { rand = new Random(); num_ovals = 3 + (int)(3 * Math.abs(rand.nextGaussian())); dx = new double[num_ovals]; dy = new double[num_ovals]; dw = new double[num_ovals]; dh = new double[num_ovals]; for (int i=0; i<num_ovals; i++) { dx[i] = (int)(20 * rand.nextGaussian()); dy[i] = (int)(5 * rand.nextGaussian()); dw[i] = 60 + (int)(20 * rand.nextGaussian()); dh[i] = 30 + (int)(5 * rand.nextGaussian()); if (dh[i] > (0.7*dw[i])) dw[i] = dh[i] / 0.7; } } public void draw ( Graphics g, int altitude, int window_height ) { g.setColor ( c ); for (int i=0; i<num_ovals; i++) { g.fillOval ( (int)(center_x+dx[i]), (int)(center_y+dy[i]+altitude-window_height), (int)(dw[i]*rel_size), (int)(dh[i]*rel_size) ); g.fillOval ( (int)(center_x+dx[i]), (int)(center_y+dy[i]+altitude), (int)(dw[i]*rel_size), (int)(dh[i]*rel_size) ); g.fillOval ( (int)(center_x+dx[i]), (int)(center_y+dy[i]+altitude+window_height), (int)(dw[i]*rel_size), (int)(dh[i]*rel_size) ); } } }
// This is the main class for this application / applet
public class Little_Hawk_004 extends java.applet.Applet implements Runnable {
// These are some "isntance" variables for the class ... including the clouds and glider
boolean is_applet = true;
Thread update_thread; boolean moved = false; int delay = 1; // This will be changed by init, so "1" is fine.
double angle = 3 * Math.PI / 2; double delta_angle = gdata.delta_angle; double current_delta_angle = delta_angle; double altitude = 0; cloud clouds[] = null; drawn_glider glider = new drawn_glider();
// This is the constructor for the class. It is called when a new instance is created. public Little_Hawk_004 () { super();
// Create an array of 3 random clouds
clouds = new cloud[3]; for (int i=0; i<clouds.length; i++) { clouds[i] = new cloud(); }
// Initialize each of the 3 clouds for a nice appearance
clouds[0].center_x=100; clouds[0].center_y=0; clouds[0].width=180; clouds[0].height=50; clouds[0].num_ovals=8; clouds[0].dx = new double[] {29.0,-9.0,-2.0,2.0,-35.0,12.0,-8.0,-8.0}; clouds[0].dy = new double[] {6.0,6.0,2.0,3.0,8.0,7.0,-5.0,-6.0}; clouds[0].dw = new double[] {64.0,41.42,60.0,72.0,35.71,37.14,56.0,77.0}; clouds[0].dh = new double[] {28.0,29.0,21.0,34.0,25.0,26.0,28.0,33.0};
clouds[1].center_x=250; clouds[1].center_y=-90; clouds[1].width=180; clouds[1].height=50; clouds[1].num_ovals=6; clouds[1].dx = new double[] {8.0,-19.0,0.0,0.0,-14.0,-4.0}; clouds[1].dy = new double[] {7.0,5.0,3.0,-8.0,3.0,-6.0}; clouds[1].dw = new double[] {87.0,42.85,42.85,67.0,76.0,72.0}; clouds[1].dh = new double[] {22.0,30.0,30.0,33.0,31.0,31.0};
clouds[2].center_x=480; clouds[2].center_y=130; clouds[2].width=180; clouds[2].height=50; clouds[2].num_ovals=10; clouds[2].dx = new double[] {-28.0,1.0,-16.0,40.0,-26.0,20.0,-7.0,12.0,-7.0,-7.0}; clouds[2].dy = new double[] {4.0,9.0,-3.0,-2.0,12.0,10.0,5.0,4.0,5.0,0.0}; clouds[2].dw = new double[] {53.0,54.0,52.85,67.0,65.0,98.0,84.0,57.14,58.0,64.0}; clouds[2].dh = new double[] {37.0,31.0,37.0,32.0,31.0,34.0,38.0,40.0,26.0,27.0};
}
// This function gets "events" ... things like mouse clicks and key presses
public boolean handleEvent(Event e) { // Use the mouse click event to toggle the "freeze frame" mode if (e.id == 501) { if (current_delta_angle == 0) { current_delta_angle = delta_angle; } else { current_delta_angle = 0; } } return super.handleEvent(e); }
// This function draws (paints) the frame and moves the glider and clouds
public void paint_frame(Graphics g) { Dimension window_size = size(); int w = window_size.width; int h = window_size.height; int nclouds = clouds.length; for (int cnum=0; cnum<nclouds; cnum++) { clouds[cnum].draw(g, (int)altitude, h); } g.setColor ( new Color(255, 0, 0) ); glider.rotate_about_axis ( current_delta_angle, 0.000, 0.001, 0.000 ); int rel_size = (int)(glider.get_z() / 10); glider.draw_view_z ( g, gdata.x_offset_for_screen, 200, 80+rel_size ); if (moved) { moved = false; angle = angle - current_delta_angle; int N = 2; altitude = ( -h * (angle / (N * 2 * Math.PI))) % h; } }
// This is the init function called within browsers or directly by main
public void init() { String param; if (is_applet) { param = getParameter("delay"); } else { param = null; } if (param == null) { delay = gdata.pause_time_ms; } else { delay = Integer.valueOf(param).intValue(); } }
// For smooth animation, each new frame is drawn to an off-screen image buffer which is then drawn to the screen all at once // This code directs the drawing to the off-screen image buffer and the painting of that buffer to the screen
BufferedImage img_buffer = null; Graphics g_buffer = null; Color bg_color = gdata.bg;
void init_gbuffer() { if ( (img_buffer == null) || (g_buffer == null) || (size().width != img_buffer.getWidth(this)) || (size().height != img_buffer.getHeight(this)) ) { img_buffer = new BufferedImage ( size().width, size().height, BufferedImage.TYPE_INT_RGB ); g_buffer = img_buffer.getGraphics(); g_buffer.setColor ( bg_color ); g_buffer.fillRect (0,0,size().width, size().height); } }
public void init_frame() { img_buffer = new BufferedImage ( size().width, size().height, BufferedImage.TYPE_INT_RGB ); g_buffer = img_buffer.getGraphics(); g_buffer.setColor ( bg_color ); g_buffer.fillRect (0,0,size().width, size().height); init_gbuffer(); }
public void update(Graphics g) { init_gbuffer(); g_buffer.setColor ( bg_color ); g_buffer.clearRect (0,0,size().width, size().height); g_buffer.fillRect (0,0,size().width, size().height); paint ( g ); }
public void paint(Graphics g) { init_gbuffer(); paint_frame ( g_buffer ); g.drawImage ( img_buffer, 0, 0, this ); }
// The start, stop, and run functions are part of the "Runnable" interface public void start() { update_thread = new Thread(this); update_thread.start(); } public void stop() { update_thread.stop(); } public void run() { while (true) { try { Thread.currentThread().sleep(delay, 0); moved = true; } catch (InterruptedException e) { } repaint(); } } // This is the main function which is called when running as a stand-alone application
public static void main ( String args[] ) { System.out.println ( "Running Little_Hawk_004 ..." );
Little_Hawk_004 app = new Little_Hawk_004(); app.is_applet = false; app.init();
Little_Hawk_Frame f = new Little_Hawk_Frame ( "Copyright 2014 US Hawks Hang Gliding Association (ushawks.org)" );
f.add("Center", app); f.resize(640,410); app.start(); f.show();
} }
It's certainly not the best program ever written, and I'm sure it violates someone's rules of good coding practice. But if you're interested in seeing how a simple animation can be done in Java, it's not a bad place to start. Just create a plain text file named "Little_Hawk_004.java" (case matters) and copy the text from above into that file. Then use a command line window (aka DOS Window or Command Tool or ...) to change directory (cd) to that location and issue these commands: javac Little_Hawk_004.java java Little_Hawk_004 If it works, you should see a new window with Little Hawk soaring happily into infinity. If you get errors, then you may have to install the Java Development Kit (JDK) on your computer. Feel free to call me at 858-204-7499 if you'd like to start programming in Java and I might be able to help you through the JDK installation process. Have fun!!
_________________ Join a National Hang Gliding Organization: US Hawks at ushawks.org View my rating at: US Hang Gliding Rating System Every human at every point in history has an opportunity to choose courage over cowardice. Look around and you will find that opportunity in your own time.
|
|