/* Copyright 1996 Matthew Freedman and University of Washington mattf@cac.washington.edu This program may be freely used, modified, or redistributed for any non-commercial purpose as long as credit is given. This file contains the Nina class. */ import java.awt.*; import java.util.*; /* The Nina class is the heart of the NINA system -- it is a subclass of Canvas that draws the pretty pictures. It has 4 methods that you might be interested in calling: Nina(): a constructor, no arguments; drawFig(NinaParams s): draw a figure, pass in a NinaParams object. drawFig(NinaParams s, NinaController controller): draw a figure, pass in a NinaParams object and an object that implements the NinaController interface, meaning it has a do_update method for setting new drawing parameters when we are ready to draw. die(): Call if Nina should be interrupted. */ class Nina extends Canvas implements Runnable { Image off_img = null; // Off-screen drawing area Graphics off_gc; // Off-screen graphics context Graphics gc; // On-screen graphics context int width = 0, height = 0; // Drawing dimensions Thread runner; NinaParams params; // Object that holds current figure params NinaController controller = null; /* An object that updates our figure params when we are ready to draw */ /* Constructor method, initialize a figure */ Nina() { params = new NinaParams(); params.setup(); } /* Called from outside if we should stop */ public void die() { stop(); } /* Called from outside to launch a new figure. Caller passes in handle to self, so it can be notified of updates to the parameters */ public void drawFig(NinaParams s, NinaController controller) { this.controller = controller; stop(); params = s; start(); } /* Called from outside to launch a new figure. No handle to self, so Nina does not notify */ public void drawFig(NinaParams s) { drawFig(s, null); } //////////// From here down is all internal stuff //////////// /* We can not really initialize until we have a size */ public void reshape(int x, int y, int width, int height) { super.reshape(x, y, width, height); /* Is it really changing? */ if ((this.width == width) && (this.height == height)) return; this.width = width; this.height = height; /* Create an offscreen image too, for expose events. If this is not the first time, halt the current drawing and restart with new size */ if (off_img != null) { stop(); off_img = createImage(width, height); off_gc = off_img.getGraphics(); start(); } else { /* Get the graphics context */ gc = getGraphics(); off_img = createImage(width, height); off_gc = off_img.getGraphics(); } off_gc.setColor(Color.black); gc.setColor(Color.black); off_gc.fillRect(0, 0, width, height); gc.fillRect(0, 0, width, height); } /* Either update random parameters ourself, or ask controller to do it */ protected void adjust() { /* Notify controller that we we-are re-drawing, and collect any changed parameters. It will call setup to do any randomization */ if (controller != null) { controller.do_update(); } else { /* No controller, do randomization ourself */ params.setup(); params.setup(); } } /* Draw a figure */ protected void drawFig(){ int count, color_count, which_color; int num_each_color; int cur_x, cur_y; int prev_x, prev_y; int first_x, first_y; int x_center, y_center, x_scale, y_scale; off_gc.setColor(params.bg_color); off_gc.fillRect(0, 0, width, height); if (params.show_lines) { gc.setColor(params.bg_color); gc.fillRect(0, 0, width, height); /* For some reason when we are in a floating frame the above seems to set the background white (Anybody know why?), but forcing a paint() now sets it back to black. */ paint(gc); } prev_x = prev_y = cur_x = cur_y = first_x = first_y = 0; x_center = width / 2; y_center = height / 2; x_scale = x_center / 2; y_scale = y_center / 2; num_each_color = params.num_lines / Math.min(params.num_colors, params.num_lines) + 1; which_color = 0; if (params.show_lines) gc.setColor(params.palette[which_color]); off_gc.setColor(params.palette[which_color]); for (count = color_count = 0; count <= params.num_lines; count++, color_count++) { if (color_count == num_each_color) { color_count = 0; which_color++; if (params.show_lines) gc.setColor(params.palette[which_color]); off_gc.setColor(params.palette[which_color]); } /* The next two statements are the heart of Nina -- calculate the points using pseudo discrete fourier transform */ cur_x = x_center + (int)((Math.cos((-2*Math.PI*params.a_pulse*count)/ params.num_lines) + Math.cos((-2*Math.PI*params.b_pulse*count)/ params.num_lines)) * x_scale); cur_y = y_center + (int)((Math.sin((-2*Math.PI*params.a_pulse*count)/ params.num_lines) + Math.sin((-2*Math.PI*params.b_pulse*count)/ params.num_lines)) * y_scale); if (count==0) { /* first point */ first_x = prev_x = cur_x; first_y = prev_y = cur_y; } else { /* Don't do this the first time */ if (params.show_lines) gc.drawLine(cur_x, cur_y, prev_x, prev_y); off_gc.drawLine(cur_x, cur_y, prev_x, prev_y); prev_x = cur_x; prev_y = cur_y; } /* Do the pause if turned on. Don't bother if we are not animating */ if ((params.ldelay > 0) && params.show_lines) { try { Thread.sleep(params.ldelay); } catch (InterruptedException e) { } } } /* Draw final line, then expose it in case drawing was hiden */ if (params.show_lines) gc.drawLine(cur_x, cur_y, first_x, first_y); off_gc.drawLine(cur_x, cur_y, first_x, first_y); paint(gc); } /* Start and stop threads */ public void start() { if (runner == null); { runner = new Thread(this); runner.start(); } } public void stop() { if (runner != null) { runner.stop(); runner = null; } } public void run() { do { drawFig(); if (params.auto_draw) { try { Thread.sleep(params.fdelay * 1000); } catch (InterruptedException e) { } adjust(); } } while (params.auto_draw); } public boolean mouseUp(Event evt, int x, int y) { stop(); adjust(); start(); return(true); } /* Handle an expose event */ public void paint(Graphics g) { gc.drawImage(off_img, 0, 0, this); } }