/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* PApplet - applet base class for the bagel engine Part of the Processing project - http://processing.org Copyright (c) 2004- Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package processing.core; import java.applet.*; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.io.*; import java.lang.reflect.*; import java.net.*; import java.text.*; import java.util.*; import java.util.zip.*; public class PApplet extends Applet implements PConstants, Runnable, MouseListener, MouseMotionListener, KeyListener, FocusListener { /** * "1.3" or "1.1" or whatever (just the first three chars) */ public static final String javaVersionName = System.getProperty("java.version").substring(0,3); /** * Version of Java that's in use, whether 1.1 or 1.3 or whatever, * stored as a float. *
* Note that because this is stored as a float, the values may * not be exactly 1.3 or 1.4. Instead, make sure you're * comparing against 1.3f or 1.4f, which will have the same amount * of error (i.e. 1.40000001). This could just be a double, but * since Processing only uses floats, it's safer to do this, * because there's no good way to specify a double with the preproc. */ public static final float javaVersion = new Float(javaVersionName).floatValue(); /** * Current platform in use, one of the * PConstants WINDOWS, MACOSX, MACOS9, LINUX or OTHER. */ static public int platform; /** * Current platform in use. *
* Equivalent to System.getProperty("os.name"), just used internally. */ static public String platformName = System.getProperty("os.name"); static { // figure out which operating system // this has to be first, since editor needs to know if (System.getProperty("mrj.version") != null) { // running on a mac platform = (platformName.equals("Mac OS X")) ? MACOSX : MACOS9; } else { String osname = System.getProperty("os.name"); if (osname.indexOf("Windows") != -1) { platform = WINDOWS; } else if (osname.equals("Linux")) { // true for the ibm vm platform = LINUX; } else { platform = OTHER; } } } /** The PGraphics renderer associated with this PApplet */ public PGraphics g; protected Object glock = new Object(); // for sync /** The frame containing this applet (if any) */ public Frame frame; /** * Message of the Exception thrown when size() is called the first time. *
* This is used internally so that setup() is forced to run twice * when the renderer is changed. Reason being that the */ static final String NEW_RENDERER = "new renderer"; /** * The screen size when the applet was started. *
* Access this via screen.width and screen.height. To make an applet * run at full screen, use size(screen.width, screen.height). *
* If you have multiple displays, this will be the size of the main * display. Running full screen across multiple displays isn't * particularly supported, and requires more monkeying with the values. * This probably can't/won't be fixed until/unless I get a dual head * system. *
* Note that this won't update if you change the resolution * of your screen once the the applet is running. */ public Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); /** * A leech graphics object that is echoing all events. */ public PGraphics recorder; /** * Command line options passed in from main(). *
* This does not include the arguments passed in to PApplet itself. */ public String args[]; /** Path to sketch folder */ public String folder; /** When debugging headaches */ static final boolean THREAD_DEBUG = false; static public final int DEFAULT_WIDTH = 100; static public final int DEFAULT_HEIGHT = 100; /** * Pixel buffer from this applet's PGraphics. *
* When used with OpenGL or Java2D, this value will * be null until loadPixels() has been called. */ public int pixels[]; /** width of this applet's associated PGraphics */ public int width; /** height of this applet's associated PGraphics */ public int height; /** current x position of the mouse */ public int mouseX; /** current y position of the mouse */ public int mouseY; /** previous x position of the mouse */ public int pmouseX; /** previous y position of the mouse */ public int pmouseY; /** * previous mouseX/Y for the draw loop, separated out because this is * separate from the pmouseX/Y when inside the mouse event handlers. */ protected int dmouseX, dmouseY; /** * pmouseX/Y for the event handlers (mousePressed(), mouseDragged() etc) * these are different because mouse events are queued to the end of * draw, so the previous position has to be updated on each event, * as opposed to the pmouseX/Y that's used inside draw, which is expected * to be updated once per trip through draw(). */ protected int emouseX, emouseY; /** * Used to set pmouseX/Y to mouseX/Y the first time mouseX/Y are used, * otherwise pmouseX/Y are always zero, causing a nasty jump. *
* Just using (frameCount == 0) won't work since mouseXxxxx() * may not be called until a couple frames into things. */ public boolean firstMouse; public boolean mousePressed; public MouseEvent mouseEvent; /** * Last key pressed. *
* If it's a coded key, i.e. UP/DOWN/CTRL/SHIFT/ALT, * this will be set to CODED (0xffff or 65535). */ public char key; /** * When "key" is set to CODED, this will contain a Java key code. *
* For the arrow keys, keyCode will be one of UP, DOWN, LEFT and RIGHT. * Also available are ALT, CONTROL and SHIFT. A full set of constants * can be obtained from java.awt.event.KeyEvent, from the VK_XXXX variables. */ public int keyCode; /** * true if the mouse is currently pressed. */ public boolean keyPressed; /** * the last KeyEvent object passed into a mouse function. */ public KeyEvent keyEvent; /** * Gets set to true/false as the applet gains/loses focus. */ public boolean focused = false; /** * true if the applet is online. *
* This can be used to test how the applet should behave * since online situations are different (no file writing, etc). */ public boolean online = false; /** * Time in milliseconds when the applet was started. *
* Used by the millis() function. */ long millisOffset; /** * The current value of frames per second. *
* The initial value will be 10 fps, and will be updated with each * frame thereafter. The value is not instantaneous (since that * wouldn't be very useful since it would jump around so much), * but is instead averaged (integrated) over several frames. * As such, this value won't be valid until after 5-10 frames. */ public float framerate = 10; protected long framerateLastMillis = 0; // setting the frame rate protected long framerateLastDelayTime = 0; protected float framerateTarget = 0; protected boolean looping; /** flag set to true when a redraw is asked for by the user */ protected boolean redraw; /** * How many frames have been displayed since the applet started. *
* This value is read-only do not attempt to set it, * otherwise bad things will happen. *
* Inside setup(), frameCount is 0. * For the first iteration of draw(), frameCount will equal 1. */ public int frameCount; /** * true if this applet has had it. */ public boolean finished; Thread thread; /** * Set to the an exception that occurs inside run() and is not * caught.
Used by PdeRuntime to determine what happened and * report back to the user. */ public Exception exception; protected RegisteredMethods sizeMethods; protected RegisteredMethods preMethods, drawMethods, postMethods; protected RegisteredMethods mouseEventMethods, keyEventMethods; protected RegisteredMethods disposeMethods; // this text isn't seen unless PApplet is used on its // own and someone takes advantage of leechErr.. not likely static public final String LEECH_WAKEUP = "Error while running applet."; public PrintStream leechErr; // messages to send if attached as an external vm /** * Position of the upper-lefthand corner of the editor window * that launched this applet. */ static public final String ARGS_EDITOR_LOCATION = "--editor-location"; /** * Location for where to position the applet window on screen. *
* This is used by the editor to when saving the previous applet * location, or could be used by other classes to launch at a * specific position on-screen. */ static public final String ARGS_EXTERNAL = "--external"; static public final String ARGS_LOCATION = "--location"; static public final String ARGS_DISPLAY = "--display"; static public final String ARGS_PRESENT = "--present"; static public final String ARGS_PRESENT_BGCOLOR = "--present-color"; static public final String ARGS_PRESENT_STOP_COLOR = "--present-stop-color"; /** * Allows the user or PdeEditor to set a specific sketch folder path. *
* Used by PdeEditor to pass in the location where saveFrame() * and all that stuff should write things. */ static public final String ARGS_SKETCH_FOLDER = "--sketch-folder"; /** * Message from parent editor (when run as external) to quit. */ static public final char EXTERNAL_STOP = 's'; /** * When run externally to a PdeEditor, * this is sent by the applet when it quits. */ static public final String EXTERNAL_QUIT = "__QUIT__"; /** * When run externally to a PdeEditor, this is sent by the applet * whenever the window is moved. *
* This is used so that the editor can re-open the sketch window * in the same position as the user last left it. */ static public final String EXTERNAL_MOVE = "__MOVE__"; public void init() { // send tab keys through to the PApplet try { if (javaVersion >= 1.4f) { //setFocusTraversalKeysEnabled(false); // 1.4-only function Method defocus = Component.class.getMethod("setFocusTraversalKeysEnabled", new Class[] { Boolean.TYPE }); defocus.invoke(this, new Object[] { Boolean.FALSE }); } } catch (Exception e) { } // oh well millisOffset = System.currentTimeMillis(); finished = false; // just for clarity // this will be cleared by loop() if it is not overridden looping = true; redraw = true; // draw this guy once firstMouse = true; // these need to be inited before setup sizeMethods = new RegisteredMethods(); preMethods = new RegisteredMethods(); drawMethods = new RegisteredMethods(); postMethods = new RegisteredMethods(); mouseEventMethods = new RegisteredMethods(); keyEventMethods = new RegisteredMethods(); disposeMethods = new RegisteredMethods(); // create a dummy graphics context size(DEFAULT_WIDTH, DEFAULT_HEIGHT); //size(INITIAL_WIDTH, INITIAL_HEIGHT); width = 0; // use this to flag whether the width/height are valid height = 0; try { getAppletContext(); online = true; } catch (NullPointerException e) { online = false; } start(); } /** * Called via the first call to PApplet.paint(), * because PAppletGL needs to have a usable screen * before getting things rolling. */ public void start() { if (thread != null) return; thread = new Thread(this); thread.start(); } // maybe start should also be used as the method for kicking // the thread on, instead of doing it inside paint() public void stop() { //finished = true; // why did i comment this out? //System.out.println("stopping applet"); // don't run stop and disposers twice if (thread == null) return; thread = null; disposeMethods.handle(); } /** * This also calls stop(), in case there was an inadvertent * override of the stop() function by a user. * * destroy() supposedly gets called as the applet viewer * is shutting down the applet. stop() is called * first, and then destroy() to really get rid of things. * no guarantees on when they're run (on browser quit, or * when moving between pages), though. */ public void destroy() { stop(); } public Dimension getPreferredSize() { return new Dimension(width, height); } ////////////////////////////////////////////////////////////// public class RegisteredMethods { int count; Object objects[]; Method methods[]; // convenience version for no args public void handle() { handle(new Object[] { }); } public void handle(Object args[]) { for (int i = 0; i < count; i++) { try { //System.out.println(objects[i] + " " + args); methods[i].invoke(objects[i], args); } catch (Exception e) { e.printStackTrace(); } } } public void add(Object object, Method method) { if (objects == null) { objects = new Object[5]; methods = new Method[5]; } if (count == objects.length) { Object otemp[] = new Object[count << 1]; System.arraycopy(objects, 0, otemp, 0, count); objects = otemp; Method mtemp[] = new Method[count << 1]; System.arraycopy(methods, 0, mtemp, 0, count); methods = mtemp; } objects[count] = object; methods[count] = method; count++; } } public void registerSize(Object o) { Class methodArgs[] = new Class[] { Integer.TYPE, Integer.TYPE }; registerWithArgs(preMethods, "size", o, methodArgs); } public void registerPre(Object o) { registerNoArgs(preMethods, "pre", o); } public void registerDraw(Object o) { registerNoArgs(drawMethods, "draw", o); } public void registerPost(Object o) { registerNoArgs(postMethods, "post", o); } public void registerMouseEvent(Object o) { Class methodArgs[] = new Class[] { MouseEvent.class }; registerWithArgs(mouseEventMethods, "mouseEvent", o, methodArgs); } public void registerKeyEvent(Object o) { Class methodArgs[] = new Class[] { KeyEvent.class }; registerWithArgs(keyEventMethods, "keyEvent", o, methodArgs); } public void registerDispose(Object o) { registerNoArgs(disposeMethods, "dispose", o); } protected void registerNoArgs(RegisteredMethods meth, String name, Object o) { Class c = o.getClass(); try { Method method = c.getMethod(name, new Class[] {}); meth.add(o, method); } catch (Exception e) { die("Could not register " + name + " + () for " + o, e); } } protected void registerWithArgs(RegisteredMethods meth, String name, Object o, Class args[]) { Class c = o.getClass(); try { Method method = c.getMethod(name, args); meth.add(o, method); } catch (Exception e) { die("Could not register " + name + " + () for " + o, e); } } ////////////////////////////////////////////////////////////// public void setup() { } public void draw() { // if no draw method, then shut things down //System.out.println("no draw method, goodbye"); finished = true; } public void redraw() { if (!looping) { redraw = true; if (thread != null) { // wake from sleep (necessary otherwise it'll be // up to 10 seconds before update) thread.interrupt(); } } } public void loop() { if (!looping) { looping = true; if (thread != null) { // wake from sleep (necessary otherwise it'll be // up to 10 seconds before update) thread.interrupt(); } } } public void noLoop() { if (looping) { looping = false; // reset framerate delay times framerateLastDelayTime = 0; framerateLastMillis = 0; if (thread != null) { thread.interrupt(); // wake from sleep } } } ////////////////////////////////////////////////////////////// /** * Starts up and creates a two-dimensional drawing surface. *
* To ensure no strangeness, this should be the first thing * called inside of setup(). *
* If using Java 1.3 or later, this will default to using * PGraphics2, the Java2D-based renderer. If using Java 1.1, * or if PGraphics2 is not available, then PGraphics will be used. * To set your own renderer, use the other version of the size() * method that takes a renderer as its last parameter. *
* If called once a renderer has already been set, this will * use the previous renderer and simply resize it. */ public void size(int iwidth, int iheight) { if (g != null) { // just resize the current renderer size(iwidth, iheight, g.getClass().getName()); } else { if (PApplet.javaVersion >= 1.3f) { try { Class c = Class.forName(JAVA2D); size(iwidth, iheight, JAVA2D); return; } catch (ClassNotFoundException e) { } } size(iwidth, iheight, P2D); // fall-through case } } /** * Creates a new PGraphics object and sets it to the specified size. *
* Note that you cannot change the renderer once outside of setup(). * You can call size() to give it a new size, but you need to always * ask for the same renderer, otherwise you're gonna run into trouble. *
* Also note that this calls defaults(), which will reset any * settings for colorMode or lights or whatever. */ public void size(int iwidth, int iheight, String renderer) { String currentRenderer = (g == null) ? null : g.getClass().getName(); if (currentRenderer != null) { if (currentRenderer.equals(renderer)) { if ((iwidth == g.width) && (iheight == g.height)) { // all set, the renderer was queued up last time // before throwing the exception //System.out.println("ignoring additional size()"); // but this time set the defaults //g.defaults(); //System.out.println("defaults set"); g.defaults(); return; } } else { if (frameCount > 0) { throw new RuntimeException("size() cannot be called to change " + "the renderer outside of setup()"); } } } String openglError = "Before using OpenGL, you must first select " + "Import Library > opengl from the Sketch menu."; try { //if (renderer.equals(OPENGL)) { //g = new processing.opengl.PGraphicsGL(iwidth, iheight, this); //} else { Class rendererClass = Class.forName(renderer); Class constructorParams[] = new Class[] { Integer.TYPE, Integer.TYPE, PApplet.class }; Constructor constructor = rendererClass.getConstructor(constructorParams); Object constructorValues[] = new Object[] { new Integer(iwidth), new Integer(iheight), this }; // create the actual PGraphics object for rendering //System.out.println("creating new PGraphics " + constructor); g = (PGraphics) constructor.newInstance(constructorValues); this.width = iwidth; this.height = iheight; // can't do this here because of gl //g.defaults(); //width = g.width; //height = g.height; //pixels = g.pixels; // this may be null // make the applet itself larger setSize(width, height); // probably needs to mess with the parent frame here? // TODO wait for a "legitimate size" flag to be set // (meaning that setup has finished properly) // at which time the parent frame will do its thing. } catch (InvocationTargetException ite) { String msg = ite.getTargetException().getMessage(); if (msg.indexOf("no jogl in java.library.path") != -1) { throw new RuntimeException(openglError); } else { ite.getTargetException().printStackTrace(); } } catch (ClassNotFoundException cnfe) { if (cnfe.getMessage().indexOf("processing.opengl.PGraphicsGL") != -1) { throw new RuntimeException(openglError); } else { throw new RuntimeException("You need to use \"Import Library\" " + "to add " + renderer + " to your sketch."); } } catch (Exception e) { e.printStackTrace(); die("Could not start because of a problem with size()", e); } if ((currentRenderer != null) && !currentRenderer.equals(renderer)) { // throw an exception so that setup() is called again // but with a properly sized render // this is for opengl, which needs a valid, properly sized // display before calling anything inside setup(). throw new RuntimeException(NEW_RENDERER); } // if the default renderer is just being resized, // restore it to its default values g.defaults(); /* if (g == null) return; g.resize(iwidth, iheight); this.pixels = g.pixels; this.width = g.width; this.height = g.height; if (frame != null) { Insets insets = frame.getInsets(); // msft windows has a limited minimum size for frames int minW = 120; int minH = 120; int winW = Math.max(width, minW) + insets.left + insets.right; int winH = Math.max(height, minH) + insets.top + insets.bottom; frame.setSize(winW, winH); setBounds((winW - width)/2, insets.top + ((winH - insets.top - insets.bottom) - height)/2, winW, winH); //} else { //System.out.println("frame was null"); //setBounds(0, 0, width, height); } */ Object methodArgs[] = new Object[] { new Integer(width), new Integer(height) }; sizeMethods.handle(methodArgs); } public void update(Graphics screen) { //System.out.println("PApplet.update()"); if (THREAD_DEBUG) println(Thread.currentThread().getName() + " 4 update() external"); paint(screen); } //synchronized public void paint(Graphics screen) { public void paint(Graphics screen) { //System.out.println("PApplet.paint()"); if (THREAD_DEBUG) println(Thread.currentThread().getName() + " 5a enter paint"); // ignore the very first call to paint, since it's coming // from the o.s., and the applet will soon update itself anyway. //if (firstFrame) return; if (frameCount == 0) { // paint() may be called more than once before things // are finally painted to the screen and the thread gets going //System.out.println("not painting"); /* if (thread == null) { initGraphics(); start(); } */ return; } // without ignoring the first call, the first several frames // are confused because paint() gets called in the midst of // the initial nextFrame() call, so there are multiple // updates fighting with one another. // g.image is synchronized so that draw/loop and paint don't // try to fight over it. this was causing a randomized slowdown // that would cut the framerate into a third on macosx, // and is probably related to the windows sluggishness bug too if (THREAD_DEBUG) println(Thread.currentThread().getName() + " 5b enter paint sync"); //synchronized (g) { synchronized (glock) { if (THREAD_DEBUG) println(Thread.currentThread().getName() + " 5c inside paint sync"); //System.out.println("5b paint has sync"); //Exception e = new Exception(); //e.printStackTrace(); // moving this into PGraphics caused weird sluggishness on win2k //g.mis.newPixels(pixels, g.cm, 0, width); // must call this // make sure the screen is visible and usable if ((g != null) && (g.image != null)) { screen.drawImage(g.image, 0, 0, null); } //if (THREAD_DEBUG) println("notifying all"); //notifyAll(); //thread.notify(); //System.out.println(" 6 exit paint"); } if (THREAD_DEBUG) println(Thread.currentThread().getName() + " 6 exit paint"); //updated = true; } public void run() { try { /* // first time around, call the applet's setup method setup(); // these are the same things as get run inside a call to size() this.pixels = g.pixels; this.width = g.width; this.height = g.height; */ while ((Thread.currentThread() == thread) && !finished) { //while ((thread != null) && !finished) { //while (!finished) { //updated = false; // render a single frame g.requestDisplay(this); // moving this to update() (for 0069+) for linux sync problems //if (firstFrame) firstFrame = false; // wait for update & paint to happen before drawing next frame // this is necessary since the drawing is sometimes in a // separate thread, meaning that the next frame will start // before the update/paint is completed //while (!updated) { try { if (THREAD_DEBUG) println(Thread.currentThread().getName() + " " + looping + " " + redraw); //Thread.yield(); // windows doesn't like 'yield', so have to sleep at least // for some small amount of time. if (THREAD_DEBUG) println(Thread.currentThread().getName() + " gonna sleep"); // can't remember when/why i changed that to '1' // (rather than 3 or 5, as has been traditional), but i // have a feeling that some platforms aren't gonna like that // if !looping, sleeps for a nice long time, // or until interrupted int nap = looping ? 1 : 10000; // don't nap after setup, because if noLoop() is called this // will make the first draw wait 10 seconds before showing up if (frameCount == 1) nap = 1; Thread.sleep(nap); if (THREAD_DEBUG) println(Thread.currentThread().getName() + " outta sleep"); } catch (InterruptedException e) { } } } catch (Exception e) { // formerly in kjcapplet, now just checks to see // if someone wants to leech off errors // note that this will not catch errors inside setup() // those are caught by the PdeRuntime System.out.println("exception occurred (if you don't see a stack " + "trace below this message, we've got a bug)"); finished = true; exception = e; //e.printStackTrace(System.out); if (leechErr != null) { // if draw() mode, make sure that ui stops waiting // and the run button quits out leechErr.println(LEECH_WAKEUP); e.printStackTrace(leechErr); } else { System.err.println(LEECH_WAKEUP); e.printStackTrace(); } } if (THREAD_DEBUG) println(Thread.currentThread().getName() + " thread finished"); //System.out.println("exiting run " + finished); stop(); // call to shutdown libs? } public void display() { if (PApplet.THREAD_DEBUG) println(Thread.currentThread().getName() + " formerly nextFrame()"); if (looping || redraw) { /* if (frameCount == 0) { // needed here for the sync //createGraphics(); // set up a dummy graphics in case size() is never // called inside setup size(INITIAL_WIDTH, INITIAL_HEIGHT); } */ // g may be rebuilt inside here, so turning of the sync //synchronized (g) { // use a different sync object synchronized (glock) { if (THREAD_DEBUG) println(Thread.currentThread().getName() + " 1a beginFrame"); g.beginFrame(); if (THREAD_DEBUG) println(Thread.currentThread().getName() + " 1b draw"); if (frameCount == 0) { try { //System.out.println("attempting setup"); //System.out.println("into try"); setup(); //g.defaults(); //System.out.println("done attempting setup"); //System.out.println("out of try"); g.postSetup(); // FIXME } catch (RuntimeException e) { //System.out.println("catching a cold " + e.getMessage()); if (e.getMessage().indexOf(NEW_RENDERER) != -1) { //System.out.println("got new renderer"); return; //continue; // will this work? } else { throw e; } } // if depth() is called inside setup, pixels/width/height // will be ok by the time it's back out again //this.pixels = g.pixels; // make em call loadPixels // now for certain that we've got a valid size this.width = g.width; this.height = g.height; } else { // update the current framerate if (framerateLastMillis != 0) { float elapsed = (float) (System.currentTimeMillis() - framerateLastMillis); if (elapsed != 0) { framerate = (framerate * 0.9f) + ((1.0f / (elapsed / 1000.0f)) * 0.1f); } } framerateLastMillis = System.currentTimeMillis(); if (framerateTarget != 0) { //System.out.println("delaying"); if (framerateLastDelayTime == 0) { framerateLastDelayTime = System.currentTimeMillis(); } else { long timeToLeave = framerateLastDelayTime + (long)(1000.0f / framerateTarget); int napTime = (int) (timeToLeave - System.currentTimeMillis()); framerateLastDelayTime = timeToLeave; delay(napTime); } } preMethods.handle(); pmouseX = dmouseX; pmouseY = dmouseY; draw(); // dmouseX/Y is updated only once per frame dmouseX = mouseX; dmouseY = mouseY; // these are called *after* loop so that valid // drawing commands can be run inside them. it can't // be before, since a call to background() would wipe // out anything that had been drawn so far. dequeueMouseEvents(); dequeueKeyEvents(); if (THREAD_DEBUG) println(Thread.currentThread().getName() + " 2b endFrame"); drawMethods.handle(); //for (int i = 0; i < libraryCount; i++) { //if (libraryCalls[i][PLibrary.DRAW]) libraries[i].draw(); //} redraw = false; // unset 'redraw' flag in case it was set // (only do this once draw() has run, not just setup()) } g.endFrame(); if (recorder != null) { recorder.endFrame(); recorder = null; } //} // older end sync //update(); // formerly 'update' //if (firstFrame) firstFrame = false; // internal frame counter frameCount++; if (THREAD_DEBUG) println(Thread.currentThread().getName() + " 3a calling repaint() " + frameCount); repaint(); if (THREAD_DEBUG) println(Thread.currentThread().getName() + " 3b calling Toolkit.sync " + frameCount); getToolkit().sync(); // force repaint now (proper method) if (THREAD_DEBUG) println(Thread.currentThread().getName() + " 3c done " + frameCount); //if (THREAD_DEBUG) println(" 3d waiting"); //wait(); //if (THREAD_DEBUG) println(" 3d out of wait"); //frameCount++; postMethods.handle(); //for (int i = 0; i < libraryCount; i++) { //if (libraryCalls[i][PLibrary.POST]) libraries[i].post(); //} } // end of synchronize } } ////////////////////////////////////////////////////////////// MouseEvent mouseEventQueue[] = new MouseEvent[10]; int mouseEventCount; protected void enqueueMouseEvent(MouseEvent e) { synchronized (mouseEventQueue) { if (mouseEventCount == mouseEventQueue.length) { MouseEvent temp[] = new MouseEvent[mouseEventCount << 1]; System.arraycopy(mouseEventQueue, 0, temp, 0, mouseEventCount); mouseEventQueue = temp; } mouseEventQueue[mouseEventCount++] = e; } } protected void dequeueMouseEvents() { synchronized (mouseEventQueue) { for (int i = 0; i < mouseEventCount; i++) { mouseEvent = mouseEventQueue[i]; handleMouseEvent(mouseEvent); } mouseEventCount = 0; } } /** * Actually take action based on a mouse event. * Internally updates mouseX, mouseY, mousePressed, and mouseEvent. * Then it calls the event type with no params, * i.e. mousePressed() or mouseReleased() that the user may have * overloaded to do something more useful. */ protected void handleMouseEvent(MouseEvent event) { pmouseX = emouseX; pmouseY = emouseY; mouseX = event.getX(); mouseY = event.getY(); mouseEvent = event; mouseEventMethods.handle(new Object[] { event }); /* for (int i = 0; i < libraryCount; i++) { if (libraryCalls[i][PLibrary.MOUSE]) { libraries[i].mouse(event); // endNet/endSerial etc } } */ // this used to only be called on mouseMoved and mouseDragged // change it back if people run into trouble if (firstMouse) { pmouseX = mouseX; pmouseY = mouseY; dmouseX = mouseX; dmouseY = mouseY; firstMouse = false; } switch (event.getID()) { case MouseEvent.MOUSE_PRESSED: mousePressed = true; mousePressed(); break; case MouseEvent.MOUSE_RELEASED: mousePressed = false; mouseReleased(); break; case MouseEvent.MOUSE_CLICKED: mouseClicked(); break; case MouseEvent.MOUSE_DRAGGED: mouseDragged(); break; case MouseEvent.MOUSE_MOVED: mouseMoved(); break; } emouseX = mouseX; emouseY = mouseY; } /** * Figure out how to process a mouse event. When loop() has been * called, the events will be queued up until drawing is complete. * If noLoop() has been called, then events will happen immediately. */ protected void checkMouseEvent(MouseEvent event) { if (looping) { enqueueMouseEvent(event); } else { handleMouseEvent(event); } } /** * If you override this or any function that takes a "MouseEvent e" * without calling its super.mouseXxxx() then mouseX, mouseY, * mousePressed, and mouseEvent will no longer be set. */ public void mousePressed(MouseEvent e) { checkMouseEvent(e); } public void mouseReleased(MouseEvent e) { checkMouseEvent(e); } public void mouseClicked(MouseEvent e) { checkMouseEvent(e); } public void mouseEntered(MouseEvent e) { checkMouseEvent(e); } public void mouseExited(MouseEvent e) { checkMouseEvent(e); } public void mouseDragged(MouseEvent e) { checkMouseEvent(e); } public void mouseMoved(MouseEvent e) { checkMouseEvent(e); } /** * Mouse has been pressed, and should be considered "down" * until mouseReleased() is called. If you must, use * int button = mouseEvent.getButton(); * to figure out which button was clicked. It will be one of: * MouseEvent.BUTTON1, MouseEvent.BUTTON2, MouseEvent.BUTTON3 * Note, however, that this is completely inconsistent across * platforms. */ public void mousePressed() { } /** * Mouse button has been released. */ public void mouseReleased() { } /** * When the mouse is clicked, mousePressed() will be called, * then mouseReleased(), then mouseClicked(). Note that * mousePressed is already false inside of mouseClicked(). */ public void mouseClicked() { } /** * Mouse button is pressed and the mouse has been dragged. */ public void mouseDragged() { } /** * Mouse button is not pressed but the mouse has changed locations. */ public void mouseMoved() { } ////////////////////////////////////////////////////////////// KeyEvent keyEventQueue[] = new KeyEvent[10]; int keyEventCount; protected void enqueueKeyEvent(KeyEvent e) { synchronized (keyEventQueue) { if (keyEventCount == keyEventQueue.length) { KeyEvent temp[] = new KeyEvent[keyEventCount << 1]; System.arraycopy(keyEventQueue, 0, temp, 0, keyEventCount); keyEventQueue = temp; } keyEventQueue[keyEventCount++] = e; } } protected void dequeueKeyEvents() { synchronized (keyEventQueue) { for (int i = 0; i < keyEventCount; i++) { keyEvent = keyEventQueue[i]; handleKeyEvent(keyEvent); } keyEventCount = 0; } } protected void handleKeyEvent(KeyEvent event) { keyEvent = event; key = event.getKeyChar(); keyCode = event.getKeyCode(); keyEventMethods.handle(new Object[] { event }); /* for (int i = 0; i < libraryCount; i++) { if (libraryCalls[i][PLibrary.KEY]) { libraries[i].key(event); // endNet/endSerial etc } } */ switch (event.getID()) { case KeyEvent.KEY_PRESSED: keyPressed = true; keyPressed(); break; case KeyEvent.KEY_RELEASED: keyPressed = false; keyReleased(); break; case KeyEvent.KEY_TYPED: keyTyped(); break; } } protected void checkKeyEvent(KeyEvent event) { if (looping) { enqueueKeyEvent(event); } else { handleKeyEvent(event); } } /** * Overriding keyXxxxx(KeyEvent e) functions will cause the 'key', * 'keyCode', and 'keyEvent' variables to no longer work; * key events will no longer be queued until the end of draw(); * and the keyPressed(), keyReleased() and keyTyped() methods * will no longer be called. */ public void keyPressed(KeyEvent e) { checkKeyEvent(e); } public void keyReleased(KeyEvent e) { checkKeyEvent(e); } public void keyTyped(KeyEvent e) { checkKeyEvent(e); } /** * Called each time a single key on the keyboard is pressed. *
* Examples for key handling: * (Tested on Windows XP, please notify if different on other * platforms, I have a feeling Mac OS and Linux may do otherwise) *
* 1. Pressing 'a' on the keyboard: * keyPressed with key == 'a' and keyCode == 'A' * keyTyped with key == 'a' and keyCode == 0 * keyReleased with key == 'a' and keyCode == 'A' * * 2. Pressing 'A' on the keyboard: * keyPressed with key == 'A' and keyCode == 'A' * keyTyped with key == 'A' and keyCode == 0 * keyReleased with key == 'A' and keyCode == 'A' * * 3. Pressing 'shift', then 'a' on the keyboard (caps lock is off): * keyPressed with key == CODED and keyCode == SHIFT * keyPressed with key == 'A' and keyCode == 'A' * keyTyped with key == 'A' and keyCode == 0 * keyReleased with key == 'A' and keyCode == 'A' * keyReleased with key == CODED and keyCode == SHIFT * * 4. Holding down the 'a' key. * The following will happen several times, * depending on your machine's "key repeat rate" settings: * keyPressed with key == 'a' and keyCode == 'A' * keyTyped with key == 'a' and keyCode == 0 * When you finally let go, you'll get: * keyReleased with key == 'a' and keyCode == 'A' * * 5. Pressing and releasing the 'shift' key * keyPressed with key == CODED and keyCode == SHIFT * keyReleased with key == CODED and keyCode == SHIFT * (note there is no keyTyped) * * 6. Pressing the tab key in an applet with Java 1.4 will * normally do nothing, but PApplet dynamically shuts * this behavior off if Java 1.4 is in use (tested 1.4.2_05 Windows). * Java 1.1 (Microsoft VM) passes the TAB key through normally. * Not tested on other platforms or for 1.3. **/ public void keyPressed() { } /** * See keyPressed(). */ public void keyReleased() { } /** * Only called for "regular" keys like letters, * see keyPressed() for full documentation. */ public void keyTyped() { } ////////////////////////////////////////////////////////////// // i am focused man, and i'm not afraid of death. // and i'm going all out. i circle the vultures in a van // and i run the block. public void focusGained() { } public void focusGained(FocusEvent e) { focused = true; focusGained(); } public void focusLost() { } public void focusLost(FocusEvent e) { focused = false; focusLost(); } ////////////////////////////////////////////////////////////// // getting the time /** * Get the number of milliseconds since the applet started. *
* This is a function, rather than a variable, because it may * change multiple times per frame. */ public int millis() { return (int) (System.currentTimeMillis() - millisOffset); } /** Seconds position of the current time. */ static public int second() { return Calendar.getInstance().get(Calendar.SECOND); } /** Minutes position of the current time. */ static public int minute() { return Calendar.getInstance().get(Calendar.MINUTE); } /** * Hour position of the current time in international format (0-23). *
* To convert this value to American time:
*
int yankeeHour = (hour() % 12); * if (yankeeHour == 0) yankeeHour = 12;*/ static public int hour() { return Calendar.getInstance().get(Calendar.HOUR_OF_DAY); } /** * Get the current day of the month (1 through 31). *
* If you're looking for the day of the week (M-F or whatever) * or day of the year (1..365) then use java's Calendar.get() */ static public int day() { return Calendar.getInstance().get(Calendar.DAY_OF_MONTH); } /** * Get the current month in range 1 through 12. */ static public int month() { // months are number 0..11 so change to colloquial 1..12 return Calendar.getInstance().get(Calendar.MONTH) + 1; } /** * Get the current year. */ static public int year() { return Calendar.getInstance().get(Calendar.YEAR); } ////////////////////////////////////////////////////////////// // controlling time and playing god /** * I'm not sure if this is even helpful anymore. */ public void delay(int napTime) { if (frameCount == 0) return; if (napTime > 0) { try { Thread.sleep(napTime); } catch (InterruptedException e) { } } } /** * Set a target framerate. This will cause delay() to be called * after each frame to allow for a specific rate to be set. */ public void framerate(float framerateTarget) { this.framerateTarget = framerateTarget; } ////////////////////////////////////////////////////////////// /** * Get a param from the web page, or (eventually) * from a properties file. */ public String param(String what) { if (online) { return getParameter(what); } else { System.err.println("param() only works inside a web browser"); } return null; } /** * Show status in the status bar of a web browser, or in the * System.out console. Eventually this might show status in the * p5 environment itself, rather than relying on the console. */ public void status(String what) { if (online) { showStatus(what); } else { System.out.println(what); // something more interesting? } } /** * Link to an external page without all the muss. Currently * only works for applets, but eventually should be implemented * for applications as well, using code from PdeBase. */ public void link(String here) { if (!online) { System.err.println("Can't open " + here); System.err.println("link() only works inside a web browser"); return; } try { getAppletContext().showDocument(new URL(here)); } catch (Exception e) { System.err.println("Could not open " + here); e.printStackTrace(); } } public void link(String here, String there) { if (!online) { System.err.println("Can't open " + here); System.err.println("link() only works inside a web browser"); return; } try { getAppletContext().showDocument(new URL(here), there); } catch (Exception e) { System.err.println("Could not open " + here); e.printStackTrace(); } } ////////////////////////////////////////////////////////////// /** * Function for an applet/application to kill itself and * display an error. Mostly this is here to be improved later. */ public void die(String what) { stop(); throw new RuntimeException(what); /* if (online) { System.err.println("i'm dead.. " + what); } else { System.err.println(what); System.exit(1); } */ } /** * Same as above but with an exception. Also needs work. */ public void die(String what, Exception e) { if (e != null) e.printStackTrace(); die(what); } /** * Explicitly exit the applet. Inserted as a call for static * mode apps, but is generally necessary because apps no longer * have draw/loop separation. */ public void exit() { stop(); // TODO if not running as an applet, do a System.exit() here } ////////////////////////////////////////////////////////////// // SCREEN GRABASS /** * This version of save() is an override of PImage.save(), * rather than calling g.save(). This version properly saves * the image to the applet folder (whereas save doesn't know * where to put things) */ public void save(String filename) { g.save(savePath(filename)); } /** * Grab an image of what's currently in the drawing area and save it * as a .tif or .tga file. *
* Best used just before endFrame() at the end of your loop(). * This can only create .tif or .tga images, so if neither extension * is specified it defaults to writing a tiff and adds a .tif suffix. */ public void saveFrame() { if (online) { System.err.println("Can't use saveFrame() when running in a browser."); return; } //File file = new File(folder, "screen-" + nf(frame, 4) + ".tif"); //save(savePath("screen-" + nf(frameCount, 4) + ".tif")); //save("screen-" + nf(frame, 4) + ".tif"); save("screen-" + nf(frameCount, 4) + ".tif"); } /** * Save the current frame as a .tif or .tga image. *
* The String passed in can contain a series of # signs * that will be replaced with the screengrab number. *
* i.e. saveFrame("blah-####.tif");
* // saves a numbered tiff image, replacing the
* // # signs with zeros and the frame number
*/
public void saveFrame(String what) {
if (online) {
System.err.println("Can't use saveFrame() when running in a browser.");
return;
}
int first = what.indexOf('#');
int last = what.lastIndexOf('#');
if (first == -1) {
save(what);
} else {
String prefix = what.substring(0, first);
int count = last - first + 1;
String suffix = what.substring(last + 1);
//File file = new File(folder, prefix + nf(frame, count) + suffix);
// in case the user tries to make subdirs with the filename
//new File(file.getParent()).mkdirs();
//save(file.getAbsolutePath());
save(savePath(prefix + nf(frameCount, count) + suffix));
}
}
//////////////////////////////////////////////////////////////
// CURSOR
// based on code contributed by amit pitaru and jonathan feinberg
int cursor_type = ARROW; // cursor type
boolean cursor_visible = true; // cursor visibility flag
PImage invisible_cursor;
/**
* Set the cursor type
*/
public void cursor(int _cursor_type) {
//if (cursor_visible && _cursor_type != cursor_type) {
setCursor(Cursor.getPredefinedCursor(_cursor_type));
//}
cursor_visible = true;
cursor_type = _cursor_type;
}
/**
* Set a custom cursor to an image with a specific hotspot.
* Only works with JDK 1.2 and later.
* Currently seems to be broken on Java 1.4 for Mac OS X
*/
public void cursor(PImage image, int hotspotX, int hotspotY) {
//if (!isOneTwoOrBetter()) {
if (javaVersion < 1.2f) {
System.err.println("Java 1.2 or higher is required to use cursor()");
System.err.println("(You're using version " + javaVersionName + ")");
return;
}
// don't set this as cursor type, instead use cursor_type
// to save the last cursor used in case cursor() is called
//cursor_type = Cursor.CUSTOM_CURSOR;
Image jimage =
createImage(new MemoryImageSource(image.width, image.height,
image.pixels, 0, image.width));
//Toolkit tk = Toolkit.getDefaultToolkit();
Point hotspot = new Point(hotspotX, hotspotY);
try {
Method mCustomCursor =
Toolkit.class.getMethod("createCustomCursor",
new Class[] { Image.class,
Point.class,
String.class, });
Cursor cursor =
(Cursor)mCustomCursor.invoke(Toolkit.getDefaultToolkit(),
new Object[] { jimage,
hotspot,
"no cursor" });
setCursor(cursor);
cursor_visible = true;
} catch (NoSuchMethodError e) {
System.err.println("cursor() is not available on " +
nf(javaVersion, 1, 1));
} catch (IndexOutOfBoundsException e) {
System.err.println("cursor() error: the hotspot " + hotspot +
" is out of bounds for the given image.");
} catch (Exception e) {
System.err.println(e);
}
}
/**
* Show the cursor after noCursor() was called.
* Notice that the program remembers the last set cursor type
*/
public void cursor() {
// maybe should always set here? seems dangerous, since
// it's likely that java will set the cursor to something
// else on its own, and the applet will be stuck b/c bagel
// thinks that the cursor is set to one particular thing
if (!cursor_visible) {
cursor_visible = true;
setCursor(Cursor.getPredefinedCursor(cursor_type));
}
}
/**
* Hide the cursor by creating a transparent image
* and using it as a custom cursor.
*/
public void noCursor() {
if (!cursor_visible) return; // don't hide if already hidden.
if (invisible_cursor == null) {
//invisible_cursor = new PImage(new int[32*32], 32, 32, RGBA);
invisible_cursor = new PImage(new int[16*16], 16, 16, ARGB);
}
// was formerly 16x16, but the 0x0 was added by jdf as a fix
// for macosx, which didn't wasn't honoring the invisible cursor
cursor(invisible_cursor, 0, 0);
cursor_visible = false;
}
//////////////////////////////////////////////////////////////
static public void print(byte what) {
System.out.print(what);
System.out.flush();
}
static public void print(boolean what) {
System.out.print(what);
System.out.flush();
}
static public void print(char what) {
System.out.print(what);
System.out.flush();
}
static public void print(int what) {
System.out.print(what);
System.out.flush();
}
static public void print(float what) {
System.out.print(what);
System.out.flush();
}
static public void print(double what) {
System.out.print(what);
System.out.flush();
}
static public void print(String what) {
System.out.print(what);
System.out.flush();
}
static public void print(Object what) {
System.out.print(what.toString());
System.out.flush();
}
//
static public void println() {
System.out.println();
}
//
static public void println(byte what) {
print(what); System.out.println();
}
static public void println(boolean what) {
print(what); System.out.println();
}
static public void println(char what) {
print(what); System.out.println();
}
static public void println(int what) {
print(what); System.out.println();
}
static public void println(float what) {
print(what); System.out.println();
}
static public void println(double what) {
print(what); System.out.println();
}
static public void println(String what) {
print(what); System.out.println();
}
static public void println(Object what) {
System.out.println(what.toString());
}
//
static public void printarr(byte what[]) {
for (int i = 0; i < what.length; i++) System.out.println(what[i]);
System.out.flush();
}
static public void printarr(boolean what[]) {
for (int i = 0; i < what.length; i++) System.out.println(what[i]);
System.out.flush();
}
static public void printarr(char what[]) {
for (int i = 0; i < what.length; i++) System.out.println(what[i]);
System.out.flush();
}
static public void printarr(int what[]) {
for (int i = 0; i < what.length; i++) System.out.println(what[i]);
System.out.flush();
}
static public void printarr(float what[]) {
for (int i = 0; i < what.length; i++) System.out.println(what[i]);
System.out.flush();
}
static public void printarr(double what[]) {
for (int i = 0; i < what.length; i++) System.out.println(what[i]);
System.out.flush();
}
static public void printarr(String what[]) {
for (int i = 0; i < what.length; i++) System.out.println(what[i]);
System.out.flush();
}
static public void printarr(Object what[]) {
for (int i = 0; i < what.length; i++) System.out.println(what[i]);
System.out.flush();
}
//
/*
public void printvar(String name) {
try {
Field field = getClass().getDeclaredField(name);
println(name + " = " + field.get(this));
} catch (Exception e) {
e.printStackTrace();
}
}
*/
//////////////////////////////////////////////////////////////
// MATH
// lots of convenience methods for math with floats.
// doubles are overkill for processing applets, and casting
// things all the time is annoying, thus the functions below.
static public final float abs(float n) {
return (n < 0) ? -n : n;
}
static public final int abs(int n) {
return (n < 0) ? -n : n;
}
static public final float sq(float a) {
return a*a;
}
static public final float sqrt(float a) {
return (float)Math.sqrt(a);
}
static public final float log(float a) {
return (float)Math.log(a);
}
static public final float exp(float a) {
return (float)Math.exp(a);
}
static public final float pow(float a, float b) {
return (float)Math.pow(a, b);
}
static public final float max(float a, float b) {
return Math.max(a, b);
}
static public final float max(float a, float b, float c) {
return Math.max(a, Math.max(b, c));
}
static public final float min(float a, float b) {
return Math.min(a, b);
}
static public final float min(float a, float b, float c) {
return Math.min(a, Math.min(b, c));
}
static public final float lerp(float a, float b, float amt) {
return a + (b-a) * amt;
}
static public final float constrain(float amt, float low, float high) {
return (amt < low) ? low : ((amt > high) ? high : amt);
}
static public final int max(int a, int b) {
return (a > b) ? a : b;
}
static public final int max(int a, int b, int c) {
return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);
}
static public final int min(int a, int b) {
return (a < b) ? a : b;
}
static public final int min(int a, int b, int c) {
return (a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c);
}
static public final int constrain(int amt, int low, int high) {
return (amt < low) ? low : ((amt > high) ? high : amt);
}
public final float sin(float angle) {
if ((g != null) && (g.angleMode == DEGREES)) angle *= DEG_TO_RAD;
return (float)Math.sin(angle);
}
public final float cos(float angle) {
if ((g != null) && (g.angleMode == DEGREES)) angle *= DEG_TO_RAD;
return (float)Math.cos(angle);
}
public final float tan(float angle) {
if ((g != null) && (g.angleMode == DEGREES)) angle *= DEG_TO_RAD;
return (float)Math.tan(angle);
}
public final float asin(float value) {
return ((g != null) && (g.angleMode == DEGREES)) ?
((float)Math.asin(value) * RAD_TO_DEG) : (float)Math.asin(value);
}
public final float acos(float value) {
return ((g != null) && (g.angleMode == DEGREES)) ?
((float)Math.acos(value) * RAD_TO_DEG) : (float)Math.acos(value);
}
public final float atan(float value) {
return ((g != null) && (g.angleMode == DEGREES)) ?
((float)Math.atan(value) * RAD_TO_DEG) : (float)Math.atan(value);
}
public final float atan2(float a, float b) {
return ((g != null) && (g.angleMode == DEGREES)) ?
((float)Math.atan2(a, b) * RAD_TO_DEG) : (float)Math.atan2(a, b);
}
static public final float degrees(float radians) {
return radians * RAD_TO_DEG;
}
static public final float radians(float degrees) {
return degrees * DEG_TO_RAD;
}
static public final float ceil(float what) {
return (float) Math.ceil(what);
}
static public final float floor(float what) {
return (float) Math.floor(what);
}
static public final float round(float what) {
return Math.round(what);
}
static public final float mag(float a, float b) {
return (float)Math.sqrt(a*a + b*b);
}
static public final float mag(float a, float b, float c) {
return (float)Math.sqrt(a*a + b*b + c*c);
}
static public final float dist(float x1, float y1, float x2, float y2) {
return sqrt(sq(x2-x1) + sq(y2-y1));
}
static public final float dist(float x1, float y1, float z1,
float x2, float y2, float z2) {
return sqrt(sq(x2-x1) + sq(y2-y1) + sq(z2-z1));
}
//////////////////////////////////////////////////////////////
// RANDOM NUMBERS
Random internalRandom;
/**
* Return a random number in the range [0, howbig).
* * The number returned will range from zero up to * (but not including) 'howbig'. */ public final float random(float howbig) { // for some reason (rounding error?) Math.random() * 3 // can sometimes return '3' (once in ~30 million tries) // so a check was added to avoid the inclusion of 'howbig' // avoid an infinite loop if (howbig == 0) return 0; // internal random number object if (internalRandom == null) internalRandom = new Random(); float value = 0; do { //value = (float)Math.random() * howbig; value = internalRandom.nextFloat() * howbig; } while (value == howbig); return value; } /** * Return a random number in the range [howsmall, howbig). *
* The number returned will range from 'howsmall' up to * (but not including 'howbig'. *
* If howsmall is >= howbig, howsmall will be returned,
* meaning that random(5, 5) will return 5 (useful)
* and random(7, 4) will return 7 (not useful.. better idea?)
*/
public final float random(float howsmall, float howbig) {
if (howsmall >= howbig) return howsmall;
float diff = howbig - howsmall;
return random(diff) + howsmall;
}
public final void randomSeed(long what) {
// internal random number object
if (internalRandom == null) internalRandom = new Random();
internalRandom.setSeed(what);
}
//////////////////////////////////////////////////////////////
// PERLIN NOISE
// [toxi 040903]
// octaves and amplitude amount per octave are now user controlled
// via the noiseDetail() function.
// [toxi 030902]
// cleaned up code and now using bagel's cosine table to speed up
// [toxi 030901]
// implementation by the german demo group farbrausch
// as used in their demo "art": http://www.farb-rausch.de/fr010src.zip
static final int PERLIN_YWRAPB = 4;
static final int PERLIN_YWRAP = 1<
* To use this on numbers, first pass the array to nf() or nfs()
* to get a list of String objects, then use join on that.
*
* The whitespace characters are "\t\n\r\f", which are the defaults
* for java.util.StringTokenizer, plus the unicode non-breaking space
* character, which is found commonly on files created by or used
* in conjunction with Mac OS X (character 160, or 0x00A0 in hex).
*
* This operates differently than the others, where the
* single delimeter is the only breaking point, and consecutive
* delimeters will produce an empty string (""). This way,
* one can split on tab characters, but maintain the column
* alignments (of say an excel file) where there are empty columns.
*/
static public String[] split(String what, char delim) {
// do this so that the exception occurs inside the user's
// program, rather than appearing to be a bug inside split()
if (what == null) return null;
//return split(what, String.valueOf(delim)); // huh
char chars[] = what.toCharArray();
int splitCount = 0; //1;
for (int i = 0; i < chars.length; i++) {
if (chars[i] == delim) splitCount++;
}
// make sure that there is something in the input string
//if (chars.length > 0) {
// if the last char is a delimeter, get rid of it..
//if (chars[chars.length-1] == delim) splitCount--;
// on second thought, i don't agree with this, will disable
//}
if (splitCount == 0) {
String splits[] = new String[1];
splits[0] = new String(what);
return splits;
}
//int pieceCount = splitCount + 1;
String splits[] = new String[splitCount + 1];
int splitIndex = 0;
int startIndex = 0;
for (int i = 0; i < chars.length; i++) {
if (chars[i] == delim) {
splits[splitIndex++] =
new String(chars, startIndex, i-startIndex);
startIndex = i + 1;
}
}
//if (startIndex != chars.length) {
splits[splitIndex] =
new String(chars, startIndex, chars.length-startIndex);
//}
return splits;
}
//////////////////////////////////////////////////////////////
// CASTING FUNCTIONS, INSERTED BY PREPROC
static final public boolean toBoolean(char what) {
return ((what == 't') || (what == 'T') || (what == '1'));
}
static final public boolean toBoolean(int what) { // this will cover byte
return (what != 0);
}
static final public boolean toBoolean(float what) {
return (what != 0);
}
static final public boolean toBoolean(String what) {
return new Boolean(what).booleanValue();
}
//
static final public boolean[] toBoolean(char what[]) {
boolean outgoing[] = new boolean[what.length];
for (int i = 0; i < what.length; i++) {
outgoing[i] =
((what[i] == 't') || (what[i] == 'T') || (what[i] == '1'));
}
return outgoing;
}
static final public boolean[] toBoolean(byte what[]) {
boolean outgoing[] = new boolean[what.length];
for (int i = 0; i < what.length; i++) {
outgoing[i] = (what[i] != 0);
}
return outgoing;
}
static final public boolean[] toBoolean(float what[]) {
boolean outgoing[] = new boolean[what.length];
for (int i = 0; i < what.length; i++) {
outgoing[i] = (what[i] != 0);
}
return outgoing;
}
static final public boolean[] toBoolean(String what[]) {
boolean outgoing[] = new boolean[what.length];
for (int i = 0; i < what.length; i++) {
outgoing[i] = new Boolean(what[i]).booleanValue();
}
return outgoing;
}
//
static final public byte toByte(boolean what) {
return what ? (byte)1 : 0;
}
static final public byte toByte(char what) {
return (byte) what;
}
static final public byte toByte(int what) {
return (byte) what;
}
static final public byte toByte(float what) { // nonsensical
return (byte) what;
}
static final public byte[] toByte(String what) { // note: array[]
return what.getBytes();
}
//
static final public byte[] toByte(boolean what[]) {
byte outgoing[] = new byte[what.length];
for (int i = 0; i < what.length; i++) {
outgoing[i] = what[i] ? (byte)1 : 0;
}
return outgoing;
}
static final public byte[] toByte(char what[]) {
byte outgoing[] = new byte[what.length];
for (int i = 0; i < what.length; i++) {
outgoing[i] = (byte) what[i];
}
return outgoing;
}
static final public byte[] toByte(int what[]) {
byte outgoing[] = new byte[what.length];
for (int i = 0; i < what.length; i++) {
outgoing[i] = (byte) what[i];
}
return outgoing;
}
static final public byte[] toByte(float what[]) { // nonsensical
byte outgoing[] = new byte[what.length];
for (int i = 0; i < what.length; i++) {
outgoing[i] = (byte) what[i];
}
return outgoing;
}
static final public byte[][] toByte(String what[]) { // note: array[][]
byte outgoing[][] = new byte[what.length][];
for (int i = 0; i < what.length; i++) {
outgoing[i] = what[i].getBytes();
}
return outgoing;
}
//
static final public char toChar(boolean what) { // 0/1 or T/F ?
return what ? 't' : 'f';
}
static final public char toChar(byte what) {
return (char) (what & 0xff);
}
static final public char toChar(int what) {
return (char) what;
}
static final public char toChar(float what) { // nonsensical
return (char) what;
}
static final public char[] toChar(String what) { // note: array[]
return what.toCharArray();
}
//
static final public char[] toChar(boolean what[]) { // 0/1 or T/F ?
char outgoing[] = new char[what.length];
for (int i = 0; i < what.length; i++) {
outgoing[i] = what[i] ? 't' : 'f';
}
return outgoing;
}
static final public char[] toChar(int what[]) {
char outgoing[] = new char[what.length];
for (int i = 0; i < what.length; i++) {
outgoing[i] = (char) what[i];
}
return outgoing;
}
static final public char[] toChar(byte what[]) {
char outgoing[] = new char[what.length];
for (int i = 0; i < what.length; i++) {
outgoing[i] = (char) (what[i] & 0xff);
}
return outgoing;
}
static final public char[] toChar(float what[]) { // nonsensical
char outgoing[] = new char[what.length];
for (int i = 0; i < what.length; i++) {
outgoing[i] = (char) what[i];
}
return outgoing;
}
static final public char[][] toChar(String what[]) { // note: array[][]
char outgoing[][] = new char[what.length][];
for (int i = 0; i < what.length; i++) {
outgoing[i] = what[i].toCharArray();
}
return outgoing;
}
//
static final public int toInt(boolean what) {
return what ? 1 : 0;
}
static final public int toInt(byte what) { // note this unsigns
return what & 0xff;
}
static final public int toInt(char what) {
return what;
}
static final public int toInt(float what) {
return (int) what;
}
static final public int toInt(String what) {
try {
return Integer.parseInt(what);
} catch (NumberFormatException e) { }
return 0;
}
static final public int toInt(String what, int otherwise) {
try {
return Integer.parseInt(what);
} catch (NumberFormatException e) { }
return otherwise;
}
//
static final public int[] toInt(boolean what[]) {
int list[] = new int[what.length];
for (int i = 0; i < what.length; i++) {
list[i] = what[i] ? 1 : 0;
}
return list;
}
static final public int[] toInt(byte what[]) { // note this unsigns
int list[] = new int[what.length];
for (int i = 0; i < what.length; i++) {
list[i] = (what[i] & 0xff);
}
return list;
}
static final public int[] toInt(char what[]) {
int list[] = new int[what.length];
for (int i = 0; i < what.length; i++) {
list[i] = what[i];
}
return list;
}
static public int[] toInt(float what[]) {
int inties[] = new int[what.length];
for (int i = 0; i < what.length; i++) {
inties[i] = (int)what[i];
}
return inties;
}
/**
* Make an array of int elements from an array of String objects.
* If the String can't be parsed as a number, it will be set to zero.
*
* String s[] = { "1", "300", "44" };
* int numbers[] = toInt(s);
*
* numbers will contain { 1, 300, 44 }
*/
static public int[] toInt(String what[]) {
return toInt(what, 0);
}
/**
* Make an array of int elements from an array of String objects.
* If the String can't be parsed as a number, its entry in the
* array will be set to the value of the "missing" parameter.
*
* String s[] = { "1", "300", "apple", "44" };
* int numbers[] = toInt(s, 9999);
*
* numbers will contain { 1, 300, 9999, 44 }
*/
static public int[] toInt(String what[], int missing) {
int output[] = new int[what.length];
for (int i = 0; i < what.length; i++) {
try {
output[i] = Integer.parseInt(what[i]);
} catch (NumberFormatException e) {
output[i] = missing;
}
}
return output;
}
//
static final public float toFloat(boolean what) {
return what ? 1 : 0;
}
static final public float toFloat(int what) {
return (float)what;
}
static final public float toFloat(String what) {
//return new Float(what).floatValue();
return toFloat(what, Float.NaN);
}
static final public float toFloat(String what, float otherwise) {
try {
return new Float(what).floatValue();
} catch (NumberFormatException e) { }
return otherwise;
}
//
static final public float[] toFloat(boolean what[]) {
float floaties[] = new float[what.length];
for (int i = 0; i < what.length; i++) {
floaties[i] = what[i] ? 1 : 0;
}
return floaties;
}
static final public float[] toFloat(char what[]) {
float floaties[] = new float[what.length];
for (int i = 0; i < what.length; i++) {
floaties[i] = (char) what[i];
}
return floaties;
}
static final public float[] toFloat(int what[]) {
float floaties[] = new float[what.length];
for (int i = 0; i < what.length; i++) {
floaties[i] = what[i];
}
return floaties;
}
static final public float[] toFloat(String what[]) {
return toFloat(what, 0);
}
static final public float[] toFloat(String what[], float missing) {
float output[] = new float[what.length];
for (int i = 0; i < what.length; i++) {
try {
output[i] = new Float(what[i]).floatValue();
} catch (NumberFormatException e) {
output[i] = missing;
}
}
return output;
}
//
static final public String str(boolean x) { return String.valueOf(x); }
static final public String str(byte x) { return String.valueOf(x); }
static final public String str(char x) { return String.valueOf(x); }
static final public String str(short x) { return String.valueOf(x); }
static final public String str(int x) { return String.valueOf(x); }
static final public String str(float x) { return String.valueOf(x); }
static final public String str(long x) { return String.valueOf(x); }
static final public String str(double x) { return String.valueOf(x); }
//
static final public String[] str(boolean x[]) {
String s[] = new String[x.length];
for (int i = 0; i < x.length; i++) s[i] = String.valueOf(x);
return s;
}
static final public String[] str(byte x[]) {
String s[] = new String[x.length];
for (int i = 0; i < x.length; i++) s[i] = String.valueOf(x);
return s;
}
static final public String[] str(char x[]) {
String s[] = new String[x.length];
for (int i = 0; i < x.length; i++) s[i] = String.valueOf(x);
return s;
}
static final public String[] str(short x[]) {
String s[] = new String[x.length];
for (int i = 0; i < x.length; i++) s[i] = String.valueOf(x);
return s;
}
static final public String[] str(int x[]) {
String s[] = new String[x.length];
for (int i = 0; i < x.length; i++) s[i] = String.valueOf(x);
return s;
}
static final public String[] str(float x[]) {
String s[] = new String[x.length];
for (int i = 0; i < x.length; i++) s[i] = String.valueOf(x);
return s;
}
static final public String[] str(long x[]) {
String s[] = new String[x.length];
for (int i = 0; i < x.length; i++) s[i] = String.valueOf(x);
return s;
}
static final public String[] str(double x[]) {
String s[] = new String[x.length];
for (int i = 0; i < x.length; i++) s[i] = String.valueOf(x);
return s;
}
//////////////////////////////////////////////////////////////
// INT NUMBER FORMATTING
/**
* Integer number formatter.
*/
static private NumberFormat int_nf;
static private int int_nf_digits;
static public String[] nf(int num[], int digits) {
String formatted[] = new String[num.length];
for (int i = 0; i < formatted.length; i++) {
formatted[i] = nf(num[i], digits);
}
return formatted;
}
static public String nf(int num, int digits) {
if ((int_nf != null) && (int_nf_digits == digits)) {
return int_nf.format(num);
}
int_nf = NumberFormat.getInstance();
int_nf.setGroupingUsed(false); // no commas
int_nf.setMinimumIntegerDigits(digits);
int_nf_digits = digits;
return int_nf.format(num);
}
/**
* number format signed (or space)
* Formats a number but leaves a blank space in the front
* when it's positive so that it can be properly aligned with
* numbers that have a negative sign in front of them.
*/
static public String nfs(int num, int digits) {
return (num < 0) ? nf(num, digits) : (' ' + nf(num, digits));
}
static public String[] nfs(int num[], int digits) {
String formatted[] = new String[num.length];
for (int i = 0; i < formatted.length; i++) {
formatted[i] = nfs(num[i], digits);
}
return formatted;
}
//
/**
* number format positive (or plus)
* Formats a number, always placing a - or + sign
* in the front when it's negative or positive.
*/
static public String nfp(int num, int digits) {
return (num < 0) ? nf(num, digits) : ('+' + nf(num, digits));
}
static public String[] nfp(int num[], int digits) {
String formatted[] = new String[num.length];
for (int i = 0; i < formatted.length; i++) {
formatted[i] = nfp(num[i], digits);
}
return formatted;
}
//////////////////////////////////////////////////////////////
// FLOAT NUMBER FORMATTING
static private NumberFormat float_nf;
static private int float_nf_left, float_nf_right;
static public String[] nf(float num[], int left, int right) {
String formatted[] = new String[num.length];
for (int i = 0; i < formatted.length; i++) {
formatted[i] = nf(num[i], left, right);
}
return formatted;
}
static public String nf(float num, int left, int right) {
if ((float_nf != null) &&
(float_nf_left == left) && (float_nf_right == right)) {
return float_nf.format(num);
}
float_nf = NumberFormat.getInstance();
float_nf.setGroupingUsed(false); // no commas
if (left != 0) float_nf.setMinimumIntegerDigits(left);
if (right != 0) {
float_nf.setMinimumFractionDigits(right);
float_nf.setMaximumFractionDigits(right);
}
float_nf_left = left;
float_nf_right = right;
return float_nf.format(num);
}
/**
* Number formatter that takes into account whether the number
* has a sign (positive, negative, etc) in front of it.
*/
static public String[] nfs(float num[], int left, int right) {
String formatted[] = new String[num.length];
for (int i = 0; i < formatted.length; i++) {
formatted[i] = nfs(num[i], left, right);
}
return formatted;
}
static public String nfs(float num, int left, int right) {
return (num < 0) ? nf(num, left, right) : (' ' + nf(num, left, right));
}
static public String[] nfp(float num[], int left, int right) {
String formatted[] = new String[num.length];
for (int i = 0; i < formatted.length; i++) {
formatted[i] = nfp(num[i], left, right);
}
return formatted;
}
static public String nfp(float num, int left, int right) {
return (num < 0) ? nf(num, left, right) : ('+' + nf(num, left, right));
}
//////////////////////////////////////////////////////////////
// HEX/BINARY CONVERSION
static final public String hex(byte what) {
return hex(what, 2);
}
static final public String hex(char what) {
return hex(what, 4);
}
static final public String hex(int what) {
return hex(what, 8);
}
static final public String hex(int what, int digits) {
String stuff = Integer.toHexString(what).toUpperCase();
int length = stuff.length();
if (length > digits) {
return stuff.substring(length - digits);
} else if (length < digits) {
return "00000000".substring(8 - (digits-length)) + stuff;
}
return stuff;
}
static final int unhex(String what) {
return Integer.parseInt(what, 16);
}
//
/**
* Returns a String that contains the binary value of a byte.
* The returned value will always have 8 digits.
*/
static final public String binary(byte what) {
return binary(what, 8);
}
/**
* Returns a String that contains the binary value of a char.
* The returned value will always have 16 digits because chars
* are two bytes long.
*/
static final public String binary(char what) {
return binary(what, 16);
}
/**
* Returns a String that contains the binary value of an int.
* The length depends on the size of the number itself.
* An int can be up to 32 binary digits, but that seems like
* overkill for almost any situation, so this function just
* auto-sizes. If you want a specific number of digits (like all 32)
* use binary(int what, int digits) to specify how many digits.
*/
static final public String binary(int what) {
return Integer.toBinaryString(what);
//return binary(what, 32);
}
/**
* Returns a String that contains the binary value of an int.
* The digits parameter determines how many digits will be used.
*/
static final public String binary(int what, int digits) {
String stuff = Integer.toBinaryString(what);
int length = stuff.length();
if (length > digits) {
return stuff.substring(length - digits);
} else if (length < digits) {
int offset = 32 - (digits-length);
return "00000000000000000000000000000000".substring(offset) + stuff;
}
return stuff;
}
/**
* Unpack a binary String into an int.
* i.e. unbinary("00001000") would return 8.
*/
static final int unbinary(String what) {
return Integer.parseInt(what, 2);
}
//////////////////////////////////////////////////////////////
// COLOR FUNCTIONS
// moved here so that they can work without
// the graphics actually being instantiated (outside setup)
public final int color(int gray) {
if (g == null) {
if (gray > 255) gray = 255; else if (gray < 0) gray = 0;
return 0xff000000 | (gray << 16) | (gray << 8) | gray;
}
return g.color(gray);
}
public final int color(float fgray) {
if (g == null) {
int gray = (int) fgray;
if (gray > 255) gray = 255; else if (gray < 0) gray = 0;
return 0xff000000 | (gray << 16) | (gray << 8) | gray;
}
return g.color(fgray);
}
public final int color(int gray, int alpha) {
if (g == null) {
if (gray > 255) gray = 255; else if (gray < 0) gray = 0;
if (alpha > 255) alpha = 255; else if (alpha < 0) alpha = 0;
return (alpha << 24) | (gray << 16) | (gray << 8) | gray;
}
return g.color(gray, alpha);
}
public final int color(float fgray, float falpha) {
if (g == null) {
int gray = (int) fgray;
int alpha = (int) falpha;
if (gray > 255) gray = 255; else if (gray < 0) gray = 0;
if (alpha > 255) alpha = 255; else if (alpha < 0) alpha = 0;
return 0xff000000 | (gray << 16) | (gray << 8) | gray;
}
return g.color(fgray, falpha);
}
public final int color(int x, int y, int z) {
if (g == null) {
if (x > 255) x = 255; else if (x < 0) x = 0;
if (y > 255) y = 255; else if (y < 0) y = 0;
if (z > 255) z = 255; else if (z < 0) z = 0;
return 0xff000000 | (x << 16) | (y << 8) | z;
}
return g.color(x, y, z);
}
public final int color(float x, float y, float z) {
if (g == null) {
if (x > 255) x = 255; else if (x < 0) x = 0;
if (y > 255) y = 255; else if (y < 0) y = 0;
if (z > 255) z = 255; else if (z < 0) z = 0;
return 0xff000000 | ((int)x << 16) | ((int)y << 8) | (int)z;
}
return g.color(x, y, z);
}
public final int color(int x, int y, int z, int a) {
if (g == null) {
if (a > 255) a = 255; else if (a < 0) a = 0;
if (x > 255) x = 255; else if (x < 0) x = 0;
if (y > 255) y = 255; else if (y < 0) y = 0;
if (z > 255) z = 255; else if (z < 0) z = 0;
return (a << 24) | (x << 16) | (y << 8) | z;
}
return g.color(x, y, z, a);
}
public final int color(float x, float y, float z, float a) {
if (g == null) {
if (a > 255) a = 255; else if (a < 0) a = 0;
if (x > 255) x = 255; else if (x < 0) x = 0;
if (y > 255) y = 255; else if (y < 0) y = 0;
if (z > 255) z = 255; else if (z < 0) z = 0;
return ((int)a << 24) | ((int)x << 16) | ((int)y << 8) | (int)z;
}
return g.color(x, y, z, a);
}
//////////////////////////////////////////////////////////////
// MAIN
private static class WorkerVar {
private Thread thread;
WorkerVar(Thread t) { thread = t; }
synchronized Thread get() { return thread; }
synchronized void clear() { thread = null; }
}
class Worker {
private Object value;
private WorkerVar workerVar;
protected synchronized Object getValue() {
return value;
}
private synchronized void setValue(Object x) {
value = x;
}
public Object construct() {
try {
int anything = System.in.read();
if (anything == EXTERNAL_STOP) {
//System.out.println("got external stop");
// adding this for 0073.. need to stop libraries
// when the stop button is hit.
PApplet.this.stop();
finished = true;
//} else {
//print((char) anything);
}
} catch (IOException e) {
finished = true;
}
try {
Thread.sleep(250);
//Thread.sleep(100); // kick up latency for 0075?
} catch (InterruptedException e) { }
return null;
}
// removing this from SwingWorker
//public void finished() { }
public void interrupt() {
Thread t = workerVar.get();
if (t != null) {
t.interrupt();
}
workerVar.clear();
}
public Object get() {
while (true) {
Thread t = workerVar.get();
if (t == null) {
return getValue();
}
try {
t.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // propagate
return null;
}
}
}
public Worker() {
// removing this from SwingWorker
//final Runnable doFinished = new Runnable() {
// public void run() { finished(); }
// };
Runnable doConstruct = new Runnable() {
public void run() {
try {
setValue(construct());
} finally {
workerVar.clear();
}
// removing this from SwingWorker to avoid swing
//javax.swing.SwingUtilities.invokeLater(doFinished);
}
};
Thread t = new Thread(doConstruct);
workerVar = new WorkerVar(t);
}
public void start() {
Thread t = workerVar.get();
if (t != null) t.start();
}
}
public void setupExternal(Frame parentFrame) {
//externalRuntime = true;
/*
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
//while ((Thread.currentThread() == this) && !finished) {
try {
// is this what's causing all the trouble?
int anything = System.in.read();
if (anything == EXTERNAL_STOP) {
//System.out.println("********** STOPPING");
// adding this for 0073.. need to stop libraries
// when the stop button is hit.
v PApplet.this.stop();
//System.out.println("********** REALLY");
finished = true;
}
} catch (IOException e) {
// not tested (needed?) but seems correct
//stop();
finished = true;
//thread = null;
}
try {
Thread.sleep(250);
//Thread.sleep(100); // kick up latency for 0075?
} catch (InterruptedException e) { }
}
});
*/
/*
Thread ethread = new Thread() { //new Runnable() {
public void run() {
// this fixes the "code folder hanging bug" (mostly)
setPriority(Thread.MIN_PRIORITY);
*/
final Worker worker = new Worker();
//worker.start();
/*
final SwingWorker worker = new SwingWorker() {
public Object construct() {
//while ((Thread.currentThread() == this) && !finished) {
try {
// is this what's causing all the trouble?
int anything = System.in.read();
if (anything == EXTERNAL_STOP) {
//System.out.println("********** STOPPING");
// adding this for 0073.. need to stop libraries
// when the stop button is hit.
PApplet.this.stop();
//System.out.println("********** REALLY");
finished = true;
}
} catch (IOException e) {
// not tested (needed?) but seems correct
//stop();
finished = true;
//thread = null;
}
try {
Thread.sleep(250);
//Thread.sleep(100); // kick up latency for 0075?
} catch (InterruptedException e) { }
return null;
}
};
//ethread.start();
*/
parentFrame.addComponentListener(new ComponentAdapter() {
public void componentMoved(ComponentEvent e) {
Point where = ((Frame) e.getSource()).getLocation();
System.err.println(PApplet.EXTERNAL_MOVE + " " +
where.x + " " + where.y);
System.err.flush(); // doesn't seem to help or hurt
}
});
parentFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.err.println(PApplet.EXTERNAL_QUIT);
System.err.flush(); // important
System.exit(0);
}
});
}
/**
* main() method for running this class from the command line.
*
* The simplest way to turn and applet into an application is to
* add the following code to your program:
*
* e.g. String stuff[] = { "apple", "bear", "cat" };
* String list = join(stuff, ", ");
* // list is now "apple, bear, cat"
*/
static public String join(String str[], String separator) {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < str.length; i++) {
if (i != 0) buffer.append(separator);
buffer.append(str[i]);
}
return buffer.toString();
}
/**
* Split the provided String at wherever whitespace occurs.
* Multiple whitespace (extra spaces or tabs or whatever)
* between items will count as a single break.
*
* i.e. split("a b") -> { "a", "b" }
* split("a b") -> { "a", "b" }
* split("a\tb") -> { "a", "b" }
* split("a \t b ") -> { "a", "b" }
*/
static public String[] split(String what) {
return split(what, WHITESPACE);
}
/**
* Splits a string into pieces, using any of the chars in the
* String 'delim' as separator characters. For instance,
* in addition to white space, you might want to treat commas
* as a separator. The delimeter characters won't appear in
* the returned String array.
*
* i.e. split("a, b", " ,") -> { "a", "b" }
*
* To include all the whitespace possibilities, use the variable
* WHITESPACE, found in PConstants:
*
* i.e. split("a | b", WHITESPACE + "|"); -> { "a", "b" }
*/
static public String[] split(String what, String delim) {
StringTokenizer toker = new StringTokenizer(what, delim);
String pieces[] = new String[toker.countTokens()];
int index = 0;
while (toker.hasMoreTokens()) {
pieces[index++] = toker.nextToken();
}
return pieces;
}
/**
* Split a string into pieces along a specific character.
* Most commonly used to break up a String along tab characters.
* static public void main(String args[]) {
* PApplet.main(new String[] { "YourSketchName" };
* }
* This will properly launch your applet from a double-clickable
* .jar or from the command line.
*
* Parameters useful for launching or also used by the PDE:
*
* --location=x,y upper-lefthand corner of where the applet
* should appear on screen. if not used,
* the default is to center on the main screen.
*
* --present put the applet into full screen presentation
* mode. requires java 1.4.
*
* --present-color background color of the presentation window
*
* --sketch-folder location of where to save files from functions
* like saveStrings() or saveFrame(). defaults to
* the folder that the java application was
* launched from, which means if this isn't set by
* the pde, everything goes into the same folder
* as processing.exe.
*
* --display=n set what display should be used by this applet.
* displays are numbered starting from 1.
*
*
* Parameters used by Processing when running an applet externally:
*
* --external set when the applet is being used by the PDE
*
* --editor-location=x,y position of the upper-lefthand corner of the
* editor window, for placement of applet window
*
* --present-stop-color color of the 'stop' text used to quit an
* applet when it's in present mode.
*
*/
static public void main(String args[]) {
if (args.length < 1) {
System.err.println("Usage: PApplet