// Dust - a simple particle system for wind visualization // Copyright 1999,2005 Nick Thompson // see http://nixfiles.com/dust for more information // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // import java.applet.*; import java.awt.*; import java.net.URL; import java.awt.image.ImageObserver; public class Dustlet extends Applet implements ImageObserver, Runnable { Dust dust; double fetch_interval = 0; long last_fetch = 0; boolean running; URL windfile_url; Image bgimage; Thread animator; double timestep; public void init() { String refetch = getParameter("refetch"); if (refetch != null) { fetch_interval = Double.valueOf(refetch).doubleValue(); } // read the background image String datfile = getParameter("bgimage"); if (datfile == null) { datfile = "http://sfports.wr.usgs.gov/~wind/windsuv.dat"; } try { URL url = new URL(getDocumentBase(), datfile); // get the image and stash it in bgimage bgimage = getImage(url); } catch (java.net.MalformedURLException e) { System.err.println("bad url " + datfile + ":\n" + e); } timestep = 0.1; try { String tmp = getParameter("timestep"); if (tmp != null) { timestep = Double.valueOf(tmp).doubleValue(); // retch } } catch (NumberFormatException e) { timestep = 0.1; } stop_running(); } public void start() { int nmotes = 1000; try { String motes_str = getParameter("motes"); if (motes_str != null) { nmotes = Integer.parseInt(motes_str); } } catch (NumberFormatException e) { nmotes = 1000; } dust = new Dust(nmotes); String datfile = getParameter("data"); if (datfile == null) { datfile = "http://sfports.wr.usgs.gov/~wind/windsuv.out"; } try { windfile_url = new URL(getDocumentBase(), datfile); } catch (java.net.MalformedURLException e) { System.err.println("bad url " + datfile + ":\n" + e); } update_wind_data(); // this must be done after xsize and ysize are set... // i should keep track of width and height and remove this problem Rectangle r = bounds(); dust.set_pixel_size(r.width, r.height); start_running(); } public void stop() { stop_running(); } public void destroy() { stop_running(); if (animator != null) { animator.stop(); animator = null; } } public synchronized boolean mouseDown(Event ev, int x, int y) { if (running) { stop_running(); } else { start_running(); } return true; } public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { if ((infoflags & ImageObserver.ALLBITS) != 0) { repaint(); return false; } return true; } public void paint_background(Graphics g) { if (bgimage == null) { g.setColor(Color.black); Rectangle rect = g.getClipRect(); g.fillRect(rect.x, rect.y, rect.width, rect.height); return; } g.drawImage(bgimage, 0, 0, null); } // XXX/nix resizing won't work right... Image backbuffer; Graphics backbuffer_graphics; public void update(Graphics g) { // skip the background fill paint(g); } public void paint(Graphics g) { // System.err.println("paint called"); if (backbuffer == null) { Rectangle rect = g.getClipRect(); backbuffer = createImage(rect.width, rect.height); backbuffer_graphics = backbuffer.getGraphics(); } paint_background(backbuffer_graphics); if (dust != null) { backbuffer_graphics.setColor(Color.green); dust.drawmotes(backbuffer_graphics); } g.drawImage(backbuffer, 0, 0, null); } public synchronized void await_running() { while (!running) { try { wait(); } catch (InterruptedException e) { } } } public synchronized void start_running() { running = true; notify(); if (animator == null) { animator = new Thread(this); animator.start(); } } public synchronized void stop_running() { running = false; notify(); } public void update_wind_data() { java.io.InputStream in = null; // XXXopen url stream try { in = windfile_url.openStream(); dust.read_windfile(in); } catch (Throwable e) { e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (java.io.IOException e) { System.err.println(e); } } } last_fetch = System.currentTimeMillis(); } public void step() { dust.stepmotes(0.1); repaint(); } // this is the main loop for the update thread public void run() { while (true) { await_running(); long now = System.currentTimeMillis(); double dt = (now - last_fetch) / 1000.0; if (fetch_interval > 0 && dt > fetch_interval) { update_wind_data(); } step(); try { Thread.sleep(100); } catch (InterruptedException e) { } } } }