diff --git a/.classpath b/.classpath
new file mode 100644
index 000000000..f713e0ef4
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,14 @@
+
+
- * 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 for this to be a float - * 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 (platformName.toLowerCase().indexOf("mac") != -1) { - // can only check this property if running on a mac - // on a pc it throws a security exception and kills the applet - // (but on the mac it does just fine) - 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 sketchPath; //folder; - - /** When debugging headaches */ - static final boolean THREAD_DEBUG = false; - - /** Default width and height for applet when not specified */ - static public final int DEFAULT_WIDTH = 100; - static public final int DEFAULT_HEIGHT = 100; - - /** - * Minimum dimensions for the window holding an applet. - * This varies between platforms, Mac OS X 10.3 can do any height - * but requires at least 128 pixels width. Windows XP has another - * set of limitations. And for all I know, Linux probably lets you - * make windows with negative sizes. - */ - static public final int MIN_WINDOW_WIDTH = 128; - static public final int MIN_WINDOW_HEIGHT = 128; - - /** - * true if no size() command has been executed. This is used to wait until - * a size has been set before placing in the window and showing it. - */ - public boolean defaultSize; - - /** - * Pixel buffer from this applet's PGraphics. - *
- * When used with OpenGL or Java2D, this value will - * be null until beginPixels() 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/y position of the mouse. This will be a different value - * when inside a mouse handler (like the mouseMoved() method) versus - * when inside draw(). Inside draw(), pmouseX is updated once each - * frame, but inside mousePressed() and friends, it's updated each time - * an event comes through. Be sure to use only one or the other type of - * means for tracking pmouseX and pmouseY within your sketch, otherwise - * you're gonna run into trouble. - */ - public int pmouseX, 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; - - /** - * Last mouse button pressed, one of LEFT, CENTER, or RIGHT. - *
- * If running on Mac OS, a ctrl-click will be interpreted as - * the righthand mouse button (unlike Java, which reports it as - * the left mouse). - */ - public int mouseButton; - - 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; - - /** - * true if exit() has been called so that things shut down - * once the main thread kicks off. - */ - protected boolean exit; - - 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; - //public Throwable 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_BGCOLOR = "--bgcolor"; - - static public final String ARGS_PRESENT = "--present"; - - static public final String ARGS_STOP_COLOR = "--stop-color"; - - static public final String ARGS_HIDE_STOP = "--hide-stop"; - - /** - * 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-path"; - - /** - * 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__"; - - - // during rev 0100 dev cycle, working on new threading model, - // but need to disable and go conservative with changes in order - // to get pdf and audio working properly first - static final boolean CRUSTY_THREADS = true; - - - public void init() { - // first get placed size in case it's non-zero - Dimension initialSize = getSize(); - - // 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 draw() 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(); - - try { - getAppletContext(); - online = true; - } catch (NullPointerException e) { - online = false; - } - - if (javaVersion < 1.3f) { - addMouseListener(new MouseAdapter() { - public void mousePressed(MouseEvent e) { - link("http://java.com/"); - } - }); - // no init to do, so don't cause no trouble, boy - return; - // call this after making the methods to minimize the - // number of places needing the javaVersion crap - // (also needs to check online first and create empty - // stop method register list) - } - - try { - if (sketchPath == null) { - sketchPath = System.getProperty("user.dir"); - } - } catch (Exception e) { } // may be a security problem - - // create a dummy graphics context - if ((initialSize.width != 0) && (initialSize.height != 0)) { - size(initialSize.width, initialSize.height); - } else { - //System.out.println("setting default"); - size(DEFAULT_WIDTH, DEFAULT_HEIGHT); - this.defaultSize = true; - //System.out.println("zeroing"); - //this.width = 0; // use this to flag whether the width/height are valid - //this.height = 0; - // need to set width/height otherwise - // they won't work for static mode apps - //defaultSize = true; - } - - // this is automatically called in applets - // though it's here for applications anyway - start(); - } - - - /** - * Called by the browser or applet viewer to inform this applet that it - * should start its execution. It is called after the init method and - * each time the applet is revisited in a Web page. - *
- * Called explicitly via the first call to PApplet.paint(), because - * PAppletGL needs to have a usable screen before getting things rolling. - */ - public void start() { - if (javaVersion < 1.3f) return; - - if (thread != null) return; - thread = new Thread(this); - thread.start(); - } - - - /** - * Called by the browser or applet viewer to inform - * this applet that it should stop its execution. - * - * Unfortunately, there are no guarantees from the Java spec - * when or if stop() will be called (i.e. on browser quit, - * or when moving between web pages), and it's not always called. - */ - public void stop() { - // maybe start should also be used as the method for kicking - // the thread on, instead of doing it inside paint() - - // bringing this back for 0111, hoping it'll help opengl shutdown - finished = true; // why did i comment this out? - - //System.out.println("stopping applet " + thread); - - // don't run stop and disposers twice - if (thread == null) return; - thread = null; - - // call to shut down renderer, in case it needs it (pdf does) - if (g != null) g.dispose(); - - // maybe this should be done earlier? might help ensure it gets called - // before the vm just craps out since 1.5 craps out so aggressively. - disposeMethods.handle(); - } - - - /** - * Called by the browser or applet viewer to inform this applet - * that it is being reclaimed and that it should destroy - * any resources that it has allocated. - * - * This also attempts to call PApplet.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() { - ((PApplet)this).stop(); - } - - - /** - * This returns the last width and height specified by the user - * via the size() command. - */ - 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; - } - - - synchronized public void redraw() { - if (!looping) { - redraw = true; - if (thread != null) { - // wake from sleep (necessary otherwise it'll be - // up to 10 seconds before update) - if (CRUSTY_THREADS) { - thread.interrupt(); - } else { - notifyAll(); - } - } - } - } - - - synchronized public void loop() { - if (!looping) { - looping = true; - if (thread != null) { - // wake from sleep (necessary otherwise it'll be - // up to 10 seconds before update) - if (CRUSTY_THREADS) { - thread.interrupt(); - } else { - notifyAll(); - } - } - } - } - - - synchronized public void noLoop() { - if (looping) { - looping = false; - - // reset framerate delay times - framerateLastDelayTime = 0; - framerateLastMillis = 0; - - if (thread != null) { - if (CRUSTY_THREADS) { - thread.interrupt(); // wake from sleep - } else { - /* - try { - wait(); // until a notify - } catch (InterruptedException e) { } - */ - } - } - } - } - - - ////////////////////////////////////////////////////////////// - - - /** - * Starts up and creates a two-dimensional drawing surface, - * or resizes the current drawing surface. - *- * 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 { - // create a JAVA2D renderer (the current default) - size(iwidth, iheight, JAVA2D); - - /* - 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 - } - */ - } - } - - - public void size(int iwidth, int iheight, String irenderer) { - size(iwidth, iheight, irenderer, null); - } - - - /** - * Creates a new PGraphics object and sets it to the specified size. - *
- * Note that you cannot change the renderer once outside of setup(). - * In most cases, 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. - *
- * XXXX Also note that this calls defaults(), which will reset any - * XXXX settings for the font, stroke, fill, colorMode, lights, etc. - */ - public void size(int iwidth, int iheight, - String irenderer, String ipath) { - String currentRenderer = - (g == null) ? null : g.getClass().getName(); - - if (currentRenderer != null) { - if (currentRenderer.equals(irenderer)) { - if ((iwidth == g.width) && (iheight == g.height)) { - // in this case, size() is being called a second time because - // setup() is being called a second time, since the first time - // that setup was called, the renderer was changed so an - // exception was thrown and setup() didn't complete. but this - // time around, g is the proper size and the proper class. - - // that or size() is being called again for no good reason, - // in which case we just ignore it anyway. - - // so all that needs to be done is to set the defaults - // (clear the background, set default strokeWeight, etc). - //g.defaults(); - // removed this in favor of calling defaults() from beginDraw() - - // this will happen when P3D or OPENGL are used with size() - // inside of setup. it's also safe to call defaults() now, - // because it's happening inside setup, which is just frame 0, - // meaning that the graphics context is proper and visible. - - } else { // just resizing, no need to create new graphics object - g.resize(iwidth, iheight); - updateSize(iwidth, iheight); - redraw(); // changed for rev 0100 - } - // in either case, the renderer is unchanged, so return - //return; - - } else { // renderer is being changed - if (frameCount > 0) { - throw new RuntimeException("size() cannot be called to change " + - "the renderer outside of setup()"); - } - // otherwise ok to fall through and create renderer below - // the renderer is changing, so need to create a new object - g = createGraphics(iwidth, iheight, irenderer, ipath); - //if (g != null) { - updateSize(iwidth, iheight); - //} - - // 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); - } - } else { // none exists, just create a freshy - g = createGraphics(iwidth, iheight, irenderer, ipath); - updateSize(iwidth, iheight); - } - - /* - // the renderer is changing, so need to create a new object - g = createGraphics(iwidth, iheight, irenderer); - //if (g != null) { - updateSize(iwidth, iheight); - //} - - //if ((currentRenderer != null) && - // !currentRenderer.equals(irenderer)) { - if (currentRenderer != null) { - // 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); - } - */ - } - - - /** - * Sets this.width and this.height, unsets defaultSize, and calls - * the size() methods inside any libraries. - */ - protected void updateSize(int iwidth, int iheight) { - this.width = iwidth; - this.height = iheight; - defaultSize = false; - - // make the applet itself larger.. it's a subclass of Component, - // so this is important for when it's embedded inside another app. - 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. - - // if the default renderer is just being resized, - // restore it to its default values - //g.defaults(); - // no, otherwise fonts that were set in setup() will go away - - // this has to be called after the exception is thrown, - // otherwise the supporting libs won't have a valid context to draw to - Object methodArgs[] = - new Object[] { new Integer(width), new Integer(height) }; - sizeMethods.handle(methodArgs); - } - - - /* - public PGraphics createGraphics(String renderer) { - return createGraphics(width, height, renderer); - } - - - public PGraphics createGraphics(int iwidth, int iheight) { - return createGraphics(iwidth, iheight, g.getClass().getName()); - } - */ - - - public PGraphics createGraphics(String irenderer, String ipath) { - return createGraphics(width, height, irenderer, this, ipath); - } - - - public PGraphics createGraphics(int iwidth, int iheight, - String irenderer) { - return createGraphics(iwidth, iheight, irenderer, this, null); - } - - - public PGraphics createGraphics(int iwidth, int iheight, - String irenderer, String ipath) { - return createGraphics(iwidth, iheight, irenderer, this, ipath); - } - - - static public PGraphics createGraphics(int iwidth, int iheight, - String irenderer, PApplet applet, - String ipath) { - /* - // ok when calling size, but not really with createGraphics() - if (renderer.equals(OPENGL)) { - throw new RuntimeException("createGraphics() with OPENGL is not " + - "supported. Use P3D instead."); - } - */ - - String openglError = - "Before using OpenGL, first select " + - "Import Library > opengl from the Sketch menu."; - - try { - Class rendererClass = Class.forName(irenderer); - Class constructorParams[] = null; - Object constructorValues[] = null; - - if (ipath == null) { - constructorParams = new Class[] { - Integer.TYPE, Integer.TYPE, PApplet.class - }; - constructorValues = new Object[] { - new Integer(iwidth), new Integer(iheight), applet - }; - } else { - // first make sure that this in a nice, full, absolute path - ipath = applet.savePath(ipath); - - constructorParams = new Class[] { - Integer.TYPE, Integer.TYPE, PApplet.class, String.class - }; - constructorValues = new Object[] { - new Integer(iwidth), new Integer(iheight), applet, ipath - }; - } - - Constructor constructor = - rendererClass.getConstructor(constructorParams); - // create the actual PGraphics object for rendering - return (PGraphics) constructor.newInstance(constructorValues); - //updateSize(iwidth, iheight); - - } catch (InvocationTargetException ite) { - String msg = ite.getTargetException().getMessage(); - if ((msg != null) && - (msg.indexOf("no jogl in java.library.path") != -1)) { - throw new RuntimeException(openglError); - - } else { - ite.getTargetException().printStackTrace(); - Throwable target = ite.getTargetException(); - if (platform == MACOSX) target.printStackTrace(System.out); // bug - // neither of these help, or work - //target.printStackTrace(System.err); - //System.err.flush(); - //System.out.println(System.err); // and the object isn't null - throw new RuntimeException(target.getMessage()); - } - - } 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 " + irenderer + " to your sketch."); - } - - } catch (Exception e) { - //System.out.println("ex3"); - if ((e instanceof IllegalArgumentException) || - (e instanceof NoSuchMethodException) || - (e instanceof IllegalAccessException)) { - - String msg = "public " + - irenderer.substring(irenderer.lastIndexOf('.') + 1) + - "(int width, int height, PApplet parent" + - ((ipath == null) ? "" : ", String filename") + - ") does not exist."; - throw new RuntimeException(msg); - - } else { - if (platform == MACOSX) e.printStackTrace(System.out); - //System.err.flush(); - //return null; - throw new RuntimeException(e.getMessage()); - //die("Could not create " + irenderer); - } - - /* - } catch (Exception e) { - e.printStackTrace(); - die("Could not start because of a problem with size()", e); - */ - } - - // clear things out to get started - //outgoing.defaults(); - // tell people to use beginDraw/endDraw - - // and send 'em off - //return outgoing; - } - - - 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) { - if (javaVersion < 1.3f) { - screen.setColor(new Color(64, 64, 64)); - Dimension size = getSize(); - screen.fillRect(0, 0, size.width, size.height); - screen.setColor(Color.white); - screen.setFont(new Font("Dialog", Font.PLAIN, 9)); - screen.drawString("You need to install", 3, 15); - screen.drawString("Java 1.3 or later", 3, 28); - screen.drawString("to view this content.", 3, 41); - screen.drawString("Click here to visit", 3, 59); - screen.drawString("java.com and install.", 3, 72); - return; - } - - //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 - // (also prevents over-drawing when using PGraphicsGL) - 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; - } - - - /*synchronized*/ 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); - //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 || finished) ? 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; - if (CRUSTY_THREADS) { - Thread.sleep(nap); - } else { - wait(nap); - } - if (THREAD_DEBUG) println(Thread.currentThread().getName() + - " outta sleep"); - } catch (InterruptedException e) { } - } - - } catch (Exception e) { - // 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; - if (e instanceof InvocationTargetException) { - //System.out.println("target problem"); - e = (Exception) (((InvocationTargetException) e).getTargetException()); - } - 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); - e.printStackTrace(System.out); - - } else { - System.err.println(LEECH_WAKEUP); - e.printStackTrace(); - e.printStackTrace(System.out); - } - } - if (THREAD_DEBUG) println(Thread.currentThread().getName() + - " thread finished"); - - // this may not be safe? this will get triggered with exit() - // but need to see if this is it - //if ((leechErr == null) && !online) { - //System.exit(0); - //} - - //System.out.println("exiting run " + finished); - stop(); // call to shutdown libs? - - if (exit) { // user called exit() function - if ((leechErr == null) && !online) { - // don't want to call System.exit() when an applet, - // or running inside the PDE (would kill the PDE) - System.exit(0); - } - } - } - - - synchronized public void handleDisplay() { - 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 beginDraw"); - g.beginDraw(); - if (THREAD_DEBUG) println(Thread.currentThread().getName() + - " 1b draw"); - - //boolean recorderNull = true; - //boolean recorderRawNull = true; - - 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("runtime extends " + e); - //System.out.println("catching a cold " + e.getMessage()); - String msg = e.getMessage(); - if ((msg != null) && - (e.getMessage().indexOf(NEW_RENDERER) != -1)) { - //System.out.println("got new renderer"); - return; - //continue; // will this work? - - } else { - //e.printStackTrace(System.out); - //System.out.println("re-throwing"); - 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 beginPixels - // now for certain that we've got a valid size - this.width = g.width; - this.height = g.height; - this.defaultSize = false; - - } else { // frameCount > 0, meaning an actual draw() - // 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); - long now = System.currentTimeMillis(); - int napTime = (int) (timeToLeave - now); - if (napTime > 0) { - framerateLastDelayTime = timeToLeave; - delay(napTime); - } else { - // nap time is negative, need to reset clock (bug #336) - framerateLastDelayTime = now; - } - } - } - - preMethods.handle(); - - pmouseX = dmouseX; - pmouseY = dmouseY; - - //synchronized (glock) { - //synchronized (this) { - //try { - draw(); - /* - // seems to catch, but then blanks out - } catch (Exception e) { - if (e instanceof InvocationTargetException) { - System.out.println("found poo"); - ((InvocationTargetException)e).getTargetException().printStackTrace(System.out); - } - } - */ - //} - //} - - // set a flag regarding whether the recorders were non-null - // as of draw().. this will prevent the recorder from being - // reset if recordShape() is called in an event method, such - // as mousePressed() - //recorderNull = (recorder == null); - //recorderRawNull = (g.recorderRaw == null); - - // 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 endDraw"); - - 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.endDraw(); - /* - if (!recorderNull) { - if (recorder != null) { - recorder.endDraw(); - recorder = null; - } - } - if (!recorderRawNull) { - if (g.recorderRaw != null) { - g.recorderRaw.endDraw(); - g.recorderRaw = 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 - } - } - - - ////////////////////////////////////////////////////////////// - - - protected boolean listenersAdded; - - public void addListeners() { - if (!listenersAdded) { - addMouseListener(this); - addMouseMotionListener(this); - addKeyListener(this); - addFocusListener(this); - - addComponentListener(new ComponentAdapter() { - public void componentResized(ComponentEvent e) { - Component c = e.getComponent(); - Rectangle bounds = c.getBounds(); - //System.out.println("componentResized()"); - //System.out.println(" " + c.getClass().getName()); - //println(" visible " + isVisible()); - //System.out.println(" " + e); - //System.out.println(" bounds: " + bounds); - //int newWidth = bounds.width - bounds.x * 2; - //int newHeight = bounds.height - (bounds.y + bounds.x); - //System.out.println(" new: " + newWidth + " " + newHeight); - - size(bounds.width, bounds.height); - - //if (c == PApplet.this) { - //Container con = (Container) c; - //Dimension newSize = getSize(); - //System.out.println("resizing to " + newSize + " "); - //System.out.println(c.getBounds()); - //System.out.println(e); - //System.out.println(c); - //System.out.println("insets " + con.getInsets()); - //size(newSize.width, newSize.height); - //} - } - }); - - listenersAdded = true; - } - } - - - ////////////////////////////////////////////////////////////// - - - 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; - - int modifiers = event.getModifiers(); - if ((modifiers & MouseEvent.BUTTON1_MASK) != 0) { - mouseButton = LEFT; - } else if ((modifiers & MouseEvent.BUTTON2_MASK) != 0) { - mouseButton = CENTER; - } else if ((modifiers & MouseEvent.BUTTON3_MASK) != 0) { - mouseButton = RIGHT; - } - // if running on macos, allow ctrl-click as right mouse - if ((platform == MACOSX) || (platform == MACOS9)) { - if (mouseEvent.isPopupTrigger()) { - mouseButton = RIGHT; - } - } - - mouseEventMethods.handle(new Object[] { event }); - - // 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; - } - - int id = event.getID(); - switch (id) { - 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; - } - // an attempt to solve bug 170 - // http://dev.processing.org/bugs/show_bug.cgi?id=170 - //if ((id == MouseEvent.MOUSE_DRAGGED) || - // (id == MouseEvent.MOUSE_MOVED)) { - //println(emouseX + " " + emouseY + " " + mouseX + " " + mouseY); - 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; - } - - // if someone else wants to intercept the key, they should - // set key to zero (or something besides the ESC). - if ((event.getID() == KeyEvent.KEY_PRESSED) && - (key == KeyEvent.VK_ESCAPE)) { - exit(); - } - } - - - 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. - * Because of how operating systems handle key repeats, holding - * down a key will cause multiple calls to keyPressed(), because - * the OS repeat takes over. - *
- * 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 - - - /** - * Delay for some amount of time. I'm not sure if this is even - * helpful anymore, as the screen isn't updated before or after the - * delay, meaning which means it just makes the app lock up - * temporarily. - */ - public void delay(int napTime) { - if (frameCount == 0) return; - if (napTime > 0) { - try { - if (CRUSTY_THREADS) { - Thread.sleep(napTime); - } else { - wait(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? - } - } - - - public void link(String here) { - link(here, null); - } - - - /** - * Link to an external page without all the muss. - *
- * When run with an applet, uses the browser to open the url, - * for applications, attempts to launch a browser with the url. - *
- * Works on Mac OS X and Windows. For Linux, use: - *
open(new String[] { "firefox", url });
- * or whatever you want as your browser, since Linux doesn't
- * yet have a standard method for launching URLs.
- */
- public void link(String url, String frameTitle) {
- if (online) {
- try {
- if (frameTitle == null) {
- getAppletContext().showDocument(new URL(url));
- } else {
- getAppletContext().showDocument(new URL(url), frameTitle);
- }
- } catch (Exception e) {
- e.printStackTrace();
- throw new RuntimeException("Could not open " + url);
- }
- } else {
- try {
- if (platform == WINDOWS) {
- // the following uses a shell execute to launch the .html file
- // note that under cygwin, the .html files have to be chmodded +x
- // after they're unpacked from the zip file. i don't know why,
- // and don't understand what this does in terms of windows
- // permissions. without the chmod, the command prompt says
- // "Access is denied" in both cygwin and the "dos" prompt.
- //Runtime.getRuntime().exec("cmd /c " + currentDir + "\\reference\\" +
- // referenceFile + ".html");
-
- // open dos prompt, give it 'start' command, which will
- // open the url properly. start by itself won't work since
- // it appears to need cmd
- Runtime.getRuntime().exec("cmd /c start " + url);
-
- } else if ((platform == MACOSX) || (platform == MACOS9)) {
- //com.apple.mrj.MRJFileUtils.openURL(url);
- try {
- Class mrjFileUtils = Class.forName("com.apple.mrj.MRJFileUtils");
- Method openMethod =
- mrjFileUtils.getMethod("openURL", new Class[] { String.class });
- openMethod.invoke(null, new Object[] { url });
- } catch (Exception e) {
- e.printStackTrace();
- }
- } else {
- throw new RuntimeException("Can't open URLs for this platform");
- }
- } catch (IOException e) {
- e.printStackTrace();
- throw new RuntimeException("Could not open " + url);
- }
- }
- }
-
-
- /**
- * Attempt to open a file using the platform's shell.
- */
- public void open(String filename) {
- if (platform == WINDOWS) {
- // just launching the .html file via the shell works
- // but make sure to chmod +x the .html files first
- // also place quotes around it in case there's a space
- // in the user.dir part of the url
- try {
- Runtime.getRuntime().exec("cmd /c \"" + filename + "\"");
- } catch (IOException e) {
- e.printStackTrace();
- throw new RuntimeException("Could not open " + filename);
- }
-
- } else if (platform == MACOSX) {
- // osx fix contributed by chandler for rev 0113
- try {
- // Java on OS X doesn't like to exec commands inside quotes
- // for some reason.. escape spaces with slashes just in case
- if (filename.indexOf(' ') != -1) {
- StringBuffer sb = new StringBuffer();
- char c[] = filename.toCharArray();
- for (int i = 0; i < c.length; i++) {
- if (c[i] == ' ') {
- sb.append("\\\\ ");
- } else {
- sb.append(c[i]);
- }
- }
- filename = sb.toString();
- }
- Runtime.getRuntime().exec("open " + filename);
-
- } catch (IOException e) {
- e.printStackTrace();
- throw new RuntimeException("Could not open " + filename);
- }
-
- } else if (platform == MACOS9) {
- // prepend file:// on this guy since it's a file
- String url = "file://" + filename;
-
- // replace spaces with %20 for the file url
- // otherwise the mac doesn't like to open it
- // can't just use URLEncoder, since that makes slashes into
- // %2F characters, which is no good. some might say "useless"
- if (url.indexOf(' ') != -1) {
- StringBuffer sb = new StringBuffer();
- char c[] = url.toCharArray();
- for (int i = 0; i < c.length; i++) {
- if (c[i] == ' ') {
- sb.append("%20");
- } else {
- sb.append(c[i]);
- }
- }
- url = sb.toString();
- }
- link(url);
-
- } else { // give up and just pass it to Runtime.exec()
- open(new String[] { filename });
- }
- }
-
-
- /**
- * Launch a process using a platforms shell, and an array of
- * args passed on the command line.
- */
- public Process open(String args[]) {
- try {
- return Runtime.getRuntime().exec(args);
- } catch (Exception e) {
- e.printStackTrace();
- throw new RuntimeException("Could not open " + join(args, ' '));
- }
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
-
- /**
- * 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);
- }
-
-
- /**
- * Call to safely exit the sketch when finished. For instance,
- * to render a single frame, save it, and quit.
- */
- public void exit() {
- if (thread == null) {
- // exit immediately, stop() has already been called,
- // meaning that the main thread has long since exited
- if ((leechErr == null) && !online) {
- // don't want to call System.exit() when an applet,
- // or running inside the PDE (would kill the PDE)
- System.exit(0);
- }
- } else {
- finished = true; // stop() will be called as the thread exits
- //stop();
- exit = true;
- }
- }
-
-
- //////////////////////////////////////////////////////////////
-
- // SCREEN GRABASS
-
-
- /**
- * Intercepts any relative paths to make them absolute (relative
- * to the sketch folder) before passing to save() in PImage.
- * (Changed in 0100)
- */
- 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 endDraw() at the end of your draw(). - * 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"); - g.save(savePath("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;
- }
-
- g.save(savePath(insertFrame(what)));
- /*
- int first = what.indexOf('#');
- int last = what.lastIndexOf('#');
-
- if (first == -1) {
- g.save(savePath(what));
-
- } else {
- String prefix = what.substring(0, first);
- int count = last - first + 1;
- String suffix = what.substring(last + 1);
- g.save(savePath(prefix + nf(frameCount, count) + suffix));
- }
- */
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // CURSOR
-
- //
-
-
- 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) {
- 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
- * - * Based on code contributed by Amit Pitaru, plus additional - * code to handle Java versions via reflection by Jonathan Feinberg. - */ - public void cursor(PImage image, int hotspotX, int hotspotY) { - 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)); - - 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 " + - "when using Java " + javaVersionName); - } 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[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) { - if (what == null) { - // special case since this does fuggly things on > 1.1 - System.out.print("null"); - - } else { - String name = what.getClass().getName(); - if (name.charAt(0) == '[') { - switch (name.charAt(1)) { - case '[': - // don't even mess with multi-dimensional arrays (case '[') - // or anything else that's not int, float, boolean, char - System.out.print(what); - System.out.print(' '); - break; - - case 'L': - // print a 1D array of objects as individual elements - Object poo[] = (Object[]) what; - for (int i = 0; i < poo.length; i++) { - System.out.print(poo[i]); - System.out.print(' '); - } - break; - - case 'Z': // boolean - boolean zz[] = (boolean[]) what; - for (int i = 0; i < zz.length; i++) { - System.out.print(zz[i]); - System.out.print(' '); - } - break; - - case 'B': // byte - byte bb[] = (byte[]) what; - for (int i = 0; i < bb.length; i++) { - System.out.print(bb[i]); - System.out.print(' '); - } - break; - - case 'C': // char - char cc[] = (char[]) what; - for (int i = 0; i < cc.length; i++) { - System.out.print(cc[i]); - System.out.print(' '); - } - break; - - case 'I': // int - int ii[] = (int[]) what; - for (int i = 0; i < ii.length; i++) { - System.out.print(ii[i]); - System.out.print(' '); - } - break; - - case 'F': // float - float ff[] = (float[]) what; - for (int i = 0; i < ff.length; i++) { - System.out.print(ff[i]); - System.out.print(' '); - } - break; - - case 'D': // double - double dd[] = (double[]) what; - for (int i = 0; i < dd.length; i++) { - System.out.print(dd[i]); - System.out.print(' '); - } - break; - - default: - System.out.print(what); - } - } else { - System.out.print(what); //.toString()); - } - } - } - - // - - 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) { - if (what == null) { - // special case since this does fuggly things on > 1.1 - System.out.println("null"); - - } else { - String name = what.getClass().getName(); - if (name.charAt(0) == '[') { - switch (name.charAt(1)) { - case '[': - // don't even mess with multi-dimensional arrays (case '[') - // or anything else that's not int, float, boolean, char - System.out.println(what); - break; - - case 'L': - // print a 1D array of objects as individual elements - Object poo[] = (Object[]) what; - for (int i = 0; i < poo.length; i++) { - System.out.println(poo[i]); - } - break; - - case 'Z': // boolean - boolean zz[] = (boolean[]) what; - for (int i = 0; i < zz.length; i++) { - System.out.println(zz[i]); - } - break; - - case 'B': // byte - byte bb[] = (byte[]) what; - for (int i = 0; i < bb.length; i++) { - System.out.println(bb[i]); - } - break; - - case 'C': // char - char cc[] = (char[]) what; - for (int i = 0; i < cc.length; i++) { - System.out.println(cc[i]); - } - break; - - case 'I': // int - int ii[] = (int[]) what; - for (int i = 0; i < ii.length; i++) { - System.out.println(ii[i]); - } - break; - - case 'F': // float - float ff[] = (float[]) what; - for (int i = 0; i < ff.length; i++) { - System.out.println(ff[i]); - } - break; - - case 'D': // double - double dd[] = (double[]) what; - for (int i = 0; i < dd.length; i++) { - System.out.println(dd[i]); - } - break; - - default: - System.out.println(what); - } - } else { - System.out.println(what); //.toString()); - } - } - } - - // - - /* - // not very useful, because it only works for public (and protected?) - // fields of a class, not local variables to methods - 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); - return (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); - return (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); - return (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); - return (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 int ceil(float what) { - return (int) Math.ceil(what); - } - - static public final int floor(float what) { - return (int) Math.floor(what); - } - - static public final int round(float what) { - return (int) 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<
- * Generally, loadImage() should only be used during setup, because
- * re-loading images inside draw() is likely to cause a significant
- * delay while memory is allocated and the thread blocks while waiting
- * for the image to load because loading is not asynchronous.
- *
- * To load several images asynchronously, see more information in the
- * FAQ about writing your own threaded image loading method.
- *
- * As of 0096, returns null if no image of that name is found,
- * rather than an error.
- *
- * Release 0115 also provides support for reading TIFF and RLE-encoded
- * Targa (.tga) files written by Processing via save() and saveFrame().
- * Other TIFF and Targa files will probably not load, use a different
- * format (gif, jpg and png are safest bets) when creating images with
- * another application to use with Processing.
- *
- * Also in release 0115, more image formats (BMP and others) can
- * be read when using Java 1.4 and later. Because many people still
- * use Java 1.1 and 1.3, these formats are not recommended for
- * work that will be posted on the web. To get a list of possible
- * image formats for use with Java 1.4 and later, use the following:
- * println(javax.imageio.ImageIO.getReaderFormatNames())
- */
- public PImage loadImage(String filename) {
- // it's not clear whether this method is more efficient for
- // loading gif, jpeg, and png data than the standard toolkit function,
- // in fact it may even be identical. but who knows, with any luck
- // it may even fix the image loading problems from bug #279.
- // http://dev.processing.org/bugs/show_bug.cgi?id=279
- // (if anyone reading this knows for certain, please post)
- if (PApplet.javaVersion >= 1.4f) {
- if (loadImageFormats == null) {
- //loadImageFormats = javax.imageio.ImageIO.getReaderFormatNames();
- try {
- Class ioClass = Class.forName("javax.imageio.ImageIO");
- Method getFormatNamesMethod =
- ioClass.getMethod("getReaderFormatNames", (Class[]) null);
- loadImageFormats = (String[])
- getFormatNamesMethod.invoke((Class[]) null, (Object[]) null);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- if (loadImageFormats != null) {
- for (int i = 0; i < loadImageFormats.length; i++) {
- if (filename.endsWith("." + loadImageFormats[i])) {
- return loadImageIO(filename);
- }
- }
- }
- }
-
- if (filename.toLowerCase().endsWith(".tga")) {
- try {
- return loadImageTGA(filename);
- } catch (IOException e) {
- e.printStackTrace();
- return null;
- }
- }
-
- byte bytes[] = loadBytes(filename);
- if (bytes == null) return null;
-
- if (filename.toLowerCase().endsWith(".tif") ||
- filename.toLowerCase().endsWith(".tiff")) {
- return PImage.loadTIFF(bytes);
- }
-
- Image awtImage = Toolkit.getDefaultToolkit().createImage(bytes);
-
- MediaTracker tracker = new MediaTracker(this);
- tracker.addImage(awtImage, 0);
- try {
- tracker.waitForAll();
- } catch (InterruptedException e) {
- // don't bother, since this may be interrupted by draw
- // or noLoop or something like that
- //e.printStackTrace(); // non-fatal, right?
- }
-
- PImage image = new PImage(awtImage);
-
- // if it's a .gif image, test to see if it has transparency
- if ((filename.toLowerCase().endsWith(".gif")) ||
- (filename.toLowerCase().endsWith(".png"))) {
- image.checkAlpha();
- }
- return image;
- }
-
-
- /**
- * Use Java 1.4 ImageIO methods to load an image. All done via reflection
- * in order to maintain compatability with previous releases.
- */
- protected PImage loadImageIO(String filename) {
- InputStream stream = openStream(filename);
- if (stream == null) {
- System.err.println("The image " + filename + " could not be found.");
- return null;
- }
-
- try {
- Class ioClass = Class.forName("javax.imageio.ImageIO");
- Method readMethod =
- ioClass.getMethod("read", new Class[] { InputStream.class });
- Object bimage = readMethod.invoke(null, new Object[] { stream });
-
- // need to get width and height, then create pixels[] at that size
- int px[] = null;
-
- Class biClass =
- Class.forName("java.awt.image.BufferedImage");
-
- Method getHeightMethod =
- biClass.getMethod("getHeight", (Class[]) null);
- Integer hi = (Integer) getHeightMethod.invoke(bimage, (Object[]) null);
-
- Method getWidthMethod =
- biClass.getMethod("getWidth", (Class[]) null);
- Integer wi = (Integer) getWidthMethod.invoke(bimage, (Object[]) null);
-
- // was gonna call getType() on the image to see if RGB or ARGB,
- // but it's not actually useful, since gif images will come through
- // as TYPE_BYTE_INDEXED, which means it'll still have to check for
- // the transparency. also, would have to iterate through all the other
- // types and guess whether alpha was in there, so.. just gonna stick
- // with the old method.
-
- PImage outgoing = new PImage(wi.intValue(), hi.intValue());
-
- Method getRgbMethod =
- biClass.getMethod("getRGB", new Class[] {
- Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE,
- outgoing.pixels.getClass(), Integer.TYPE, Integer.TYPE
- });
- getRgbMethod.invoke(bimage, new Object[] {
- new Integer(0), new Integer(0),
- new Integer(outgoing.width), new Integer(outgoing.height),
- outgoing.pixels, new Integer(0), new Integer(outgoing.width)
- });
-
- // check the alpha for this image
- outgoing.checkAlpha();
-
- // return the image
- return outgoing;
-
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
-
-
- /**
- * Targa image loader for RLE-compressed TGA files.
- *
- * Rewritten for 0115 to read/write RLE-encoded targa images.
- */
- protected PImage loadImageTGA(String filename) throws IOException {
- InputStream is = openStream(filename);
- if (is == null) return null;
-
- byte header[] = new byte[18];
- int offset = 0;
- do {
- int count = is.read(header, offset, header.length - offset);
- if (count == -1) return null;
- offset += count;
- } while (offset < 18);
-
- int format = 0;
- if ((header[2] == 0x0B) &&
- (header[16] == 0x08) &&
- (header[17] == 0x28)) {
- format = ALPHA;
-
- } else if ((header[2] == 0x0A) &&
- (header[16] == 24) &&
- (header[17] == 0x20)) {
- format = RGB;
-
- } else if ((header[2] == 0x0A) &&
- (header[16] == 32) &&
- (header[17] == 0x28)) {
- format = ARGB;
- }
- if (format == 0) {
- System.err.println("Unknown .tga file format for " + filename);
- return null;
- }
-
- // get image dimensions
- int w = ((header[13] & 0xff) << 8) + (header[12] & 0xff);
- int h = ((header[15] & 0xff) << 8) + (header[14] & 0xff);
- PImage outgoing = new PImage(w, h, format);
-
- int index = 0;
- int px[] = outgoing.pixels;
-
- while (index < px.length) {
- int num = is.read();
- boolean isRLE = (num & 0x80) != 0;
- if (isRLE) {
- num -= 127; // (num & 0x7F) + 1
- int pixel = 0;
- switch (format) {
- case ALPHA:
- pixel = is.read();
- break;
- case RGB:
- pixel = 0xFF000000 |
- is.read() | (is.read() << 8) | (is.read() << 16);
- //(is.read() << 16) | (is.read() << 8) | is.read();
- break;
- case ARGB:
- pixel = is.read() |
- (is.read() << 8) | (is.read() << 16) | (is.read() << 24);
- break;
- }
- for (int i = 0; i < num; i++) {
- px[index++] = pixel;
- if (index == px.length) break;
- }
- } else { // write up to 127 bytes as uncompressed
- num += 1;
- switch (format) {
- case ALPHA:
- for (int i = 0; i < num; i++) {
- px[index++] = is.read();
- }
- break;
- case RGB:
- for (int i = 0; i < num; i++) {
- px[index++] = 0xFF000000 |
- is.read() | (is.read() << 8) | (is.read() << 16);
- //(is.read() << 16) | (is.read() << 8) | is.read();
- }
- break;
- case ARGB:
- for (int i = 0; i < num; i++) {
- px[index++] = is.read() | //(is.read() << 24) |
- (is.read() << 8) | (is.read() << 16) | (is.read() << 24);
- //(is.read() << 16) | (is.read() << 8) | is.read();
- }
- break;
- }
- }
- }
- return outgoing;
-
- /*
- // targa's are written upside down, so we need to parse it in reverse
- int index = (h-1) * w;
- // actual bitmap data starts at byte 18
- int offset = 18;
-
- // read out line by line
- for (int y = h-1; y >= 0; y--) {
- for (int x = 0; x < w; x++) {
- img.pixels[index + x] =
- (buffer[offset++] & 0xff) |
- ((buffer[offset++] & 0xff) << 8) |
- ((buffer[offset++] & 0xff) << 16) |
- (hasAlpha ? ((buffer[offset++] & 0xff) << 24) : 0xff000000);
- }
- index -= w;
- }
- return img;
- }
-
- System.err.println("loadImage(): bad targa image format");
- return null;
- */
-}
-
-
-
- //////////////////////////////////////////////////////////////
-
- // FONT I/O
-
-
- Hashtable fontTable;
-
- /**
- * Set the font based on its filename. This is less than efficient
- * than using loadFont because there's no way to unload it from memory,
- * but it's useful for beginners.
- */
- public void textFont(String filename) {
- if (filename.toLowerCase().indexOf(".vlw") == -1) {
- System.err.println("textFont() needs the filename of a .vlw font");
- } else {
- textFont(tableFont(filename));
- }
- }
-
-
- /**
- * Set the font based on its filename. This is less than efficient
- * than using loadFont because there's no way to unload it from memory,
- * but it's useful for beginners.
- */
- public void textFont(String filename, float size) {
- if (filename.toLowerCase().indexOf(".vlw") == -1) {
- System.err.println("textFont() needs the filename of a .vlw font");
- } else {
- textFont(tableFont(filename), size);
- }
- }
-
-
- protected PFont tableFont(String filename) {
- if (fontTable == null) fontTable = new Hashtable();
-
- PFont font = (PFont) fontTable.get(filename);
- if (font != null) return font;
-
- font = loadFont(filename);
- return font;
- }
-
-
- // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
-
-
- public PFont loadFont(String filename) {
- //if (g == null) { // just for good measure
- //die("loadFont() only be used inside setup() or draw()");
- //}
-
- try {
- String lower = filename.toLowerCase();
- InputStream input = openStream(filename);
-
- if (lower.endsWith(".vlw.gz")) {
- input = new GZIPInputStream(input);
-
- } else if (!lower.endsWith(".vlw")) {
- // this gets thrown down below
- throw new IOException("I don't know how to load a font named " +
- filename);
- }
- return new PFont(input);
-
- } catch (Exception e) {
- die("Could not load font " + filename + ". " +
- "Make sure that the font has been copied " +
- "to the data folder of your sketch.", e);
- }
- return null;
- }
-
-
- public PFont createFont(String name, float size) {
- return createFont(name, size, true, PFont.DEFAULT_CHARSET);
- }
-
-
- public PFont createFont(String name, float size, boolean smooth) {
- return createFont(name, size, smooth, PFont.DEFAULT_CHARSET);
- }
-
-
- /**
- * Create a .vlw font on the fly from either a font name that's
- * installed on the system, or from a .ttf or .otf that's inside
- * the data folder of this sketch.
- *
- * The parentFrame is the Frame that will guide the placement of
- * the prompt window. If no Frame is available, just pass in null.
- */
- // can't be static because it wants a host component
- static public File inputFile(String prompt, Frame parentFrame) {
- if (parentFrame == null) parentFrame = new Frame();
- FileDialog fd = new FileDialog(parentFrame, prompt, FileDialog.LOAD);
- fd.setVisible(true);
-
- String directory = fd.getDirectory();
- String filename = fd.getFile();
- if (filename == null) return null;
- return new File(directory, filename);
- }
-
-
- public File outputFile() {
- return outputFile("Save as...");
- }
-
-
- public File outputFile(String prompt) {
- Frame parentFrame = null;
- Component comp = getParent();
- while (comp != null) {
- //System.out.println(comp + " " + comp.getClass());
- if (comp instanceof Frame) {
- parentFrame = (Frame) comp;
- break;
- }
- comp = comp.getParent();
- }
- return outputFile(prompt, parentFrame);
- }
-
-
-
- static public File outputFile(Frame parentFrame) {
- return outputFile("Save as...", parentFrame);
- }
-
-
- /**
- * static version of outputFile usable by external classes.
- *
- * The parentFrame is the Frame that will guide the placement of
- * the prompt window. If no Frame is available, just pass in null.
- */
- static public File outputFile(String prompt, Frame parentFrame) {
- if (parentFrame == null) parentFrame = new Frame();
- FileDialog fd = new FileDialog(parentFrame, prompt, FileDialog.SAVE);
- fd.setVisible(true);
-
- String directory = fd.getDirectory();
- String filename = fd.getFile();
- if (filename == null) return null;
- return new File(directory, filename);
- }
-
-
- /**
- * I want to read lines from a file. I have RSI from typing these
- * eight lines of code so many times.
- */
- public BufferedReader reader(String filename) {
- try {
- InputStream is = openStream(filename);
- if (is == null) {
- System.err.println(filename + " does not exist or could not be read");
- return null;
- }
- return reader(is);
-
- } catch (Exception e) {
- if (filename == null) {
- die("Filename passed to reader() was null", e);
- } else {
- die("Couldn't create a reader for " + filename, e);
- }
- }
- return null;
- }
-
-
- /**
- * I want to read lines from a file. And I'm still annoyed.
- */
- static public BufferedReader reader(File file) {
- try {
- return reader(new FileInputStream(file));
-
- } catch (Exception e) {
- if (file == null) {
- throw new RuntimeException("File passed to reader() was null");
- } else {
- e.printStackTrace();
- throw new RuntimeException("Couldn't create a reader for " +
- file.getAbsolutePath());
- }
- }
- //return null;
- }
-
-
- /**
- * I want to read lines from a stream. If I have to type the
- * following lines any more I'm gonna send Sun my medical bills.
- */
- static public BufferedReader reader(InputStream input) {
- InputStreamReader isr = new InputStreamReader(input);
- return new BufferedReader(isr);
- }
-
-
- /**
- * decode a gzip input stream
- */
- static public InputStream gzipInput(InputStream input) {
- try {
- return new GZIPInputStream(input);
- } catch (IOException e) {
- e.printStackTrace();
- throw new RuntimeException("Problem with gzip input");
- }
- //return null;
- }
-
-
- /**
- * decode a gzip output stream
- */
- static public OutputStream gzipOutput(OutputStream output) {
- try {
- return new GZIPOutputStream(output);
- } catch (IOException e) {
- e.printStackTrace();
- throw new RuntimeException("Problem with gzip output");
- }
- //return null;
- }
-
-
- /**
- * I want to print lines to a file. Why can't I?
- */
- public PrintWriter writer(String filename) {
- try {
- return writer(new FileOutputStream(savePath(filename)));
-
- } catch (Exception e) {
- if (filename == null) {
- die("Filename passed to writer() was null", e);
- } else {
- die("Couldn't create a writer for " + filename, e);
- }
- }
- return null;
- }
-
-
- /**
- * I want to print lines to a file. I have RSI from typing these
- * eight lines of code so many times.
- */
- static public PrintWriter writer(File file) {
- try {
- return writer(new FileOutputStream(file));
-
- } catch (Exception e) {
- if (file == null) {
- throw new RuntimeException("File passed to writer() was null");
- } else {
- e.printStackTrace();
- throw new RuntimeException("Couldn't create a writer for " +
- file.getAbsolutePath());
- }
- }
- //return null;
- }
-
-
- /**
- * I want to print lines to a file. Why am I always explaining myself?
- * It's the JavaSoft API engineers who need to explain themselves.
- */
- static public PrintWriter writer(OutputStream output) {
- OutputStreamWriter osw = new OutputStreamWriter(output);
- return new PrintWriter(osw);
- }
-
-
- static public InputStream openStream(File file) {
- try {
- return new FileInputStream(file);
-
- } catch (IOException e) {
- if (file == null) {
- throw new RuntimeException("File passed to openStream() was null");
-
- } else {
- e.printStackTrace();
- throw new RuntimeException("Couldn't openStream() for " +
- file.getAbsolutePath());
- }
- }
- }
-
-
- /**
- * Simplified method to open a Java InputStream.
- *
- * This method is useful if you want to use the facilities provided
- * by PApplet to easily open things from the data folder or from a URL,
- * but want an InputStream object so that you can use other Java
- * methods to take more control of how the stream is read.
- *
- * If the requested item doesn't exist, null is returned.
- * (Prior to 0096, die() would be called, killing the applet)
- *
- * For 0096, the "data" folder is exported intact with subfolders,
- * and openStream() properly handles subdirectories from the data folder
- *
- * If not online, this will also check to see if the user is asking
- * for a file whose name isn't properly capitalized. This helps prevent
- * issues when a sketch is exported to the web, where case sensitivity
- * matters, as opposed to Windows and the Mac OS default where
- * case sensitivity is preserved but ignored.
- *
- * It is strongly recommended that libraries use this method to open
- * data files, so that the loading sequence is handled in the same way
- * as functions like loadBytes(), loadImage(), etc.
- *
- * The filename passed in can be:
- *
- * In this method, the data path is defined not as the applet's actual
- * data path, but a folder titled "data" in the sketch's working
- * directory. This is because in an application, the "data" folder is
- * exported as part of the jar file, and it's not as though you're gonna
- * write into the jar file itself. If you need to get things out of
- * the jar file, you should use openStream().
- */
- public String dataPath(String where) {
- // isAbsolute() could throw an access exception, but so will writing
- // to the local disk using the sketch path, so this is safe here.
- if (new File(where).isAbsolute()) return where;
-
- return sketchPath + File.separator + "data" + File.separator + where;
- }
-
-
- /**
- * Takes a path and creates any in-between folders if they don't
- * already exist. Useful when trying to save to a subfolder that
- * may not actually exist.
- */
- static public void createPath(String filename) {
- File file = new File(filename);
- String parent = file.getParent();
- if (parent != null) {
- File unit = new File(parent);
- if (!unit.exists()) unit.mkdirs();
- }
- }
-
-
- //////////////////////////////////////////////////////////////
-
- // SORT
-
- int sort_mode;
-
- static final int BYTES = 1;
- static final int CHARS = 2;
- static final int INTS = 3;
- static final int FLOATS = 4;
- static final int STRINGS = 5;
-
- byte sort_bytes[];
- char sort_chars[];
- int sort_ints[];
- float sort_floats[];
- String sort_strings[];
-
-
- public byte[] sort(byte what[]) {
- return sort(what, what.length);
- }
-
- public char[] sort(char what[]) {
- return sort(what, what.length);
- }
-
- public int[] sort(int what[]) {
- return sort(what, what.length);
- }
-
- public float[] sort(float what[]) {
- return sort(what, what.length);
- }
-
- public String[] sort(String what[]) {
- return sort(what, what.length);
- }
-
- //
-
- public byte[] sort(byte what[], int count) {
- if (count == 0) return null;
- sort_mode = BYTES;
- sort_bytes = new byte[count];
- System.arraycopy(what, 0, sort_bytes, 0, count);
- sort_internal(0, count-1);
- return sort_bytes;
- }
-
- public char[] sort(char what[], int count) {
- if (count == 0) return null;
- sort_mode = CHARS;
- sort_chars = new char[count];
- System.arraycopy(what, 0, sort_chars, 0, count);
- sort_internal(0, count-1);
- return sort_chars;
- }
-
- public int[] sort(int what[], int count) {
- if (count == 0) return null;
- sort_mode = INTS;
- sort_ints = new int[count];
- System.arraycopy(what, 0, sort_ints, 0, count);
- sort_internal(0, count-1);
- return sort_ints;
- }
-
- public float[] sort(float what[], int count) {
- if (count == 0) return null;
- sort_mode = FLOATS;
- sort_floats = new float[count];
- System.arraycopy(what, 0, sort_floats, 0, count);
- sort_internal(0, count-1);
- return sort_floats;
- }
-
- public String[] sort(String what[], int count) {
- if (count == 0) return null;
- sort_mode = STRINGS;
- sort_strings = new String[count];
- System.arraycopy(what, 0, sort_strings, 0, count);
- sort_internal(0, count-1);
- return sort_strings;
- }
-
- //
-
- protected void sort_internal(int i, int j) {
- int pivotIndex = (i+j)/2;
- sort_swap(pivotIndex, j);
- int k = sort_partition(i-1, j);
- sort_swap(k, j);
- if ((k-i) > 1) sort_internal(i, k-1);
- if ((j-k) > 1) sort_internal(k+1, j);
- }
-
-
- protected int sort_partition(int left, int right) {
- int pivot = right;
- do {
- while (sort_compare(++left, pivot) < 0) { }
- while ((right != 0) && (sort_compare(--right, pivot) > 0)) { }
- sort_swap(left, right);
- } while (left < right);
- sort_swap(left, right);
- return left;
- }
-
-
- protected void sort_swap(int a, int b) {
- switch (sort_mode) {
- case BYTES:
- byte btemp = sort_bytes[a];
- sort_bytes[a] = sort_bytes[b];
- sort_bytes[b] = btemp;
- break;
- case CHARS:
- char ctemp = sort_chars[a];
- sort_chars[a] = sort_chars[b];
- sort_chars[b] = ctemp;
- break;
- case INTS:
- int itemp = sort_ints[a];
- sort_ints[a] = sort_ints[b];
- sort_ints[b] = itemp;
- break;
- case FLOATS:
- float ftemp = sort_floats[a];
- sort_floats[a] = sort_floats[b];
- sort_floats[b] = ftemp;
- break;
- case STRINGS:
- String stemp = sort_strings[a];
- sort_strings[a] = sort_strings[b];
- sort_strings[b] = stemp;
- break;
- }
- }
-
- protected int sort_compare(int a, int b) {
- switch (sort_mode) {
- case BYTES:
- return sort_bytes[a] - sort_bytes[b];
- case CHARS:
- return sort_chars[a] - sort_chars[b];
- case INTS:
- return sort_ints[a] - sort_ints[b];
- case FLOATS:
- // can't just cast to an int because 0.2 and 0.4 would
- // just appear to be the same thing. no good.
- if (sort_floats[a] < sort_floats[b]) return -1;
- return (sort_floats[a] == sort_floats[b]) ? 0 : 1;
- case STRINGS:
- return sort_strings[a].compareTo(sort_strings[b]);
- }
- return 0;
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // ARRAY UTILITIES
-
-
- /**
- * Calls System.arraycopy(), included here so that we can
- * avoid people needing to learn about the System object
- * before they can just copy an array.
- */
- static public void arraycopy(Object src, int srcPosition,
- Object dst, int dstPosition,
- int length) {
- System.arraycopy(src, srcPosition, dst, dstPosition, length);
- }
-
-
- /**
- * Convenience method for arraycopy().
- * Identical to
- * 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;
- }
-
- /**
- * Note that toInt('5') is unlike String in the sense that it
- * won't return 5, but the ascii value. This is because ((int) someChar)
- * returns the ascii value, and toInt() is just longhand for the cast.
- */
- 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 private boolean int_nf_commas;
-
-
- 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) &&
- !int_nf_commas) {
- return int_nf.format(num);
- }
-
- int_nf = NumberFormat.getInstance();
- int_nf.setGroupingUsed(false); // no commas
- int_nf_commas = false;
- int_nf.setMinimumIntegerDigits(digits);
- int_nf_digits = digits;
- return int_nf.format(num);
- }
-
-
- static public String[] nfc(int num[]) {
- String formatted[] = new String[num.length];
- for (int i = 0; i < formatted.length; i++) {
- formatted[i] = nfc(num[i]);
- }
- return formatted;
- }
-
-
- static public String nfc(int num) {
- if ((int_nf != null) &&
- (int_nf_digits == 0) &&
- int_nf_commas) {
- return int_nf.format(num);
- }
-
- int_nf = NumberFormat.getInstance();
- int_nf.setGroupingUsed(true);
- int_nf_commas = true;
- int_nf.setMinimumIntegerDigits(0);
- int_nf_digits = 0;
- 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 private boolean float_nf_commas;
-
-
- 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) &&
- !float_nf_commas) {
- return float_nf.format(num);
- }
-
- float_nf = NumberFormat.getInstance();
- float_nf.setGroupingUsed(false);
- float_nf_commas = false;
-
- 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);
- }
-
-
- static public String[] nfc(float num[], int right) {
- String formatted[] = new String[num.length];
- for (int i = 0; i < formatted.length; i++) {
- formatted[i] = nfc(num[i], right);
- }
- return formatted;
- }
-
-
- static public String nfc(float num, int right) {
- if ((float_nf != null) &&
- (float_nf_left == 0) &&
- (float_nf_right == right) &&
- float_nf_commas) {
- return float_nf.format(num);
- }
-
- float_nf = NumberFormat.getInstance();
- float_nf.setGroupingUsed(true);
- float_nf_commas = true;
-
- if (right != 0) {
- float_nf.setMinimumFractionDigits(right);
- float_nf.setMaximumFractionDigits(right);
- }
- float_nf_left = 0;
- 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 public int unhex(String what) {
- // has to parse as a Long so that it'll work for numbers bigger than 2^31
- return (int) (Long.parseLong(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-size. 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 public 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);
- }
-
-
- /**
- * As of 0116 this also takes color(#FF8800, alpha)
- */
- public final int color(int gray, int alpha) {
- if (g == null) {
- if (alpha > 255) alpha = 255; else if (alpha < 0) alpha = 0;
- if (gray > 255) {
- // then assume this is actually a #FF8800
- return (alpha << 24) | (gray & 0xFFFFFF);
- } else {
- //if (gray > 255) gray = 255; else if (gray < 0) gray = 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 to help external communication run as a separate class.
- *
- * The options shown here are not yet finalized and will be
- * changing over the next several releases.
- *
- * The simplest way to turn and applet into an application is to
- * add the following code to your program:
- *
+ * 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 for this to be a float
+ * 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 (platformName.toLowerCase().indexOf("mac") != -1) {
+ // can only check this property if running on a mac
+ // on a pc it throws a security exception and kills the applet
+ // (but on the mac it does just fine)
+ 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 sketchPath; //folder;
+
+ /** When debugging headaches */
+ static final boolean THREAD_DEBUG = false;
+
+ /** Default width and height for applet when not specified */
+ static public final int DEFAULT_WIDTH = 100;
+ static public final int DEFAULT_HEIGHT = 100;
+
+ /**
+ * Minimum dimensions for the window holding an applet.
+ * This varies between platforms, Mac OS X 10.3 can do any height
+ * but requires at least 128 pixels width. Windows XP has another
+ * set of limitations. And for all I know, Linux probably lets you
+ * make windows with negative sizes.
+ */
+ static public final int MIN_WINDOW_WIDTH = 128;
+ static public final int MIN_WINDOW_HEIGHT = 128;
+
+ /**
+ * true if no size() command has been executed. This is used to wait until
+ * a size has been set before placing in the window and showing it.
+ */
+ public boolean defaultSize;
+
+ /**
+ * Pixel buffer from this applet's PGraphics.
+ *
+ * When used with OpenGL or Java2D, this value will
+ * be null until beginPixels() 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/y position of the mouse. This will be a different value
+ * when inside a mouse handler (like the mouseMoved() method) versus
+ * when inside draw(). Inside draw(), pmouseX is updated once each
+ * frame, but inside mousePressed() and friends, it's updated each time
+ * an event comes through. Be sure to use only one or the other type of
+ * means for tracking pmouseX and pmouseY within your sketch, otherwise
+ * you're gonna run into trouble.
+ */
+ public int pmouseX, 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;
+
+ /**
+ * Last mouse button pressed, one of LEFT, CENTER, or RIGHT.
+ *
+ * If running on Mac OS, a ctrl-click will be interpreted as
+ * the righthand mouse button (unlike Java, which reports it as
+ * the left mouse).
+ */
+ public int mouseButton;
+
+ 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;
+
+ /**
+ * true if exit() has been called so that things shut down
+ * once the main thread kicks off.
+ */
+ protected boolean exit;
+
+ 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;
+ //public Throwable 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_BGCOLOR = "--bgcolor";
+
+ static public final String ARGS_PRESENT = "--present";
+
+ static public final String ARGS_STOP_COLOR = "--stop-color";
+
+ static public final String ARGS_HIDE_STOP = "--hide-stop";
+
+ /**
+ * 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-path";
+
+ /**
+ * 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__";
+
+
+ // during rev 0100 dev cycle, working on new threading model,
+ // but need to disable and go conservative with changes in order
+ // to get pdf and audio working properly first
+ static final boolean CRUSTY_THREADS = true;
+
+
+ public void init() {
+ // first get placed size in case it's non-zero
+ Dimension initialSize = getSize();
+
+ // 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 draw() 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();
+
+ try {
+ getAppletContext();
+ online = true;
+ } catch (NullPointerException e) {
+ online = false;
+ }
+
+ if (javaVersion < 1.3f) {
+ addMouseListener(new MouseAdapter() {
+ public void mousePressed(MouseEvent e) {
+ link("http://java.com/");
+ }
+ });
+ // no init to do, so don't cause no trouble, boy
+ return;
+ // call this after making the methods to minimize the
+ // number of places needing the javaVersion crap
+ // (also needs to check online first and create empty
+ // stop method register list)
+ }
+
+ try {
+ if (sketchPath == null) {
+ sketchPath = System.getProperty("user.dir");
+ }
+ } catch (Exception e) { } // may be a security problem
+
+ // create a dummy graphics context
+ if ((initialSize.width != 0) && (initialSize.height != 0)) {
+ size(initialSize.width, initialSize.height);
+ } else {
+ //System.out.println("setting default");
+ size(DEFAULT_WIDTH, DEFAULT_HEIGHT);
+ this.defaultSize = true;
+ //System.out.println("zeroing");
+ //this.width = 0; // use this to flag whether the width/height are valid
+ //this.height = 0;
+ // need to set width/height otherwise
+ // they won't work for static mode apps
+ //defaultSize = true;
+ }
+
+ // this is automatically called in applets
+ // though it's here for applications anyway
+ start();
+ }
+
+
+ /**
+ * Called by the browser or applet viewer to inform this applet that it
+ * should start its execution. It is called after the init method and
+ * each time the applet is revisited in a Web page.
+ *
+ * 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 {
+ // create a JAVA2D renderer (the current default)
+ size(iwidth, iheight, JAVA2D);
+
+ /*
+ 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
+ }
+ */
+ }
+ }
+
+
+ public void size(int iwidth, int iheight, String irenderer) {
+ size(iwidth, iheight, irenderer, null);
+ }
+
+
+ /**
+ * Creates a new PGraphics object and sets it to the specified size.
+ *
+ * Note that you cannot change the renderer once outside of setup().
+ * In most cases, 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.
+ *
+ * XXXX Also note that this calls defaults(), which will reset any
+ * XXXX settings for the font, stroke, fill, colorMode, lights, etc.
+ */
+ public void size(int iwidth, int iheight,
+ String irenderer, String ipath) {
+ String currentRenderer =
+ (g == null) ? null : g.getClass().getName();
+
+ if (currentRenderer != null) {
+ if (currentRenderer.equals(irenderer)) {
+ if ((iwidth == g.width) && (iheight == g.height)) {
+ // in this case, size() is being called a second time because
+ // setup() is being called a second time, since the first time
+ // that setup was called, the renderer was changed so an
+ // exception was thrown and setup() didn't complete. but this
+ // time around, g is the proper size and the proper class.
+
+ // that or size() is being called again for no good reason,
+ // in which case we just ignore it anyway.
+
+ // so all that needs to be done is to set the defaults
+ // (clear the background, set default strokeWeight, etc).
+ //g.defaults();
+ // removed this in favor of calling defaults() from beginDraw()
+
+ // this will happen when P3D or OPENGL are used with size()
+ // inside of setup. it's also safe to call defaults() now,
+ // because it's happening inside setup, which is just frame 0,
+ // meaning that the graphics context is proper and visible.
+
+ } else { // just resizing, no need to create new graphics object
+ g.resize(iwidth, iheight);
+ updateSize(iwidth, iheight);
+ redraw(); // changed for rev 0100
+ }
+ // in either case, the renderer is unchanged, so return
+ //return;
+
+ } else { // renderer is being changed
+ if (frameCount > 0) {
+ throw new RuntimeException("size() cannot be called to change " +
+ "the renderer outside of setup()");
+ }
+ // otherwise ok to fall through and create renderer below
+ // the renderer is changing, so need to create a new object
+ g = createGraphics(iwidth, iheight, irenderer, ipath);
+ //if (g != null) {
+ updateSize(iwidth, iheight);
+ //}
+
+ // 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);
+ }
+ } else { // none exists, just create a freshy
+ g = createGraphics(iwidth, iheight, irenderer, ipath);
+ updateSize(iwidth, iheight);
+ }
+
+ /*
+ // the renderer is changing, so need to create a new object
+ g = createGraphics(iwidth, iheight, irenderer);
+ //if (g != null) {
+ updateSize(iwidth, iheight);
+ //}
+
+ //if ((currentRenderer != null) &&
+ // !currentRenderer.equals(irenderer)) {
+ if (currentRenderer != null) {
+ // 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);
+ }
+ */
+ }
+
+
+ /**
+ * Sets this.width and this.height, unsets defaultSize, and calls
+ * the size() methods inside any libraries.
+ */
+ protected void updateSize(int iwidth, int iheight) {
+ this.width = iwidth;
+ this.height = iheight;
+ defaultSize = false;
+
+ // make the applet itself larger.. it's a subclass of Component,
+ // so this is important for when it's embedded inside another app.
+ 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.
+
+ // if the default renderer is just being resized,
+ // restore it to its default values
+ //g.defaults();
+ // no, otherwise fonts that were set in setup() will go away
+
+ // this has to be called after the exception is thrown,
+ // otherwise the supporting libs won't have a valid context to draw to
+ Object methodArgs[] =
+ new Object[] { new Integer(width), new Integer(height) };
+ sizeMethods.handle(methodArgs);
+ }
+
+
+ /*
+ public PGraphics createGraphics(String renderer) {
+ return createGraphics(width, height, renderer);
+ }
+
+
+ public PGraphics createGraphics(int iwidth, int iheight) {
+ return createGraphics(iwidth, iheight, g.getClass().getName());
+ }
+ */
+
+
+ public PGraphics createGraphics(String irenderer, String ipath) {
+ return createGraphics(width, height, irenderer, this, ipath);
+ }
+
+
+ public PGraphics createGraphics(int iwidth, int iheight,
+ String irenderer) {
+ return createGraphics(iwidth, iheight, irenderer, this, null);
+ }
+
+
+ public PGraphics createGraphics(int iwidth, int iheight,
+ String irenderer, String ipath) {
+ return createGraphics(iwidth, iheight, irenderer, this, ipath);
+ }
+
+
+ static public PGraphics createGraphics(int iwidth, int iheight,
+ String irenderer, PApplet applet,
+ String ipath) {
+ /*
+ // ok when calling size, but not really with createGraphics()
+ if (renderer.equals(OPENGL)) {
+ throw new RuntimeException("createGraphics() with OPENGL is not " +
+ "supported. Use P3D instead.");
+ }
+ */
+
+ String openglError =
+ "Before using OpenGL, first select " +
+ "Import Library > opengl from the Sketch menu.";
+
+ try {
+ Class rendererClass = Class.forName(irenderer);
+ Class constructorParams[] = null;
+ Object constructorValues[] = null;
+
+ if (ipath == null) {
+ constructorParams = new Class[] {
+ Integer.TYPE, Integer.TYPE, PApplet.class
+ };
+ constructorValues = new Object[] {
+ new Integer(iwidth), new Integer(iheight), applet
+ };
+ } else {
+ // first make sure that this in a nice, full, absolute path
+ ipath = applet.savePath(ipath);
+
+ constructorParams = new Class[] {
+ Integer.TYPE, Integer.TYPE, PApplet.class, String.class
+ };
+ constructorValues = new Object[] {
+ new Integer(iwidth), new Integer(iheight), applet, ipath
+ };
+ }
+
+ Constructor constructor =
+ rendererClass.getConstructor(constructorParams);
+ // create the actual PGraphics object for rendering
+ return (PGraphics) constructor.newInstance(constructorValues);
+ //updateSize(iwidth, iheight);
+
+ } catch (InvocationTargetException ite) {
+ String msg = ite.getTargetException().getMessage();
+ if ((msg != null) &&
+ (msg.indexOf("no jogl in java.library.path") != -1)) {
+ throw new RuntimeException(openglError);
+
+ } else {
+ ite.getTargetException().printStackTrace();
+ Throwable target = ite.getTargetException();
+ if (platform == MACOSX) target.printStackTrace(System.out); // bug
+ // neither of these help, or work
+ //target.printStackTrace(System.err);
+ //System.err.flush();
+ //System.out.println(System.err); // and the object isn't null
+ throw new RuntimeException(target.getMessage());
+ }
+
+ } 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 " + irenderer + " to your sketch.");
+ }
+
+ } catch (Exception e) {
+ //System.out.println("ex3");
+ if ((e instanceof IllegalArgumentException) ||
+ (e instanceof NoSuchMethodException) ||
+ (e instanceof IllegalAccessException)) {
+
+ String msg = "public " +
+ irenderer.substring(irenderer.lastIndexOf('.') + 1) +
+ "(int width, int height, PApplet parent" +
+ ((ipath == null) ? "" : ", String filename") +
+ ") does not exist.";
+ throw new RuntimeException(msg);
+
+ } else {
+ if (platform == MACOSX) e.printStackTrace(System.out);
+ //System.err.flush();
+ //return null;
+ throw new RuntimeException(e.getMessage());
+ //die("Could not create " + irenderer);
+ }
+
+ /*
+ } catch (Exception e) {
+ e.printStackTrace();
+ die("Could not start because of a problem with size()", e);
+ */
+ }
+
+ // clear things out to get started
+ //outgoing.defaults();
+ // tell people to use beginDraw/endDraw
+
+ // and send 'em off
+ //return outgoing;
+ }
+
+
+ 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) {
+ if (javaVersion < 1.3f) {
+ screen.setColor(new Color(64, 64, 64));
+ Dimension size = getSize();
+ screen.fillRect(0, 0, size.width, size.height);
+ screen.setColor(Color.white);
+ screen.setFont(new Font("Dialog", Font.PLAIN, 9));
+ screen.drawString("You need to install", 3, 15);
+ screen.drawString("Java 1.3 or later", 3, 28);
+ screen.drawString("to view this content.", 3, 41);
+ screen.drawString("Click here to visit", 3, 59);
+ screen.drawString("java.com and install.", 3, 72);
+ return;
+ }
+
+ //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
+ // (also prevents over-drawing when using PGraphicsGL)
+ 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;
+ }
+
+
+ /*synchronized*/ 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);
+ //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 || finished) ? 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;
+ if (CRUSTY_THREADS) {
+ Thread.sleep(nap);
+ } else {
+ wait(nap);
+ }
+ if (THREAD_DEBUG) println(Thread.currentThread().getName() +
+ " outta sleep");
+ } catch (InterruptedException e) { }
+ }
+
+ } catch (Exception e) {
+ // 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;
+ if (e instanceof InvocationTargetException) {
+ //System.out.println("target problem");
+ e = (Exception) (((InvocationTargetException) e).getTargetException());
+ }
+ 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);
+ e.printStackTrace(System.out);
+
+ } else {
+ System.err.println(LEECH_WAKEUP);
+ e.printStackTrace();
+ e.printStackTrace(System.out);
+ }
+ }
+ if (THREAD_DEBUG) println(Thread.currentThread().getName() +
+ " thread finished");
+
+ // this may not be safe? this will get triggered with exit()
+ // but need to see if this is it
+ //if ((leechErr == null) && !online) {
+ //System.exit(0);
+ //}
+
+ //System.out.println("exiting run " + finished);
+ stop(); // call to shutdown libs?
+
+ if (exit) { // user called exit() function
+ if ((leechErr == null) && !online) {
+ // don't want to call System.exit() when an applet,
+ // or running inside the PDE (would kill the PDE)
+ System.exit(0);
+ }
+ }
+ }
+
+
+ synchronized public void handleDisplay() {
+ 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 beginDraw");
+ g.beginDraw();
+ if (THREAD_DEBUG) println(Thread.currentThread().getName() +
+ " 1b draw");
+
+ //boolean recorderNull = true;
+ //boolean recorderRawNull = true;
+
+ 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("runtime extends " + e);
+ //System.out.println("catching a cold " + e.getMessage());
+ String msg = e.getMessage();
+ if ((msg != null) &&
+ (e.getMessage().indexOf(NEW_RENDERER) != -1)) {
+ //System.out.println("got new renderer");
+ return;
+ //continue; // will this work?
+
+ } else {
+ //e.printStackTrace(System.out);
+ //System.out.println("re-throwing");
+ 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 beginPixels
+ // now for certain that we've got a valid size
+ this.width = g.width;
+ this.height = g.height;
+ this.defaultSize = false;
+
+ } else { // frameCount > 0, meaning an actual draw()
+ // 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);
+ long now = System.currentTimeMillis();
+ int napTime = (int) (timeToLeave - now);
+ if (napTime > 0) {
+ framerateLastDelayTime = timeToLeave;
+ delay(napTime);
+ } else {
+ // nap time is negative, need to reset clock (bug #336)
+ framerateLastDelayTime = now;
+ }
+ }
+ }
+
+ preMethods.handle();
+
+ pmouseX = dmouseX;
+ pmouseY = dmouseY;
+
+ //synchronized (glock) {
+ //synchronized (this) {
+ //try {
+ draw();
+ /*
+ // seems to catch, but then blanks out
+ } catch (Exception e) {
+ if (e instanceof InvocationTargetException) {
+ System.out.println("found poo");
+ ((InvocationTargetException)e).getTargetException().printStackTrace(System.out);
+ }
+ }
+ */
+ //}
+ //}
+
+ // set a flag regarding whether the recorders were non-null
+ // as of draw().. this will prevent the recorder from being
+ // reset if recordShape() is called in an event method, such
+ // as mousePressed()
+ //recorderNull = (recorder == null);
+ //recorderRawNull = (g.recorderRaw == null);
+
+ // 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 endDraw");
+
+ 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.endDraw();
+ /*
+ if (!recorderNull) {
+ if (recorder != null) {
+ recorder.endDraw();
+ recorder = null;
+ }
+ }
+ if (!recorderRawNull) {
+ if (g.recorderRaw != null) {
+ g.recorderRaw.endDraw();
+ g.recorderRaw = 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
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ protected boolean listenersAdded;
+
+ public void addListeners() {
+ if (!listenersAdded) {
+ addMouseListener(this);
+ addMouseMotionListener(this);
+ addKeyListener(this);
+ addFocusListener(this);
+
+ addComponentListener(new ComponentAdapter() {
+ public void componentResized(ComponentEvent e) {
+ Component c = e.getComponent();
+ Rectangle bounds = c.getBounds();
+ //System.out.println("componentResized()");
+ //System.out.println(" " + c.getClass().getName());
+ //println(" visible " + isVisible());
+ //System.out.println(" " + e);
+ //System.out.println(" bounds: " + bounds);
+ //int newWidth = bounds.width - bounds.x * 2;
+ //int newHeight = bounds.height - (bounds.y + bounds.x);
+ //System.out.println(" new: " + newWidth + " " + newHeight);
+
+ size(bounds.width, bounds.height);
+
+ //if (c == PApplet.this) {
+ //Container con = (Container) c;
+ //Dimension newSize = getSize();
+ //System.out.println("resizing to " + newSize + " ");
+ //System.out.println(c.getBounds());
+ //System.out.println(e);
+ //System.out.println(c);
+ //System.out.println("insets " + con.getInsets());
+ //size(newSize.width, newSize.height);
+ //}
+ }
+ });
+
+ listenersAdded = true;
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ 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;
+
+ int modifiers = event.getModifiers();
+ if ((modifiers & MouseEvent.BUTTON1_MASK) != 0) {
+ mouseButton = LEFT;
+ } else if ((modifiers & MouseEvent.BUTTON2_MASK) != 0) {
+ mouseButton = CENTER;
+ } else if ((modifiers & MouseEvent.BUTTON3_MASK) != 0) {
+ mouseButton = RIGHT;
+ }
+ // if running on macos, allow ctrl-click as right mouse
+ if ((platform == MACOSX) || (platform == MACOS9)) {
+ if (mouseEvent.isPopupTrigger()) {
+ mouseButton = RIGHT;
+ }
+ }
+
+ mouseEventMethods.handle(new Object[] { event });
+
+ // 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;
+ }
+
+ int id = event.getID();
+ switch (id) {
+ 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;
+ }
+ // an attempt to solve bug 170
+ // http://dev.processing.org/bugs/show_bug.cgi?id=170
+ //if ((id == MouseEvent.MOUSE_DRAGGED) ||
+ // (id == MouseEvent.MOUSE_MOVED)) {
+ //println(emouseX + " " + emouseY + " " + mouseX + " " + mouseY);
+ 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;
+ }
+
+ // if someone else wants to intercept the key, they should
+ // set key to zero (or something besides the ESC).
+ if ((event.getID() == KeyEvent.KEY_PRESSED) &&
+ (key == KeyEvent.VK_ESCAPE)) {
+ exit();
+ }
+ }
+
+
+ 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.
+ * Because of how operating systems handle key repeats, holding
+ * down a key will cause multiple calls to keyPressed(), because
+ * the OS repeat takes over.
+ *
+ * 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)
+ *
+ * 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:
+ * 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
+
+
+ /**
+ * Delay for some amount of time. I'm not sure if this is even
+ * helpful anymore, as the screen isn't updated before or after the
+ * delay, meaning which means it just makes the app lock up
+ * temporarily.
+ */
+ public void delay(int napTime) {
+ if (frameCount == 0) return;
+ if (napTime > 0) {
+ try {
+ if (CRUSTY_THREADS) {
+ Thread.sleep(napTime);
+ } else {
+ wait(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?
+ }
+ }
+
+
+ public void link(String here) {
+ link(here, null);
+ }
+
+
+ /**
+ * Link to an external page without all the muss.
+ *
+ * When run with an applet, uses the browser to open the url,
+ * for applications, attempts to launch a browser with the url.
+ *
+ * Works on Mac OS X and Windows. For Linux, use:
+ *
+ * Best used just before endDraw() at the end of your draw().
+ * 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");
+ g.save(savePath("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.
+ *
+ * Based on code contributed by Amit Pitaru, plus additional
+ * code to handle Java versions via reflection by Jonathan Feinberg.
+ */
+ public void cursor(PImage image, int hotspotX, int hotspotY) {
+ 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));
+
+ 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 " +
+ "when using Java " + javaVersionName);
+ } 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[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) {
+ if (what == null) {
+ // special case since this does fuggly things on > 1.1
+ System.out.print("null");
+
+ } else {
+ String name = what.getClass().getName();
+ if (name.charAt(0) == '[') {
+ switch (name.charAt(1)) {
+ case '[':
+ // don't even mess with multi-dimensional arrays (case '[')
+ // or anything else that's not int, float, boolean, char
+ System.out.print(what);
+ System.out.print(' ');
+ break;
+
+ case 'L':
+ // print a 1D array of objects as individual elements
+ Object poo[] = (Object[]) what;
+ for (int i = 0; i < poo.length; i++) {
+ System.out.print(poo[i]);
+ System.out.print(' ');
+ }
+ break;
+
+ case 'Z': // boolean
+ boolean zz[] = (boolean[]) what;
+ for (int i = 0; i < zz.length; i++) {
+ System.out.print(zz[i]);
+ System.out.print(' ');
+ }
+ break;
+
+ case 'B': // byte
+ byte bb[] = (byte[]) what;
+ for (int i = 0; i < bb.length; i++) {
+ System.out.print(bb[i]);
+ System.out.print(' ');
+ }
+ break;
+
+ case 'C': // char
+ char cc[] = (char[]) what;
+ for (int i = 0; i < cc.length; i++) {
+ System.out.print(cc[i]);
+ System.out.print(' ');
+ }
+ break;
+
+ case 'I': // int
+ int ii[] = (int[]) what;
+ for (int i = 0; i < ii.length; i++) {
+ System.out.print(ii[i]);
+ System.out.print(' ');
+ }
+ break;
+
+ case 'F': // float
+ float ff[] = (float[]) what;
+ for (int i = 0; i < ff.length; i++) {
+ System.out.print(ff[i]);
+ System.out.print(' ');
+ }
+ break;
+
+ case 'D': // double
+ double dd[] = (double[]) what;
+ for (int i = 0; i < dd.length; i++) {
+ System.out.print(dd[i]);
+ System.out.print(' ');
+ }
+ break;
+
+ default:
+ System.out.print(what);
+ }
+ } else {
+ System.out.print(what); //.toString());
+ }
+ }
+ }
+
+ //
+
+ 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) {
+ if (what == null) {
+ // special case since this does fuggly things on > 1.1
+ System.out.println("null");
+
+ } else {
+ String name = what.getClass().getName();
+ if (name.charAt(0) == '[') {
+ switch (name.charAt(1)) {
+ case '[':
+ // don't even mess with multi-dimensional arrays (case '[')
+ // or anything else that's not int, float, boolean, char
+ System.out.println(what);
+ break;
+
+ case 'L':
+ // print a 1D array of objects as individual elements
+ Object poo[] = (Object[]) what;
+ for (int i = 0; i < poo.length; i++) {
+ System.out.println(poo[i]);
+ }
+ break;
+
+ case 'Z': // boolean
+ boolean zz[] = (boolean[]) what;
+ for (int i = 0; i < zz.length; i++) {
+ System.out.println(zz[i]);
+ }
+ break;
+
+ case 'B': // byte
+ byte bb[] = (byte[]) what;
+ for (int i = 0; i < bb.length; i++) {
+ System.out.println(bb[i]);
+ }
+ break;
+
+ case 'C': // char
+ char cc[] = (char[]) what;
+ for (int i = 0; i < cc.length; i++) {
+ System.out.println(cc[i]);
+ }
+ break;
+
+ case 'I': // int
+ int ii[] = (int[]) what;
+ for (int i = 0; i < ii.length; i++) {
+ System.out.println(ii[i]);
+ }
+ break;
+
+ case 'F': // float
+ float ff[] = (float[]) what;
+ for (int i = 0; i < ff.length; i++) {
+ System.out.println(ff[i]);
+ }
+ break;
+
+ case 'D': // double
+ double dd[] = (double[]) what;
+ for (int i = 0; i < dd.length; i++) {
+ System.out.println(dd[i]);
+ }
+ break;
+
+ default:
+ System.out.println(what);
+ }
+ } else {
+ System.out.println(what); //.toString());
+ }
+ }
+ }
+
+ //
+
+ /*
+ // not very useful, because it only works for public (and protected?)
+ // fields of a class, not local variables to methods
+ 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);
+ return (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);
+ return (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);
+ return (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);
+ return (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 int ceil(float what) {
+ return (int) Math.ceil(what);
+ }
+
+ static public final int floor(float what) {
+ return (int) Math.floor(what);
+ }
+
+ static public final int round(float what) {
+ return (int) 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<
+ * Generally, loadImage() should only be used during setup, because
+ * re-loading images inside draw() is likely to cause a significant
+ * delay while memory is allocated and the thread blocks while waiting
+ * for the image to load because loading is not asynchronous.
+ *
+ * To load several images asynchronously, see more information in the
+ * FAQ about writing your own threaded image loading method.
+ *
+ * As of 0096, returns null if no image of that name is found,
+ * rather than an error.
+ *
+ * Release 0115 also provides support for reading TIFF and RLE-encoded
+ * Targa (.tga) files written by Processing via save() and saveFrame().
+ * Other TIFF and Targa files will probably not load, use a different
+ * format (gif, jpg and png are safest bets) when creating images with
+ * another application to use with Processing.
+ *
+ * Also in release 0115, more image formats (BMP and others) can
+ * be read when using Java 1.4 and later. Because many people still
+ * use Java 1.1 and 1.3, these formats are not recommended for
+ * work that will be posted on the web. To get a list of possible
+ * image formats for use with Java 1.4 and later, use the following:
+ * println(javax.imageio.ImageIO.getReaderFormatNames())
+ */
+ public PImage loadImage(String filename) {
+ // it's not clear whether this method is more efficient for
+ // loading gif, jpeg, and png data than the standard toolkit function,
+ // in fact it may even be identical. but who knows, with any luck
+ // it may even fix the image loading problems from bug #279.
+ // http://dev.processing.org/bugs/show_bug.cgi?id=279
+ // (if anyone reading this knows for certain, please post)
+ if (PApplet.javaVersion >= 1.4f) {
+ if (loadImageFormats == null) {
+ //loadImageFormats = javax.imageio.ImageIO.getReaderFormatNames();
+ try {
+ Class ioClass = Class.forName("javax.imageio.ImageIO");
+ Method getFormatNamesMethod =
+ ioClass.getMethod("getReaderFormatNames", (Class[]) null);
+ loadImageFormats = (String[])
+ getFormatNamesMethod.invoke((Class[]) null, (Object[]) null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ if (loadImageFormats != null) {
+ for (int i = 0; i < loadImageFormats.length; i++) {
+ if (filename.endsWith("." + loadImageFormats[i])) {
+ return loadImageIO(filename);
+ }
+ }
+ }
+ }
+
+ if (filename.toLowerCase().endsWith(".tga")) {
+ try {
+ return loadImageTGA(filename);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ byte bytes[] = loadBytes(filename);
+ if (bytes == null) return null;
+
+ if (filename.toLowerCase().endsWith(".tif") ||
+ filename.toLowerCase().endsWith(".tiff")) {
+ return PImage.loadTIFF(bytes);
+ }
+
+ Image awtImage = Toolkit.getDefaultToolkit().createImage(bytes);
+
+ MediaTracker tracker = new MediaTracker(this);
+ tracker.addImage(awtImage, 0);
+ try {
+ tracker.waitForAll();
+ } catch (InterruptedException e) {
+ // don't bother, since this may be interrupted by draw
+ // or noLoop or something like that
+ //e.printStackTrace(); // non-fatal, right?
+ }
+
+ PImage image = new PImage(awtImage);
+
+ // if it's a .gif image, test to see if it has transparency
+ if ((filename.toLowerCase().endsWith(".gif")) ||
+ (filename.toLowerCase().endsWith(".png"))) {
+ image.checkAlpha();
+ }
+ return image;
+ }
+
+
+ /**
+ * Use Java 1.4 ImageIO methods to load an image. All done via reflection
+ * in order to maintain compatability with previous releases.
+ */
+ protected PImage loadImageIO(String filename) {
+ InputStream stream = openStream(filename);
+ if (stream == null) {
+ System.err.println("The image " + filename + " could not be found.");
+ return null;
+ }
+
+ try {
+ Class ioClass = Class.forName("javax.imageio.ImageIO");
+ Method readMethod =
+ ioClass.getMethod("read", new Class[] { InputStream.class });
+ Object bimage = readMethod.invoke(null, new Object[] { stream });
+
+ // need to get width and height, then create pixels[] at that size
+ //int px[] = null;
+
+ Class biClass =
+ Class.forName("java.awt.image.BufferedImage");
+
+ Method getHeightMethod =
+ biClass.getMethod("getHeight", (Class[]) null);
+ Integer hi = (Integer) getHeightMethod.invoke(bimage, (Object[]) null);
+
+ Method getWidthMethod =
+ biClass.getMethod("getWidth", (Class[]) null);
+ Integer wi = (Integer) getWidthMethod.invoke(bimage, (Object[]) null);
+
+ // was gonna call getType() on the image to see if RGB or ARGB,
+ // but it's not actually useful, since gif images will come through
+ // as TYPE_BYTE_INDEXED, which means it'll still have to check for
+ // the transparency. also, would have to iterate through all the other
+ // types and guess whether alpha was in there, so.. just gonna stick
+ // with the old method.
+
+ PImage outgoing = new PImage(wi.intValue(), hi.intValue());
+
+ Method getRgbMethod =
+ biClass.getMethod("getRGB", new Class[] {
+ Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE,
+ outgoing.pixels.getClass(), Integer.TYPE, Integer.TYPE
+ });
+ getRgbMethod.invoke(bimage, new Object[] {
+ new Integer(0), new Integer(0),
+ new Integer(outgoing.width), new Integer(outgoing.height),
+ outgoing.pixels, new Integer(0), new Integer(outgoing.width)
+ });
+
+ // check the alpha for this image
+ outgoing.checkAlpha();
+
+ // return the image
+ return outgoing;
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+
+ /**
+ * Targa image loader for RLE-compressed TGA files.
+ *
+ * Rewritten for 0115 to read/write RLE-encoded targa images.
+ */
+ protected PImage loadImageTGA(String filename) throws IOException {
+ InputStream is = openStream(filename);
+ if (is == null) return null;
+
+ byte header[] = new byte[18];
+ int offset = 0;
+ do {
+ int count = is.read(header, offset, header.length - offset);
+ if (count == -1) return null;
+ offset += count;
+ } while (offset < 18);
+
+ int format = 0;
+ if ((header[2] == 0x0B) &&
+ (header[16] == 0x08) &&
+ (header[17] == 0x28)) {
+ format = ALPHA;
+
+ } else if ((header[2] == 0x0A) &&
+ (header[16] == 24) &&
+ (header[17] == 0x20)) {
+ format = RGB;
+
+ } else if ((header[2] == 0x0A) &&
+ (header[16] == 32) &&
+ (header[17] == 0x28)) {
+ format = ARGB;
+ }
+ if (format == 0) {
+ System.err.println("Unknown .tga file format for " + filename);
+ return null;
+ }
+
+ // get image dimensions
+ int w = ((header[13] & 0xff) << 8) + (header[12] & 0xff);
+ int h = ((header[15] & 0xff) << 8) + (header[14] & 0xff);
+ PImage outgoing = new PImage(w, h, format);
+
+ int index = 0;
+ int px[] = outgoing.pixels;
+
+ while (index < px.length) {
+ int num = is.read();
+ boolean isRLE = (num & 0x80) != 0;
+ if (isRLE) {
+ num -= 127; // (num & 0x7F) + 1
+ int pixel = 0;
+ switch (format) {
+ case ALPHA:
+ pixel = is.read();
+ break;
+ case RGB:
+ pixel = 0xFF000000 |
+ is.read() | (is.read() << 8) | (is.read() << 16);
+ //(is.read() << 16) | (is.read() << 8) | is.read();
+ break;
+ case ARGB:
+ pixel = is.read() |
+ (is.read() << 8) | (is.read() << 16) | (is.read() << 24);
+ break;
+ }
+ for (int i = 0; i < num; i++) {
+ px[index++] = pixel;
+ if (index == px.length) break;
+ }
+ } else { // write up to 127 bytes as uncompressed
+ num += 1;
+ switch (format) {
+ case ALPHA:
+ for (int i = 0; i < num; i++) {
+ px[index++] = is.read();
+ }
+ break;
+ case RGB:
+ for (int i = 0; i < num; i++) {
+ px[index++] = 0xFF000000 |
+ is.read() | (is.read() << 8) | (is.read() << 16);
+ //(is.read() << 16) | (is.read() << 8) | is.read();
+ }
+ break;
+ case ARGB:
+ for (int i = 0; i < num; i++) {
+ px[index++] = is.read() | //(is.read() << 24) |
+ (is.read() << 8) | (is.read() << 16) | (is.read() << 24);
+ //(is.read() << 16) | (is.read() << 8) | is.read();
+ }
+ break;
+ }
+ }
+ }
+ return outgoing;
+
+ /*
+ // targa's are written upside down, so we need to parse it in reverse
+ int index = (h-1) * w;
+ // actual bitmap data starts at byte 18
+ int offset = 18;
+
+ // read out line by line
+ for (int y = h-1; y >= 0; y--) {
+ for (int x = 0; x < w; x++) {
+ img.pixels[index + x] =
+ (buffer[offset++] & 0xff) |
+ ((buffer[offset++] & 0xff) << 8) |
+ ((buffer[offset++] & 0xff) << 16) |
+ (hasAlpha ? ((buffer[offset++] & 0xff) << 24) : 0xff000000);
+ }
+ index -= w;
+ }
+ return img;
+ }
+
+ System.err.println("loadImage(): bad targa image format");
+ return null;
+ */
+}
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // FONT I/O
+
+
+ Hashtable fontTable;
+
+ /**
+ * Set the font based on its filename. This is less than efficient
+ * than using loadFont because there's no way to unload it from memory,
+ * but it's useful for beginners.
+ */
+ public void textFont(String filename) {
+ if (filename.toLowerCase().indexOf(".vlw") == -1) {
+ System.err.println("textFont() needs the filename of a .vlw font");
+ } else {
+ textFont(tableFont(filename));
+ }
+ }
+
+
+ /**
+ * Set the font based on its filename. This is less than efficient
+ * than using loadFont because there's no way to unload it from memory,
+ * but it's useful for beginners.
+ */
+ public void textFont(String filename, float size) {
+ if (filename.toLowerCase().indexOf(".vlw") == -1) {
+ System.err.println("textFont() needs the filename of a .vlw font");
+ } else {
+ textFont(tableFont(filename), size);
+ }
+ }
+
+
+ protected PFont tableFont(String filename) {
+ if (fontTable == null) fontTable = new Hashtable();
+
+ PFont font = (PFont) fontTable.get(filename);
+ if (font != null) return font;
+
+ font = loadFont(filename);
+ return font;
+ }
+
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+ public PFont loadFont(String filename) {
+ //if (g == null) { // just for good measure
+ //die("loadFont() only be used inside setup() or draw()");
+ //}
+
+ try {
+ String lower = filename.toLowerCase();
+ InputStream input = openStream(filename);
+
+ if (lower.endsWith(".vlw.gz")) {
+ input = new GZIPInputStream(input);
+
+ } else if (!lower.endsWith(".vlw")) {
+ // this gets thrown down below
+ throw new IOException("I don't know how to load a font named " +
+ filename);
+ }
+ return new PFont(input);
+
+ } catch (Exception e) {
+ die("Could not load font " + filename + ". " +
+ "Make sure that the font has been copied " +
+ "to the data folder of your sketch.", e);
+ }
+ return null;
+ }
+
+
+ public PFont createFont(String name, float size) {
+ return createFont(name, size, true, PFont.DEFAULT_CHARSET);
+ }
+
+
+ public PFont createFont(String name, float size, boolean smooth) {
+ return createFont(name, size, smooth, PFont.DEFAULT_CHARSET);
+ }
+
+
+ /**
+ * Create a .vlw font on the fly from either a font name that's
+ * installed on the system, or from a .ttf or .otf that's inside
+ * the data folder of this sketch.
+ *
+ * The parentFrame is the Frame that will guide the placement of
+ * the prompt window. If no Frame is available, just pass in null.
+ */
+ // can't be static because it wants a host component
+ static public File inputFile(String prompt, Frame parentFrame) {
+ if (parentFrame == null) parentFrame = new Frame();
+ FileDialog fd = new FileDialog(parentFrame, prompt, FileDialog.LOAD);
+ fd.setVisible(true);
+
+ String directory = fd.getDirectory();
+ String filename = fd.getFile();
+ if (filename == null) return null;
+ return new File(directory, filename);
+ }
+
+
+ public File outputFile() {
+ return outputFile("Save as...");
+ }
+
+
+ public File outputFile(String prompt) {
+ Frame parentFrame = null;
+ Component comp = getParent();
+ while (comp != null) {
+ //System.out.println(comp + " " + comp.getClass());
+ if (comp instanceof Frame) {
+ parentFrame = (Frame) comp;
+ break;
+ }
+ comp = comp.getParent();
+ }
+ return outputFile(prompt, parentFrame);
+ }
+
+
+
+ static public File outputFile(Frame parentFrame) {
+ return outputFile("Save as...", parentFrame);
+ }
+
+
+ /**
+ * static version of outputFile usable by external classes.
+ *
+ * The parentFrame is the Frame that will guide the placement of
+ * the prompt window. If no Frame is available, just pass in null.
+ */
+ static public File outputFile(String prompt, Frame parentFrame) {
+ if (parentFrame == null) parentFrame = new Frame();
+ FileDialog fd = new FileDialog(parentFrame, prompt, FileDialog.SAVE);
+ fd.setVisible(true);
+
+ String directory = fd.getDirectory();
+ String filename = fd.getFile();
+ if (filename == null) return null;
+ return new File(directory, filename);
+ }
+
+
+ /**
+ * I want to read lines from a file. I have RSI from typing these
+ * eight lines of code so many times.
+ */
+ public BufferedReader reader(String filename) {
+ try {
+ InputStream is = openStream(filename);
+ if (is == null) {
+ System.err.println(filename + " does not exist or could not be read");
+ return null;
+ }
+ return reader(is);
+
+ } catch (Exception e) {
+ if (filename == null) {
+ die("Filename passed to reader() was null", e);
+ } else {
+ die("Couldn't create a reader for " + filename, e);
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * I want to read lines from a file. And I'm still annoyed.
+ */
+ static public BufferedReader reader(File file) {
+ try {
+ return reader(new FileInputStream(file));
+
+ } catch (Exception e) {
+ if (file == null) {
+ throw new RuntimeException("File passed to reader() was null");
+ } else {
+ e.printStackTrace();
+ throw new RuntimeException("Couldn't create a reader for " +
+ file.getAbsolutePath());
+ }
+ }
+ //return null;
+ }
+
+
+ /**
+ * I want to read lines from a stream. If I have to type the
+ * following lines any more I'm gonna send Sun my medical bills.
+ */
+ static public BufferedReader reader(InputStream input) {
+ InputStreamReader isr = new InputStreamReader(input);
+ return new BufferedReader(isr);
+ }
+
+
+ /**
+ * decode a gzip input stream
+ */
+ static public InputStream gzipInput(InputStream input) {
+ try {
+ return new GZIPInputStream(input);
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Problem with gzip input");
+ }
+ //return null;
+ }
+
+
+ /**
+ * decode a gzip output stream
+ */
+ static public OutputStream gzipOutput(OutputStream output) {
+ try {
+ return new GZIPOutputStream(output);
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Problem with gzip output");
+ }
+ //return null;
+ }
+
+
+ /**
+ * I want to print lines to a file. Why can't I?
+ */
+ public PrintWriter writer(String filename) {
+ try {
+ return writer(new FileOutputStream(savePath(filename)));
+
+ } catch (Exception e) {
+ if (filename == null) {
+ die("Filename passed to writer() was null", e);
+ } else {
+ die("Couldn't create a writer for " + filename, e);
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * I want to print lines to a file. I have RSI from typing these
+ * eight lines of code so many times.
+ */
+ static public PrintWriter writer(File file) {
+ try {
+ return writer(new FileOutputStream(file));
+
+ } catch (Exception e) {
+ if (file == null) {
+ throw new RuntimeException("File passed to writer() was null");
+ } else {
+ e.printStackTrace();
+ throw new RuntimeException("Couldn't create a writer for " +
+ file.getAbsolutePath());
+ }
+ }
+ //return null;
+ }
+
+
+ /**
+ * I want to print lines to a file. Why am I always explaining myself?
+ * It's the JavaSoft API engineers who need to explain themselves.
+ */
+ static public PrintWriter writer(OutputStream output) {
+ OutputStreamWriter osw = new OutputStreamWriter(output);
+ return new PrintWriter(osw);
+ }
+
+
+ static public InputStream openStream(File file) {
+ try {
+ return new FileInputStream(file);
+
+ } catch (IOException e) {
+ if (file == null) {
+ throw new RuntimeException("File passed to openStream() was null");
+
+ } else {
+ e.printStackTrace();
+ throw new RuntimeException("Couldn't openStream() for " +
+ file.getAbsolutePath());
+ }
+ }
+ }
+
+
+ /**
+ * Simplified method to open a Java InputStream.
+ *
+ * This method is useful if you want to use the facilities provided
+ * by PApplet to easily open things from the data folder or from a URL,
+ * but want an InputStream object so that you can use other Java
+ * methods to take more control of how the stream is read.
+ *
+ * If the requested item doesn't exist, null is returned.
+ * (Prior to 0096, die() would be called, killing the applet)
+ *
+ * For 0096, the "data" folder is exported intact with subfolders,
+ * and openStream() properly handles subdirectories from the data folder
+ *
+ * If not online, this will also check to see if the user is asking
+ * for a file whose name isn't properly capitalized. This helps prevent
+ * issues when a sketch is exported to the web, where case sensitivity
+ * matters, as opposed to Windows and the Mac OS default where
+ * case sensitivity is preserved but ignored.
+ *
+ * It is strongly recommended that libraries use this method to open
+ * data files, so that the loading sequence is handled in the same way
+ * as functions like loadBytes(), loadImage(), etc.
+ *
+ * The filename passed in can be:
+ *
+ * In this method, the data path is defined not as the applet's actual
+ * data path, but a folder titled "data" in the sketch's working
+ * directory. This is because in an application, the "data" folder is
+ * exported as part of the jar file, and it's not as though you're gonna
+ * write into the jar file itself. If you need to get things out of
+ * the jar file, you should use openStream().
+ */
+ public String dataPath(String where) {
+ // isAbsolute() could throw an access exception, but so will writing
+ // to the local disk using the sketch path, so this is safe here.
+ if (new File(where).isAbsolute()) return where;
+
+ return sketchPath + File.separator + "data" + File.separator + where;
+ }
+
+
+ /**
+ * Takes a path and creates any in-between folders if they don't
+ * already exist. Useful when trying to save to a subfolder that
+ * may not actually exist.
+ */
+ static public void createPath(String filename) {
+ File file = new File(filename);
+ String parent = file.getParent();
+ if (parent != null) {
+ File unit = new File(parent);
+ if (!unit.exists()) unit.mkdirs();
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // SORT
+
+ int sort_mode;
+
+ static final int BYTES = 1;
+ static final int CHARS = 2;
+ static final int INTS = 3;
+ static final int FLOATS = 4;
+ static final int STRINGS = 5;
+
+ byte sort_bytes[];
+ char sort_chars[];
+ int sort_ints[];
+ float sort_floats[];
+ String sort_strings[];
+
+
+ public byte[] sort(byte what[]) {
+ return sort(what, what.length);
+ }
+
+ public char[] sort(char what[]) {
+ return sort(what, what.length);
+ }
+
+ public int[] sort(int what[]) {
+ return sort(what, what.length);
+ }
+
+ public float[] sort(float what[]) {
+ return sort(what, what.length);
+ }
+
+ public String[] sort(String what[]) {
+ return sort(what, what.length);
+ }
+
+ //
+
+ public byte[] sort(byte what[], int count) {
+ if (count == 0) return null;
+ sort_mode = BYTES;
+ sort_bytes = new byte[count];
+ System.arraycopy(what, 0, sort_bytes, 0, count);
+ sort_internal(0, count-1);
+ return sort_bytes;
+ }
+
+ public char[] sort(char what[], int count) {
+ if (count == 0) return null;
+ sort_mode = CHARS;
+ sort_chars = new char[count];
+ System.arraycopy(what, 0, sort_chars, 0, count);
+ sort_internal(0, count-1);
+ return sort_chars;
+ }
+
+ public int[] sort(int what[], int count) {
+ if (count == 0) return null;
+ sort_mode = INTS;
+ sort_ints = new int[count];
+ System.arraycopy(what, 0, sort_ints, 0, count);
+ sort_internal(0, count-1);
+ return sort_ints;
+ }
+
+ public float[] sort(float what[], int count) {
+ if (count == 0) return null;
+ sort_mode = FLOATS;
+ sort_floats = new float[count];
+ System.arraycopy(what, 0, sort_floats, 0, count);
+ sort_internal(0, count-1);
+ return sort_floats;
+ }
+
+ public String[] sort(String what[], int count) {
+ if (count == 0) return null;
+ sort_mode = STRINGS;
+ sort_strings = new String[count];
+ System.arraycopy(what, 0, sort_strings, 0, count);
+ sort_internal(0, count-1);
+ return sort_strings;
+ }
+
+ //
+
+ protected void sort_internal(int i, int j) {
+ int pivotIndex = (i+j)/2;
+ sort_swap(pivotIndex, j);
+ int k = sort_partition(i-1, j);
+ sort_swap(k, j);
+ if ((k-i) > 1) sort_internal(i, k-1);
+ if ((j-k) > 1) sort_internal(k+1, j);
+ }
+
+
+ protected int sort_partition(int left, int right) {
+ int pivot = right;
+ do {
+ while (sort_compare(++left, pivot) < 0) { }
+ while ((right != 0) && (sort_compare(--right, pivot) > 0)) { }
+ sort_swap(left, right);
+ } while (left < right);
+ sort_swap(left, right);
+ return left;
+ }
+
+
+ protected void sort_swap(int a, int b) {
+ switch (sort_mode) {
+ case BYTES:
+ byte btemp = sort_bytes[a];
+ sort_bytes[a] = sort_bytes[b];
+ sort_bytes[b] = btemp;
+ break;
+ case CHARS:
+ char ctemp = sort_chars[a];
+ sort_chars[a] = sort_chars[b];
+ sort_chars[b] = ctemp;
+ break;
+ case INTS:
+ int itemp = sort_ints[a];
+ sort_ints[a] = sort_ints[b];
+ sort_ints[b] = itemp;
+ break;
+ case FLOATS:
+ float ftemp = sort_floats[a];
+ sort_floats[a] = sort_floats[b];
+ sort_floats[b] = ftemp;
+ break;
+ case STRINGS:
+ String stemp = sort_strings[a];
+ sort_strings[a] = sort_strings[b];
+ sort_strings[b] = stemp;
+ break;
+ }
+ }
+
+ protected int sort_compare(int a, int b) {
+ switch (sort_mode) {
+ case BYTES:
+ return sort_bytes[a] - sort_bytes[b];
+ case CHARS:
+ return sort_chars[a] - sort_chars[b];
+ case INTS:
+ return sort_ints[a] - sort_ints[b];
+ case FLOATS:
+ // can't just cast to an int because 0.2 and 0.4 would
+ // just appear to be the same thing. no good.
+ if (sort_floats[a] < sort_floats[b]) return -1;
+ return (sort_floats[a] == sort_floats[b]) ? 0 : 1;
+ case STRINGS:
+ return sort_strings[a].compareTo(sort_strings[b]);
+ }
+ return 0;
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // ARRAY UTILITIES
+
+
+ /**
+ * Calls System.arraycopy(), included here so that we can
+ * avoid people needing to learn about the System object
+ * before they can just copy an array.
+ */
+ static public void arraycopy(Object src, int srcPosition,
+ Object dst, int dstPosition,
+ int length) {
+ System.arraycopy(src, srcPosition, dst, dstPosition, length);
+ }
+
+
+ /**
+ * Convenience method for arraycopy().
+ * Identical to
+ * 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;
+ }
+
+ /**
+ * Note that toInt('5') is unlike String in the sense that it
+ * won't return 5, but the ascii value. This is because ((int) someChar)
+ * returns the ascii value, and toInt() is just longhand for the cast.
+ */
+ 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 private boolean int_nf_commas;
+
+
+ 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) &&
+ !int_nf_commas) {
+ return int_nf.format(num);
+ }
+
+ int_nf = NumberFormat.getInstance();
+ int_nf.setGroupingUsed(false); // no commas
+ int_nf_commas = false;
+ int_nf.setMinimumIntegerDigits(digits);
+ int_nf_digits = digits;
+ return int_nf.format(num);
+ }
+
+
+ static public String[] nfc(int num[]) {
+ String formatted[] = new String[num.length];
+ for (int i = 0; i < formatted.length; i++) {
+ formatted[i] = nfc(num[i]);
+ }
+ return formatted;
+ }
+
+
+ static public String nfc(int num) {
+ if ((int_nf != null) &&
+ (int_nf_digits == 0) &&
+ int_nf_commas) {
+ return int_nf.format(num);
+ }
+
+ int_nf = NumberFormat.getInstance();
+ int_nf.setGroupingUsed(true);
+ int_nf_commas = true;
+ int_nf.setMinimumIntegerDigits(0);
+ int_nf_digits = 0;
+ 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 private boolean float_nf_commas;
+
+
+ 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) &&
+ !float_nf_commas) {
+ return float_nf.format(num);
+ }
+
+ float_nf = NumberFormat.getInstance();
+ float_nf.setGroupingUsed(false);
+ float_nf_commas = false;
+
+ 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);
+ }
+
+
+ static public String[] nfc(float num[], int right) {
+ String formatted[] = new String[num.length];
+ for (int i = 0; i < formatted.length; i++) {
+ formatted[i] = nfc(num[i], right);
+ }
+ return formatted;
+ }
+
+
+ static public String nfc(float num, int right) {
+ if ((float_nf != null) &&
+ (float_nf_left == 0) &&
+ (float_nf_right == right) &&
+ float_nf_commas) {
+ return float_nf.format(num);
+ }
+
+ float_nf = NumberFormat.getInstance();
+ float_nf.setGroupingUsed(true);
+ float_nf_commas = true;
+
+ if (right != 0) {
+ float_nf.setMinimumFractionDigits(right);
+ float_nf.setMaximumFractionDigits(right);
+ }
+ float_nf_left = 0;
+ 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 public int unhex(String what) {
+ // has to parse as a Long so that it'll work for numbers bigger than 2^31
+ return (int) (Long.parseLong(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-size. 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 public 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);
+ }
+
+
+ /**
+ * As of 0116 this also takes color(#FF8800, alpha)
+ */
+ public final int color(int gray, int alpha) {
+ if (g == null) {
+ if (alpha > 255) alpha = 255; else if (alpha < 0) alpha = 0;
+ if (gray > 255) {
+ // then assume this is actually a #FF8800
+ return (alpha << 24) | (gray & 0xFFFFFF);
+ } else {
+ //if (gray > 255) gray = 255; else if (gray < 0) gray = 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 to help external communication run as a separate class.
+ *
+ * The options shown here are not yet finalized and will be
+ * changing over the next several releases.
+ *
+ * The simplest way to turn and applet into an application is to
+ * add the following code to your program:
+ *
- * An attempt is made to keep the constants as short/non-verbose
- * as possible. For instance, the constant is TIFF instead of
- * FILE_TYPE_TIFF. We'll do this as long as we can get away with it.
- */
-public interface PConstants {
-
- // renderers known to processing.core
-
- static final String P2D = "processing.core.PGraphics2";
- static final String P3D = "processing.core.PGraphics3";
- static final String JAVA2D = "processing.core.PGraphicsJava";
- static final String OPENGL = "processing.opengl.PGraphicsGL";
- static final String PDF = "processing.pdf.PGraphicsPDF";
- static final String DXF = "processing.dxf.RawDXF";
- //static final String SVG = "processing.dxf.PGraphicsSVG";
-
-
- // platform IDs for PApplet.platform
-
- static final int WINDOWS = 1;
- static final int MACOS9 = 2;
- static final int MACOSX = 3;
- static final int LINUX = 4;
- static final int OTHER = 0;
-
-
- // for better parity between c++ version (at no speed cost)
-
- static final float EPSILON = 0.0001f;
- static final float TWO = 2.0f;
- static final float ONE = 1.0f;
- static final float HALF = 0.5f;
- static final float TFF = 255.0f;
- static final float MAX_FLOAT = Float.MAX_VALUE;
-
-
- // useful goodness
-
- static final float PI = (float) Math.PI;
- static final float HALF_PI = PI / 2.0f;
- static final float THIRD_PI = PI / 3.0f;
- static final float QUARTER_PI = PI / 4.0f;
- static final float TWO_PI = PI * 2.0f;
-
- static final float DEG_TO_RAD = PI/180.0f;
- static final float RAD_TO_DEG = 180.0f/PI;
-
-
- // angle modes
-
- static final int RADIANS = 0;
- static final int DEGREES = 1;
-
-
- // used by split, all the standard whitespace chars
- // (also includes unicode nbsp, that little bostage)
-
- static final String WHITESPACE = " \t\n\r\f\u00A0";
-
-
- // for colors and/or images
-
- static final int RGB = 1; // image & color
- static final int ARGB = 2; // image
- static final int HSB = 3; // color
- static final int ALPHA = 4; // image
-
-
- // image file types
-
- static final int TIFF = 0;
- static final int TARGA = 1;
- static final int JPEG = 2;
- static final int GIF = 3;
-
-
- // filter/convert types
-
- static final int BLUR = 11;
- static final int GRAY = 12;
- static final int INVERT = 13;
- static final int OPAQUE = 14;
- static final int POSTERIZE = 15;
- static final int THRESHOLD = 16;
- static final int ERODE = 17;
- static final int DILATE = 18;
-
- // blend mode keyword definitions
-
- public final static int REPLACE = 0;
- public final static int BLEND = 1 << 0;
- public final static int ADD = 1 << 1;
- public final static int SUBTRACT = 1 << 2;
- public final static int LIGHTEST = 1 << 3;
- public final static int DARKEST = 1 << 4;
-
- // incomplete, slated for beta
- public final static int DIFFERENCE = 1 << 5;
- public final static int MULTIPLY = 1 << 6;
- public final static int SCREEN = 1 << 7;
- public final static int OVERLAY = 1 << 8;
- public final static int HARD_LIGHT = 1 << 9;
- public final static int SOFT_LIGHT = 1 << 10;
-
-
- // colour component bitmasks
-
- public static final int ALPHA_MASK = 0xff000000;
- public static final int RED_MASK = 0x00ff0000;
- public static final int GREEN_MASK = 0x0000ff00;
- public static final int BLUE_MASK = 0x000000ff;
-
-
- // for messages
-
- static final int CHATTER = 0;
- static final int COMPLAINT = 1;
- static final int PROBLEM = 2;
-
-
- // types of projection matrices
-
- static final int CUSTOM = 0; // user-specified fanciness
- static final int ORTHOGRAPHIC = 2; // 2D isometric projection
- static final int PERSPECTIVE = 3; // perspective matrix
-
-
- // rendering settings
-
- static final float PIXEL_CENTER = 0.5f; // for polygon aa
-
-
- // shapes
-
- // the low four bits set the variety,
- // higher bits set the specific shape type
-
- static final int POINTS = (1 << 4) | 0;
-
- static final int LINES = (1 << 5) | 0;
- static final int LINE_STRIP = (1 << 5) | 1;
- static final int LINE_LOOP = (1 << 5) | 2;
-
- static final int TRIANGLES = (1 << 6) | 0;
- static final int TRIANGLE_STRIP = (1 << 6) | 1;
- static final int TRIANGLE_FAN = (1 << 6) | 2;
-
- static final int QUADS = (1 << 7) | 0;
- static final int QUAD_STRIP = (1 << 7) | 1;
-
- static final int POLYGON = (1 << 8) | 0;
- //static final int CONCAVE_POLYGON = (1 << 8) | 1;
- //static final int CONVEX_POLYGON = (1 << 8) | 2;
-
-
- // shape modes
-
- static final int CORNER = 0;
- static final int CORNERS = 1;
- static final int CENTER_RADIUS = 2;
- static final int CENTER = 3; // former CENTER_DIAMETER
-
-
- // uv texture orientation modes
-
- static final int NORMALIZED = 1; //_SPACE = 0; // 0..1
- static final int IMAGE = 2;
-
-
- // text placement modes
-
- /**
- * textMode(MODEL) is the default, meaning that characters
- * will be affected by transformations like any other shapes.
- *
+ * An attempt is made to keep the constants as short/non-verbose
+ * as possible. For instance, the constant is TIFF instead of
+ * FILE_TYPE_TIFF. We'll do this as long as we can get away with it.
+ */
+public interface PConstants {
+
+ // renderers known to processing.core
+
+ static final String P2D = "processing.core.PGraphics2";
+ static final String P3D = "processing.core.PGraphics3";
+ static final String JAVA2D = "processing.core.PGraphicsJava";
+ static final String OPENGL = "processing.opengl.PGraphicsGL";
+ static final String PDF = "processing.pdf.PGraphicsPDF";
+ static final String DXF = "processing.dxf.RawDXF";
+ //static final String SVG = "processing.dxf.PGraphicsSVG";
+
+
+ // platform IDs for PApplet.platform
+
+ static final int WINDOWS = 1;
+ static final int MACOS9 = 2;
+ static final int MACOSX = 3;
+ static final int LINUX = 4;
+ static final int OTHER = 0;
+
+
+ // for better parity between c++ version (at no speed cost)
+
+ static final float EPSILON = 0.0001f;
+ static final float TWO = 2.0f;
+ static final float ONE = 1.0f;
+ static final float HALF = 0.5f;
+ static final float TFF = 255.0f;
+ static final float MAX_FLOAT = Float.MAX_VALUE;
+
+
+ // useful goodness
+
+ static final float PI = (float) Math.PI;
+ static final float HALF_PI = PI / 2.0f;
+ static final float THIRD_PI = PI / 3.0f;
+ static final float QUARTER_PI = PI / 4.0f;
+ static final float TWO_PI = PI * 2.0f;
+
+ static final float DEG_TO_RAD = PI/180.0f;
+ static final float RAD_TO_DEG = 180.0f/PI;
+
+
+ // angle modes
+
+ static final int RADIANS = 0;
+ static final int DEGREES = 1;
+
+
+ // used by split, all the standard whitespace chars
+ // (also includes unicode nbsp, that little bostage)
+
+ static final String WHITESPACE = " \t\n\r\f\u00A0";
+
+
+ // for colors and/or images
+
+ static final int RGB = 1; // image & color
+ static final int ARGB = 2; // image
+ static final int HSB = 3; // color
+ static final int ALPHA = 4; // image
+
+
+ // image file types
+
+ static final int TIFF = 0;
+ static final int TARGA = 1;
+ static final int JPEG = 2;
+ static final int GIF = 3;
+
+
+ // filter/convert types
+
+ static final int BLUR = 11;
+ static final int GRAY = 12;
+ static final int INVERT = 13;
+ static final int OPAQUE = 14;
+ static final int POSTERIZE = 15;
+ static final int THRESHOLD = 16;
+ static final int ERODE = 17;
+ static final int DILATE = 18;
+
+ // blend mode keyword definitions
+
+ public final static int REPLACE = 0;
+ public final static int BLEND = 1 << 0;
+ public final static int ADD = 1 << 1;
+ public final static int SUBTRACT = 1 << 2;
+ public final static int LIGHTEST = 1 << 3;
+ public final static int DARKEST = 1 << 4;
+
+ // incomplete, slated for beta
+ public final static int DIFFERENCE = 1 << 5;
+ public final static int MULTIPLY = 1 << 6;
+ public final static int SCREEN = 1 << 7;
+ public final static int OVERLAY = 1 << 8;
+ public final static int HARD_LIGHT = 1 << 9;
+ public final static int SOFT_LIGHT = 1 << 10;
+
+
+ // colour component bitmasks
+
+ public static final int ALPHA_MASK = 0xff000000;
+ public static final int RED_MASK = 0x00ff0000;
+ public static final int GREEN_MASK = 0x0000ff00;
+ public static final int BLUE_MASK = 0x000000ff;
+
+
+ // for messages
+
+ static final int CHATTER = 0;
+ static final int COMPLAINT = 1;
+ static final int PROBLEM = 2;
+
+
+ // types of projection matrices
+
+ static final int CUSTOM = 0; // user-specified fanciness
+ static final int ORTHOGRAPHIC = 2; // 2D isometric projection
+ static final int PERSPECTIVE = 3; // perspective matrix
+
+
+ // rendering settings
+
+ static final float PIXEL_CENTER = 0.5f; // for polygon aa
+
+
+ // shapes
+
+ // the low four bits set the variety,
+ // higher bits set the specific shape type
+
+ static final int POINTS = (1 << 4) | 0;
+
+ static final int LINES = (1 << 5) | 0;
+ static final int LINE_STRIP = (1 << 5) | 1;
+ static final int LINE_LOOP = (1 << 5) | 2;
+
+ static final int TRIANGLES = (1 << 6) | 0;
+ static final int TRIANGLE_STRIP = (1 << 6) | 1;
+ static final int TRIANGLE_FAN = (1 << 6) | 2;
+
+ static final int QUADS = (1 << 7) | 0;
+ static final int QUAD_STRIP = (1 << 7) | 1;
+
+ static final int POLYGON = (1 << 8) | 0;
+ //static final int CONCAVE_POLYGON = (1 << 8) | 1;
+ //static final int CONVEX_POLYGON = (1 << 8) | 2;
+
+
+ // shape modes
+
+ static final int CORNER = 0;
+ static final int CORNERS = 1;
+ static final int CENTER_RADIUS = 2;
+ static final int CENTER = 3; // former CENTER_DIAMETER
+
+
+ // uv texture orientation modes
+
+ static final int NORMALIZED = 1; //_SPACE = 0; // 0..1
+ static final int IMAGE = 2;
+
+
+ // text placement modes
+
+ /**
+ * textMode(MODEL) is the default, meaning that characters
+ * will be affected by transformations like any other shapes.
+ *
- * Awful (and by that, I mean awesome) ascii (non)art for how this works:
- *
- * This is used by the Create Font tool, or whatever anyone else dreams
- * up for messing with fonts themselves.
- *
- * It is assumed that the calling class will handle closing
- * the stream when finished.
- */
- public void save(OutputStream output) throws IOException {
- DataOutputStream os = new DataOutputStream(output);
-
- os.writeInt(charCount);
-
- if ((name == null) || (psname == null)) {
- name = "";
- psname = "";
- }
- // formerly numBits, now used for version number
- //os.writeInt((name != null) ? 11 : 8);
- os.writeInt(11);
-
- os.writeInt(size); // formerly mboxX (was 64, now 48)
- os.writeInt(mbox2); // formerly mboxY (was 64, still 64)
- os.writeInt(ascent); // formerly baseHt (was ignored)
- os.writeInt(descent); // formerly struct padding for c version
-
- for (int i = 0; i < charCount; i++) {
- os.writeInt(value[i]);
- os.writeInt(height[i]);
- os.writeInt(width[i]);
- os.writeInt(setWidth[i]);
- os.writeInt(topExtent[i]);
- os.writeInt(leftExtent[i]);
- os.writeInt(0); // padding
- }
-
- for (int i = 0; i < charCount; i++) {
- for (int y = 0; y < height[i]; y++) {
- for (int x = 0; x < width[i]; x++) {
- os.write(images[i].pixels[y * mbox2 + x] & 0xff);
- }
- }
- }
-
- //if (name != null) { // version 11
- os.writeUTF(name);
- os.writeUTF(psname);
- os.writeBoolean(smooth);
- //}
-
- os.flush();
- }
-
-
- /**
- * Get index for the char (convert from unicode to bagel charset).
- * @return index into arrays or -1 if not found
- */
- public int index(char c) {
- // degenerate case, but the find function will have trouble
- // if there are somehow zero chars in the lookup
- if (value.length == 0) return -1;
-
- // quicker lookup for the ascii fellers
- if (c < 128) return ascii[c];
-
- // some other unicode char, hunt it out
- return index_hunt(c, 0, value.length-1);
- }
-
-
- protected int index_hunt(int c, int start, int stop) {
- int pivot = (start + stop) / 2;
-
- // if this is the char, then return it
- if (c == value[pivot]) return pivot;
-
- // char doesn't exist, otherwise would have been the pivot
- //if (start == stop) return -1;
- if (start >= stop) return -1;
-
- // if it's in the lower half, continue searching that
- if (c < value[pivot]) return index_hunt(c, start, pivot-1);
-
- // if it's in the upper half, continue there
- return index_hunt(c, pivot+1, stop);
- }
-
-
- /**
- * Currently un-implemented for .vlw fonts,
- * but honored for layout in case subclasses use it.
- */
- public float kern(char a, char b) {
- return 0;
- }
-
-
- /**
- * Returns the ascent of this font from the baseline.
- * The value is based on a font of size 1.
- */
- public float ascent() {
- return ((float)ascent / fheight);
- }
-
-
- /**
- * Returns how far this font descends from the baseline.
- * The value is based on a font size of 1.
- */
- public float descent() {
- return ((float)descent / fheight);
- }
-
-
- /**
- * Width of this character for a font of size 1.
- */
- public float width(char c) {
- if (c == 32) return width('i');
-
- int cc = index(c);
- if (cc == -1) return 0;
-
- return ((float)setWidth[cc] / fwidth);
- }
-
-
- /**
- * Draw a character at an x, y position.
- */
- /*
- public void text(char c, float x, float y, PGraphics parent) {
- text(c, x, y, 0, parent);
- }
- */
-
- /**
- * Draw a character at an x, y, z position.
- */
- /*
- public void text(char c, float x, float y, float z, PGraphics parent) {
- if (parent.textAlign == CENTER) {
- x -= parent.textSize * width(c) / 2f;
-
- } else if (parent.textAlign == RIGHT) {
- x -= parent.textSize * width(c);
- }
-
- //textImpl(c, x, y, z, parent);
- if (z != 0) parent.translate(0, 0, z); // TEMPORARY HACK! SLOW!
- parent.textImpl(c, x, y, z);
- if (z != 0) parent.translate(0, 0, -z); // TEMPORARY HACK! SLOW!
- }
- */
-
-
- /*
- public void text(String str, float x, float y, PGraphics parent) {
- text(str, x, y, 0, parent);
- }
-
-
- public void text(String str, float x, float y, float z, PGraphics parent) {
- if (z != 0) parent.translate(0, 0, z); // TEMPORARY HACK! SLOW!
-
- int length = str.length();
- if (length > textBuffer.length) {
- textBuffer = new char[length + 10];
- }
- str.getChars(0, length, textBuffer, 0);
-
- int start = 0;
- int index = 0;
- while (index < length) {
- if (textBuffer[index] == '\n') {
- textLine(start, index, x, y, z, parent);
- start = index + 1;
- y += parent.textLeading;
- }
- index++;
- }
- if (start < length) {
- textLine(start, index, x, y, z, parent);
- }
- if (z != 0) parent.translate(0, 0, -z); // TEMPORARY HACK! SLOW!
- }
-
-
- protected void textLine(int start, int stop,
- float x, float y, float z,
- PGraphics parent) {
- if (parent.textAlign == CENTER) {
- x -= parent.textSize * calcWidth(textBuffer, start, stop) / 2f;
-
- } else if (parent.textAlign == RIGHT) {
- x -= parent.textSize * calcWidth(textBuffer, start, stop);
- }
-
- for (int index = start; index < stop; index++) {
- //textImpl(textBuffer[index], x, y, z, parent);
- //parent.textImpl(textBuffer[index], x, y, z);
- // HACK FOR Z COORDINATES.. FIX ME SOON
- parent.textImpl(textBuffer[index], x, y, 0); //z);
- x += parent.textSize *width(textBuffer[index]);
- }
- }
- */
-
-
- /**
- * Same as below, just without a z coordinate.
- */
- /*
- public void text(String str, float x, float y,
- float c, float d, PGraphics parent) {
- text(str, x, y, c, d, 0, parent);
- }
- */
-
- /**
- * Draw text in a box that is constrained to a particular size.
- *
- * The parent PApplet will have converted the coords based on
- * the current rectMode().
- *
- * Note that the x,y coords of the start of the box
- * will align with the *ascent* of the text, not the baseline,
- * as is the case for the other text() functions.
- */
- /*
- public void text(String str, float boxX1, float boxY1,
- float boxX2, float boxY2, float boxZ, PGraphics parent) {
- if (boxZ != 0) parent.translate(0, 0, boxZ); // TEMPORARY HACK! SLOW!
-
- float spaceWidth = width(' ') * parent.textSize;
- float runningX = boxX1;
- float currentY = boxY1;
- float boxWidth = boxX2 - boxX1;
-
- float lineX = boxX1;
- if (parent.textAlign == CENTER) {
- lineX = lineX + boxWidth/2f;
- } else if (parent.textAlign == RIGHT) {
- lineX = boxX2;
- }
-
- // ala illustrator, the text itself must fit inside the box
- currentY += ascent() * parent.textSize;
- // if the box is already too small, tell em to f off
- if (currentY > boxY2) return;
-
- int length = str.length();
- if (length > textBuffer.length) {
- textBuffer = new char[length + 10];
- }
- str.getChars(0, length, textBuffer, 0);
-
- int wordStart = 0;
- int wordStop = 0;
- int lineStart = 0;
- int index = 0;
- while (index < length) {
- if ((textBuffer[index] == ' ') || (index == length-1)) {
- // boundary of a word
- float wordWidth = parent.textSize *
- calcWidth(textBuffer, wordStart, index);
-
- if (runningX + wordWidth > boxX2) {
- if (runningX == boxX1) {
- // if this is the first word, and its width is
- // greater than the width of the text box,
- // then break the word where at the max width,
- // and send the rest of the word to the next line.
- do {
- index--;
- if (index == wordStart) {
- // not a single char will fit on this line. screw 'em.
- //System.out.println("screw you");
- return;
- }
- wordWidth = parent.textSize *
- calcWidth(textBuffer, wordStart, index);
- } while (wordWidth > boxWidth);
- textLine(lineStart, index, lineX, currentY, boxZ, parent);
-
- } else {
- // next word is too big, output current line
- // and advance to the next line
- textLine(lineStart, wordStop, lineX, currentY, boxZ, parent);
- // only increment index if a word wasn't broken inside the
- // do/while loop above.. also, this is a while() loop too,
- // because multiple spaces don't count for shit when they're
- // at the end of a line like this.
-
- index = wordStop; // back that ass up
- while ((index < length) &&
- (textBuffer[index] == ' ')) {
- index++;
- }
- }
- lineStart = index;
- wordStart = index;
- wordStop = index;
- runningX = boxX1;
- currentY += parent.textLeading;
- if (currentY > boxY2) return; // box is now full
-
- } else {
- runningX += wordWidth + spaceWidth;
- // on to the next word
- wordStop = index;
- wordStart = index + 1;
- }
-
- } else if (textBuffer[index] == '\n') {
- if (lineStart != index) { // if line is not empty
- textLine(lineStart, index, lineX, currentY, boxZ, parent);
- }
- lineStart = index + 1;
- wordStart = lineStart;
- currentY += parent.textLeading;
- if (currentY > boxY2) return; // box is now full
- }
- index++;
- }
- if ((lineStart < length) && (lineStart != index)) {
- textLine(lineStart, index, lineX, currentY, boxZ, parent);
- }
-
- if (boxZ != 0) parent.translate(0, 0, -boxZ); // TEMPORARY HACK! SLOW!
- }
- */
-
-
- //////////////////////////////////////////////////////////////
-
-
- static final char[] EXTRA_CHARS = {
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
- 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
- 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
- 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
- 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
- 0x00B0, 0x00B1, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00BA,
- 0x00BB, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5,
- 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD,
- 0x00CE, 0x00CF, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6,
- 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DF,
- 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
- 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
- 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8,
- 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FF, 0x0102, 0x0103,
- 0x0104, 0x0105, 0x0106, 0x0107, 0x010C, 0x010D, 0x010E, 0x010F,
- 0x0110, 0x0111, 0x0118, 0x0119, 0x011A, 0x011B, 0x0131, 0x0139,
- 0x013A, 0x013D, 0x013E, 0x0141, 0x0142, 0x0143, 0x0144, 0x0147,
- 0x0148, 0x0150, 0x0151, 0x0152, 0x0153, 0x0154, 0x0155, 0x0158,
- 0x0159, 0x015A, 0x015B, 0x015E, 0x015F, 0x0160, 0x0161, 0x0162,
- 0x0163, 0x0164, 0x0165, 0x016E, 0x016F, 0x0170, 0x0171, 0x0178,
- 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x0192, 0x02C6,
- 0x02C7, 0x02D8, 0x02D9, 0x02DA, 0x02DB, 0x02DC, 0x02DD, 0x03A9,
- 0x03C0, 0x2013, 0x2014, 0x2018, 0x2019, 0x201A, 0x201C, 0x201D,
- 0x201E, 0x2020, 0x2021, 0x2022, 0x2026, 0x2030, 0x2039, 0x203A,
- 0x2044, 0x20AC, 0x2122, 0x2202, 0x2206, 0x220F, 0x2211, 0x221A,
- 0x221E, 0x222B, 0x2248, 0x2260, 0x2264, 0x2265, 0x25CA, 0xF8FF,
- 0xFB01, 0xFB02
- };
-
-
- /**
- * The default Processing character set.
- *
- * This is the union of the Mac Roman and Windows ANSI
- * character sets. ISO Latin 1 would be Unicode characters
- * 0x80 -> 0xFF, but in practice, it would seem that most
- * designers using P5 would rather have the characters
- * that they expect from their platform's fonts.
- *
- * This is more of an interim solution until a much better
- * font solution can be determined. (i.e. create fonts on
- * the fly from some sort of vector format).
- *
- * Not that I expect that to happen.
- */
- static public char[] DEFAULT_CHARSET;
- static {
- DEFAULT_CHARSET = new char[126-33+1 + EXTRA_CHARS.length];
- int index = 0;
- for (int i = 33; i <= 126; i++) {
- DEFAULT_CHARSET[index++] = (char)i;
- }
- for (int i = 0; i < EXTRA_CHARS.length; i++) {
- DEFAULT_CHARSET[index++] = EXTRA_CHARS[i];
- }
- };
-
-
- /**
- * Use reflection to create a new .vlw font on the fly.
- * This only works with Java 1.3 and higher.
- *
- * @param font the font object to create from
- * @param charset array of all unicode chars that should be included
- * @param smooth true to enable smoothing/anti-aliasing
- */
- public PFont(Font font, boolean smooth, char charset[]) {
- if (PApplet.javaVersion < 1.3f) {
- throw new RuntimeException("Can only create fonts with " +
- "Java 1.3 or higher");
- }
-
- // save this so that we can use the native version
- this.font = font;
- this.smooth = smooth;
-
- name = font.getName();
- psname = font.getPSName();
-
- try {
- // the count gets reset later based on how many of
- // the chars are actually found inside the font.
- this.charCount = (charset == null) ? 65536 : charset.length;
- this.size = font.getSize();
-
- fwidth = fheight = size;
-
- PImage bitmaps[] = new PImage[charCount];
-
- // allocate enough space for the character info
- value = new int[charCount];
- height = new int[charCount];
- width = new int[charCount];
- setWidth = new int[charCount];
- topExtent = new int[charCount];
- leftExtent = new int[charCount];
-
- ascii = new int[128];
- for (int i = 0; i < 128; i++) ascii[i] = -1;
-
- int mbox3 = size * 3;
-
- /*
- BufferedImage playground =
- new BufferedImage(mbox3, mbox3, BufferedImage.TYPE_INT_RGB);
-
- Graphics2D g = (Graphics2D) playground.getGraphics();
- g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
- smooth ?
- RenderingHints.VALUE_ANTIALIAS_ON :
- RenderingHints.VALUE_ANTIALIAS_OFF);
- */
-
- Class bufferedImageClass =
- Class.forName("java.awt.image.BufferedImage");
- Constructor bufferedImageConstructor =
- bufferedImageClass.getConstructor(new Class[] {
- Integer.TYPE,
- Integer.TYPE,
- Integer.TYPE });
- Field typeIntRgbField = bufferedImageClass.getField("TYPE_INT_RGB");
- int typeIntRgb = typeIntRgbField.getInt(typeIntRgbField);
- Object playground =
- bufferedImageConstructor.newInstance(new Object[] {
- new Integer(mbox3),
- new Integer(mbox3),
- new Integer(typeIntRgb) });
-
- Class graphicsClass =
- Class.forName("java.awt.Graphics2D");
- Method getGraphicsMethod =
- bufferedImageClass.getMethod("getGraphics", new Class[] { });
- //Object g = getGraphicsMethod.invoke(playground, new Object[] { });
- Graphics g = (Graphics)
- getGraphicsMethod.invoke(playground, new Object[] { });
-
- Class renderingHintsClass =
- Class.forName("java.awt.RenderingHints");
- Class renderingHintsKeyClass =
- Class.forName("java.awt.RenderingHints$Key");
- //PApplet.printarr(renderingHintsClass.getFields());
-
- Field antialiasingKeyField =
- renderingHintsClass.getDeclaredField("KEY_TEXT_ANTIALIASING");
- Object antialiasingKey =
- antialiasingKeyField.get(renderingHintsClass);
-
- Field antialiasField = smooth ?
- renderingHintsClass.getField("VALUE_TEXT_ANTIALIAS_ON") :
- renderingHintsClass.getField("VALUE_TEXT_ANTIALIAS_OFF");
- Object antialiasState =
- antialiasField.get(renderingHintsClass);
-
- Method setRenderingHintMethod =
- graphicsClass.getMethod("setRenderingHint",
- new Class[] { renderingHintsKeyClass,
- Object.class });
- setRenderingHintMethod.invoke(g, new Object[] {
- antialiasingKey,
- antialiasState
- });
-
- g.setFont(font);
- FontMetrics metrics = g.getFontMetrics();
-
- Method canDisplayMethod = null;
- Method getDataMethod = null;
- Method getSamplesMethod = null;
-
- int samples[] = new int[mbox3 * mbox3];
-
- canDisplayMethod =
- Font.class.getMethod("canDisplay", new Class[] { Character.TYPE });
- getDataMethod =
- bufferedImageClass.getMethod("getData", new Class[] { });
- Class rasterClass = Class.forName("java.awt.image.Raster");
- getSamplesMethod = rasterClass.getMethod("getSamples", new Class[] {
- Integer.TYPE,
- Integer.TYPE,
- Integer.TYPE,
- Integer.TYPE,
- Integer.TYPE,
- // integer array type?
- //Array.class
- samples.getClass()
- });
-
- //} catch (Exception e) {
- //e.printStackTrace();
- //return;
- //}
-
- //Array samples = Array.newInstance(Integer.TYPE, mbox3*mbox3);
-
- int maxWidthHeight = 0;
- int index = 0;
- for (int i = 0; i < charCount; i++) {
- char c = (charset == null) ? (char)i : charset[i];
-
- //if (!font.canDisplay(c)) { // skip chars not in the font
- try {
- Character ch = new Character(c);
- Boolean canDisplay = (Boolean)
- canDisplayMethod.invoke(font, new Object[] { ch });
- if (canDisplay.booleanValue() == false) {
- continue;
- }
- } catch (Exception e) {
- e.printStackTrace();
- return;
- }
-
- g.setColor(Color.white);
- g.fillRect(0, 0, mbox3, mbox3);
- g.setColor(Color.black);
- g.drawString(String.valueOf(c), size, size * 2);
-
- // grabs copy of the current data.. so no updates (do each time)
- /*
- Raster raster = playground.getData();
- raster.getSamples(0, 0, mbox3, mbox3, 0, samples);
- */
-
- Object raster = getDataMethod.invoke(playground, new Object[] {});
- getSamplesMethod.invoke(raster, new Object[] {
- new Integer(0),
- new Integer(0),
- new Integer(mbox3),
- new Integer(mbox3),
- new Integer(0),
- samples
- });
-
- //int w = metrics.charWidth(c);
- int minX = 1000, maxX = 0;
- int minY = 1000, maxY = 0;
- boolean pixelFound = false;
-
- for (int y = 0; y < mbox3; y++) {
- for (int x = 0; x < mbox3; x++) {
- //int sample = raster.getSample(x, y, 0); // maybe?
- int sample = samples[y * mbox3 + x] & 0xff;
- // or int samples[] = raster.getPixel(x, y, null);
-
- //if (sample == 0) { // or just not white? hmm
- if (sample != 255) {
- if (x < minX) minX = x;
- if (y < minY) minY = y;
- if (x > maxX) maxX = x;
- if (y > maxY) maxY = y;
- pixelFound = true;
- //System.out.println(x + " " + y + " = " + sample);
- }
- }
- }
-
- if (!pixelFound) {
- //System.out.println("no pixels found in unicode char " + c +
- // "(" + PApplet.hex(c) + ")");
- // this was dumb that it was set to 20 & 30, because for small
- // fonts, those guys don't exist
- minX = minY = 0; //20;
- maxX = maxY = 0; //30;
-
- // this will create a 1 pixel white (clear) character..
- // maybe better to set one to -1 so nothing is added?
- /*
- } else {
- System.out.println(PApplet.hex(c) + " has bounds " +
- minX + ", " + minY + " to " +
- maxX + ", " + maxY);
- */
- }
-
- value[index] = c;
- height[index] = (maxY - minY) + 1;
- width[index] = (maxX - minX) + 1;
- setWidth[index] = metrics.charWidth(c);
- //System.out.println((char)c + " " + setWidth[index]);
-
- // cache locations of the ascii charset
- //if (value[i] < 128) ascii[value[i]] = i;
- if (c < 128) ascii[c] = index;
-
- // offset from vertical location of baseline
- // of where the char was drawn (size*2)
- topExtent[index] = size*2 - minY;
-
- // offset from left of where coord was drawn
- leftExtent[index] = minX - size;
-
- if (c == 'd') {
- ascent = topExtent[index];
- }
- if (c == 'p') {
- descent = -topExtent[index] + height[index];
- }
-
- if (width[index] > maxWidthHeight) maxWidthHeight = width[index];
- if (height[index] > maxWidthHeight) maxWidthHeight = height[index];
-
- bitmaps[index] = new PImage(new int[width[index] * height[index]],
- width[index], height[index], ALPHA);
-
- for (int y = minY; y <= maxY; y++) {
- for (int x = minX; x <= maxX; x++) {
- int val = 255 - (samples[y * mbox3 + x] & 0xff);
- int pindex = (y - minY) * width[index] + (x - minX);
- bitmaps[index].pixels[pindex] = val;
- }
- }
- index++;
- }
- charCount = index;
-
- // foreign font, so just make ascent the max topExtent
- if ((ascent == 0) && (descent == 0)) {
- for (int i = 0; i < charCount; i++) {
- char cc = (char) value[i];
- if (Character.isWhitespace(cc) ||
- (cc == '\u00A0') || (cc == '\u2007') || (cc == '\u202F')) {
- continue;
- }
- if (topExtent[i] > ascent) {
- ascent = topExtent[i];
- }
- int d = -topExtent[i] + height[i];
- if (d > descent) {
- descent = d;
- }
- }
- }
- // size for image/texture is next power of 2 over largest char
- mbox2 = (int)
- Math.pow(2, Math.ceil(Math.log(maxWidthHeight) / Math.log(2)));
- twidth = theight = mbox2;
-
- // shove the smaller PImage data into textures of next-power-of-2 size,
- // so that this font can be used immediately by p5.
- images = new PImage[charCount];
- for (int i = 0; i < charCount; i++) {
- images[i] = new PImage(new int[mbox2*mbox2], mbox2, mbox2, ALPHA);
- for (int y = 0; y < height[i]; y++) {
- System.arraycopy(bitmaps[i].pixels, y*width[i],
- images[i].pixels, y*mbox2,
- width[i]);
- }
- bitmaps[i] = null;
- }
-
- } catch (Exception e) { // catch-all for reflection stuff
- e.printStackTrace();
- throw new RuntimeException(e.getMessage());
- }
- }
-
-
- /**
- * Get a list of the fonts installed on the system that can be used
- * by Java. Not all fonts can be used in Java, in fact it's mostly
- * only TrueType fonts. OpenType fonts with CFF data such as Adobe's
- * OpenType fonts seem to have trouble (even though they're sort of
- * TrueType fonts as well, or may have a .ttf extension). Regular
- * PostScript fonts seem to work O.K. though.
- *
- * Not recommended for use in applets, but this is implemented
- * in PFont because the Java methods to access this information
- * have changed between 1.1 and 1.4, and the 1.4 method is
- * typical of the sort of undergraduate-level over-abstraction
- * that the seems to have made its way into the Java API after 1.1.
- */
- static public String[] list() {
- if (PApplet.javaVersion < 1.3f) {
- // make this reflection too, since compilers complain about the
- // deprecation, and it's bound to stop working in 1.6 or something
- //return Toolkit.getDefaultToolkit().getFontList();
- try {
- Toolkit tk = Toolkit.getDefaultToolkit();
- Method getFontListMethod =
- tk.getClass().getMethod("getFontList", (Class[]) null);
- return (String[]) getFontListMethod.invoke(tk, (Object[]) null);
- } catch (Exception e) {
- e.printStackTrace();
- return new String[] { };
- }
- }
-
- // getFontList is deprecated in 1.4, so this has to be used
- try {
- //GraphicsEnvironment ge =
- // GraphicsEnvironment.getLocalGraphicsEnvironment();
- Class geClass = Class.forName("java.awt.GraphicsEnvironment");
- Method glgeMethod =
- geClass.getMethod("getLocalGraphicsEnvironment", (Class[]) null);
- Object ge = glgeMethod.invoke((Class[]) null, (Object[]) null);
-
- //Font fonts[] = ge.getAllFonts();
- Method gafMethod = geClass.getMethod("getAllFonts", (Class[]) null);
- Font fonts[] = (Font[]) gafMethod.invoke(ge, (Object[]) null);
- String list[] = new String[fonts.length];
- for (int i = 0; i < list.length; i++) {
- list[i] = fonts[i].getName();
- }
- return list;
-
- } catch (Exception e) {
- e.printStackTrace();
- throw new RuntimeException("Error inside PFont.list()");
- }
- }
-}
+/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2004-06 Ben Fry & Casey Reas
+ Portions 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.awt.*;
+import java.io.*;
+import java.lang.reflect.*;
+
+
+/**
+ * Grayscale bitmap font class used by Processing.
+ *
+ * Awful (and by that, I mean awesome) ascii (non)art for how this works:
+ *
+ * This is used by the Create Font tool, or whatever anyone else dreams
+ * up for messing with fonts themselves.
+ *
+ * It is assumed that the calling class will handle closing
+ * the stream when finished.
+ */
+ public void save(OutputStream output) throws IOException {
+ DataOutputStream os = new DataOutputStream(output);
+
+ os.writeInt(charCount);
+
+ if ((name == null) || (psname == null)) {
+ name = "";
+ psname = "";
+ }
+ // formerly numBits, now used for version number
+ //os.writeInt((name != null) ? 11 : 8);
+ os.writeInt(11);
+
+ os.writeInt(size); // formerly mboxX (was 64, now 48)
+ os.writeInt(mbox2); // formerly mboxY (was 64, still 64)
+ os.writeInt(ascent); // formerly baseHt (was ignored)
+ os.writeInt(descent); // formerly struct padding for c version
+
+ for (int i = 0; i < charCount; i++) {
+ os.writeInt(value[i]);
+ os.writeInt(height[i]);
+ os.writeInt(width[i]);
+ os.writeInt(setWidth[i]);
+ os.writeInt(topExtent[i]);
+ os.writeInt(leftExtent[i]);
+ os.writeInt(0); // padding
+ }
+
+ for (int i = 0; i < charCount; i++) {
+ for (int y = 0; y < height[i]; y++) {
+ for (int x = 0; x < width[i]; x++) {
+ os.write(images[i].pixels[y * mbox2 + x] & 0xff);
+ }
+ }
+ }
+
+ //if (name != null) { // version 11
+ os.writeUTF(name);
+ os.writeUTF(psname);
+ os.writeBoolean(smooth);
+ //}
+
+ os.flush();
+ }
+
+
+ /**
+ * Get index for the char (convert from unicode to bagel charset).
+ * @return index into arrays or -1 if not found
+ */
+ public int index(char c) {
+ // degenerate case, but the find function will have trouble
+ // if there are somehow zero chars in the lookup
+ if (value.length == 0) return -1;
+
+ // quicker lookup for the ascii fellers
+ if (c < 128) return ascii[c];
+
+ // some other unicode char, hunt it out
+ return index_hunt(c, 0, value.length-1);
+ }
+
+
+ protected int index_hunt(int c, int start, int stop) {
+ int pivot = (start + stop) / 2;
+
+ // if this is the char, then return it
+ if (c == value[pivot]) return pivot;
+
+ // char doesn't exist, otherwise would have been the pivot
+ //if (start == stop) return -1;
+ if (start >= stop) return -1;
+
+ // if it's in the lower half, continue searching that
+ if (c < value[pivot]) return index_hunt(c, start, pivot-1);
+
+ // if it's in the upper half, continue there
+ return index_hunt(c, pivot+1, stop);
+ }
+
+
+ /**
+ * Currently un-implemented for .vlw fonts,
+ * but honored for layout in case subclasses use it.
+ */
+ public float kern(char a, char b) {
+ return 0;
+ }
+
+
+ /**
+ * Returns the ascent of this font from the baseline.
+ * The value is based on a font of size 1.
+ */
+ public float ascent() {
+ return ((float)ascent / fheight);
+ }
+
+
+ /**
+ * Returns how far this font descends from the baseline.
+ * The value is based on a font size of 1.
+ */
+ public float descent() {
+ return ((float)descent / fheight);
+ }
+
+
+ /**
+ * Width of this character for a font of size 1.
+ */
+ public float width(char c) {
+ if (c == 32) return width('i');
+
+ int cc = index(c);
+ if (cc == -1) return 0;
+
+ return ((float)setWidth[cc] / fwidth);
+ }
+
+
+ /**
+ * Draw a character at an x, y position.
+ */
+ /*
+ public void text(char c, float x, float y, PGraphics parent) {
+ text(c, x, y, 0, parent);
+ }
+ */
+
+ /**
+ * Draw a character at an x, y, z position.
+ */
+ /*
+ public void text(char c, float x, float y, float z, PGraphics parent) {
+ if (parent.textAlign == CENTER) {
+ x -= parent.textSize * width(c) / 2f;
+
+ } else if (parent.textAlign == RIGHT) {
+ x -= parent.textSize * width(c);
+ }
+
+ //textImpl(c, x, y, z, parent);
+ if (z != 0) parent.translate(0, 0, z); // TEMPORARY HACK! SLOW!
+ parent.textImpl(c, x, y, z);
+ if (z != 0) parent.translate(0, 0, -z); // TEMPORARY HACK! SLOW!
+ }
+ */
+
+
+ /*
+ public void text(String str, float x, float y, PGraphics parent) {
+ text(str, x, y, 0, parent);
+ }
+
+
+ public void text(String str, float x, float y, float z, PGraphics parent) {
+ if (z != 0) parent.translate(0, 0, z); // TEMPORARY HACK! SLOW!
+
+ int length = str.length();
+ if (length > textBuffer.length) {
+ textBuffer = new char[length + 10];
+ }
+ str.getChars(0, length, textBuffer, 0);
+
+ int start = 0;
+ int index = 0;
+ while (index < length) {
+ if (textBuffer[index] == '\n') {
+ textLine(start, index, x, y, z, parent);
+ start = index + 1;
+ y += parent.textLeading;
+ }
+ index++;
+ }
+ if (start < length) {
+ textLine(start, index, x, y, z, parent);
+ }
+ if (z != 0) parent.translate(0, 0, -z); // TEMPORARY HACK! SLOW!
+ }
+
+
+ protected void textLine(int start, int stop,
+ float x, float y, float z,
+ PGraphics parent) {
+ if (parent.textAlign == CENTER) {
+ x -= parent.textSize * calcWidth(textBuffer, start, stop) / 2f;
+
+ } else if (parent.textAlign == RIGHT) {
+ x -= parent.textSize * calcWidth(textBuffer, start, stop);
+ }
+
+ for (int index = start; index < stop; index++) {
+ //textImpl(textBuffer[index], x, y, z, parent);
+ //parent.textImpl(textBuffer[index], x, y, z);
+ // HACK FOR Z COORDINATES.. FIX ME SOON
+ parent.textImpl(textBuffer[index], x, y, 0); //z);
+ x += parent.textSize *width(textBuffer[index]);
+ }
+ }
+ */
+
+
+ /**
+ * Same as below, just without a z coordinate.
+ */
+ /*
+ public void text(String str, float x, float y,
+ float c, float d, PGraphics parent) {
+ text(str, x, y, c, d, 0, parent);
+ }
+ */
+
+ /**
+ * Draw text in a box that is constrained to a particular size.
+ *
+ * The parent PApplet will have converted the coords based on
+ * the current rectMode().
+ *
+ * Note that the x,y coords of the start of the box
+ * will align with the *ascent* of the text, not the baseline,
+ * as is the case for the other text() functions.
+ */
+ /*
+ public void text(String str, float boxX1, float boxY1,
+ float boxX2, float boxY2, float boxZ, PGraphics parent) {
+ if (boxZ != 0) parent.translate(0, 0, boxZ); // TEMPORARY HACK! SLOW!
+
+ float spaceWidth = width(' ') * parent.textSize;
+ float runningX = boxX1;
+ float currentY = boxY1;
+ float boxWidth = boxX2 - boxX1;
+
+ float lineX = boxX1;
+ if (parent.textAlign == CENTER) {
+ lineX = lineX + boxWidth/2f;
+ } else if (parent.textAlign == RIGHT) {
+ lineX = boxX2;
+ }
+
+ // ala illustrator, the text itself must fit inside the box
+ currentY += ascent() * parent.textSize;
+ // if the box is already too small, tell em to f off
+ if (currentY > boxY2) return;
+
+ int length = str.length();
+ if (length > textBuffer.length) {
+ textBuffer = new char[length + 10];
+ }
+ str.getChars(0, length, textBuffer, 0);
+
+ int wordStart = 0;
+ int wordStop = 0;
+ int lineStart = 0;
+ int index = 0;
+ while (index < length) {
+ if ((textBuffer[index] == ' ') || (index == length-1)) {
+ // boundary of a word
+ float wordWidth = parent.textSize *
+ calcWidth(textBuffer, wordStart, index);
+
+ if (runningX + wordWidth > boxX2) {
+ if (runningX == boxX1) {
+ // if this is the first word, and its width is
+ // greater than the width of the text box,
+ // then break the word where at the max width,
+ // and send the rest of the word to the next line.
+ do {
+ index--;
+ if (index == wordStart) {
+ // not a single char will fit on this line. screw 'em.
+ //System.out.println("screw you");
+ return;
+ }
+ wordWidth = parent.textSize *
+ calcWidth(textBuffer, wordStart, index);
+ } while (wordWidth > boxWidth);
+ textLine(lineStart, index, lineX, currentY, boxZ, parent);
+
+ } else {
+ // next word is too big, output current line
+ // and advance to the next line
+ textLine(lineStart, wordStop, lineX, currentY, boxZ, parent);
+ // only increment index if a word wasn't broken inside the
+ // do/while loop above.. also, this is a while() loop too,
+ // because multiple spaces don't count for shit when they're
+ // at the end of a line like this.
+
+ index = wordStop; // back that ass up
+ while ((index < length) &&
+ (textBuffer[index] == ' ')) {
+ index++;
+ }
+ }
+ lineStart = index;
+ wordStart = index;
+ wordStop = index;
+ runningX = boxX1;
+ currentY += parent.textLeading;
+ if (currentY > boxY2) return; // box is now full
+
+ } else {
+ runningX += wordWidth + spaceWidth;
+ // on to the next word
+ wordStop = index;
+ wordStart = index + 1;
+ }
+
+ } else if (textBuffer[index] == '\n') {
+ if (lineStart != index) { // if line is not empty
+ textLine(lineStart, index, lineX, currentY, boxZ, parent);
+ }
+ lineStart = index + 1;
+ wordStart = lineStart;
+ currentY += parent.textLeading;
+ if (currentY > boxY2) return; // box is now full
+ }
+ index++;
+ }
+ if ((lineStart < length) && (lineStart != index)) {
+ textLine(lineStart, index, lineX, currentY, boxZ, parent);
+ }
+
+ if (boxZ != 0) parent.translate(0, 0, -boxZ); // TEMPORARY HACK! SLOW!
+ }
+ */
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ static final char[] EXTRA_CHARS = {
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+ 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+ 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+ 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ 0x00B0, 0x00B1, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00BA,
+ 0x00BB, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5,
+ 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD,
+ 0x00CE, 0x00CF, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6,
+ 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+ 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8,
+ 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FF, 0x0102, 0x0103,
+ 0x0104, 0x0105, 0x0106, 0x0107, 0x010C, 0x010D, 0x010E, 0x010F,
+ 0x0110, 0x0111, 0x0118, 0x0119, 0x011A, 0x011B, 0x0131, 0x0139,
+ 0x013A, 0x013D, 0x013E, 0x0141, 0x0142, 0x0143, 0x0144, 0x0147,
+ 0x0148, 0x0150, 0x0151, 0x0152, 0x0153, 0x0154, 0x0155, 0x0158,
+ 0x0159, 0x015A, 0x015B, 0x015E, 0x015F, 0x0160, 0x0161, 0x0162,
+ 0x0163, 0x0164, 0x0165, 0x016E, 0x016F, 0x0170, 0x0171, 0x0178,
+ 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x0192, 0x02C6,
+ 0x02C7, 0x02D8, 0x02D9, 0x02DA, 0x02DB, 0x02DC, 0x02DD, 0x03A9,
+ 0x03C0, 0x2013, 0x2014, 0x2018, 0x2019, 0x201A, 0x201C, 0x201D,
+ 0x201E, 0x2020, 0x2021, 0x2022, 0x2026, 0x2030, 0x2039, 0x203A,
+ 0x2044, 0x20AC, 0x2122, 0x2202, 0x2206, 0x220F, 0x2211, 0x221A,
+ 0x221E, 0x222B, 0x2248, 0x2260, 0x2264, 0x2265, 0x25CA, 0xF8FF,
+ 0xFB01, 0xFB02
+ };
+
+
+ /**
+ * The default Processing character set.
+ *
+ * This is the union of the Mac Roman and Windows ANSI
+ * character sets. ISO Latin 1 would be Unicode characters
+ * 0x80 -> 0xFF, but in practice, it would seem that most
+ * designers using P5 would rather have the characters
+ * that they expect from their platform's fonts.
+ *
+ * This is more of an interim solution until a much better
+ * font solution can be determined. (i.e. create fonts on
+ * the fly from some sort of vector format).
+ *
+ * Not that I expect that to happen.
+ */
+ static public char[] DEFAULT_CHARSET;
+ static {
+ DEFAULT_CHARSET = new char[126-33+1 + EXTRA_CHARS.length];
+ int index = 0;
+ for (int i = 33; i <= 126; i++) {
+ DEFAULT_CHARSET[index++] = (char)i;
+ }
+ for (int i = 0; i < EXTRA_CHARS.length; i++) {
+ DEFAULT_CHARSET[index++] = EXTRA_CHARS[i];
+ }
+ };
+
+
+ /**
+ * Use reflection to create a new .vlw font on the fly.
+ * This only works with Java 1.3 and higher.
+ *
+ * @param font the font object to create from
+ * @param charset array of all unicode chars that should be included
+ * @param smooth true to enable smoothing/anti-aliasing
+ */
+ public PFont(Font font, boolean smooth, char charset[]) {
+ if (PApplet.javaVersion < 1.3f) {
+ throw new RuntimeException("Can only create fonts with " +
+ "Java 1.3 or higher");
+ }
+
+ // save this so that we can use the native version
+ this.font = font;
+ this.smooth = smooth;
+
+ name = font.getName();
+ psname = font.getPSName();
+
+ try {
+ // the count gets reset later based on how many of
+ // the chars are actually found inside the font.
+ this.charCount = (charset == null) ? 65536 : charset.length;
+ this.size = font.getSize();
+
+ fwidth = fheight = size;
+
+ PImage bitmaps[] = new PImage[charCount];
+
+ // allocate enough space for the character info
+ value = new int[charCount];
+ height = new int[charCount];
+ width = new int[charCount];
+ setWidth = new int[charCount];
+ topExtent = new int[charCount];
+ leftExtent = new int[charCount];
+
+ ascii = new int[128];
+ for (int i = 0; i < 128; i++) ascii[i] = -1;
+
+ int mbox3 = size * 3;
+
+ /*
+ BufferedImage playground =
+ new BufferedImage(mbox3, mbox3, BufferedImage.TYPE_INT_RGB);
+
+ Graphics2D g = (Graphics2D) playground.getGraphics();
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ smooth ?
+ RenderingHints.VALUE_ANTIALIAS_ON :
+ RenderingHints.VALUE_ANTIALIAS_OFF);
+ */
+
+ Class bufferedImageClass =
+ Class.forName("java.awt.image.BufferedImage");
+ Constructor bufferedImageConstructor =
+ bufferedImageClass.getConstructor(new Class[] {
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE });
+ Field typeIntRgbField = bufferedImageClass.getField("TYPE_INT_RGB");
+ int typeIntRgb = typeIntRgbField.getInt(typeIntRgbField);
+ Object playground =
+ bufferedImageConstructor.newInstance(new Object[] {
+ new Integer(mbox3),
+ new Integer(mbox3),
+ new Integer(typeIntRgb) });
+
+ Class graphicsClass =
+ Class.forName("java.awt.Graphics2D");
+ Method getGraphicsMethod =
+ bufferedImageClass.getMethod("getGraphics", new Class[] { });
+ //Object g = getGraphicsMethod.invoke(playground, new Object[] { });
+ Graphics g = (Graphics)
+ getGraphicsMethod.invoke(playground, new Object[] { });
+
+ Class renderingHintsClass =
+ Class.forName("java.awt.RenderingHints");
+ Class renderingHintsKeyClass =
+ Class.forName("java.awt.RenderingHints$Key");
+ //PApplet.printarr(renderingHintsClass.getFields());
+
+ Field antialiasingKeyField =
+ renderingHintsClass.getDeclaredField("KEY_TEXT_ANTIALIASING");
+ Object antialiasingKey =
+ antialiasingKeyField.get(renderingHintsClass);
+
+ Field antialiasField = smooth ?
+ renderingHintsClass.getField("VALUE_TEXT_ANTIALIAS_ON") :
+ renderingHintsClass.getField("VALUE_TEXT_ANTIALIAS_OFF");
+ Object antialiasState =
+ antialiasField.get(renderingHintsClass);
+
+ Method setRenderingHintMethod =
+ graphicsClass.getMethod("setRenderingHint",
+ new Class[] { renderingHintsKeyClass,
+ Object.class });
+ setRenderingHintMethod.invoke(g, new Object[] {
+ antialiasingKey,
+ antialiasState
+ });
+
+ g.setFont(font);
+ FontMetrics metrics = g.getFontMetrics();
+
+ Method canDisplayMethod = null;
+ Method getDataMethod = null;
+ Method getSamplesMethod = null;
+
+ int samples[] = new int[mbox3 * mbox3];
+
+ canDisplayMethod =
+ Font.class.getMethod("canDisplay", new Class[] { Character.TYPE });
+ getDataMethod =
+ bufferedImageClass.getMethod("getData", new Class[] { });
+ Class rasterClass = Class.forName("java.awt.image.Raster");
+ getSamplesMethod = rasterClass.getMethod("getSamples", new Class[] {
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ Integer.TYPE,
+ // integer array type?
+ //Array.class
+ samples.getClass()
+ });
+
+ //} catch (Exception e) {
+ //e.printStackTrace();
+ //return;
+ //}
+
+ //Array samples = Array.newInstance(Integer.TYPE, mbox3*mbox3);
+
+ int maxWidthHeight = 0;
+ int index = 0;
+ for (int i = 0; i < charCount; i++) {
+ char c = (charset == null) ? (char)i : charset[i];
+
+ //if (!font.canDisplay(c)) { // skip chars not in the font
+ try {
+ Character ch = new Character(c);
+ Boolean canDisplay = (Boolean)
+ canDisplayMethod.invoke(font, new Object[] { ch });
+ if (canDisplay.booleanValue() == false) {
+ continue;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ return;
+ }
+
+ g.setColor(Color.white);
+ g.fillRect(0, 0, mbox3, mbox3);
+ g.setColor(Color.black);
+ g.drawString(String.valueOf(c), size, size * 2);
+
+ // grabs copy of the current data.. so no updates (do each time)
+ /*
+ Raster raster = playground.getData();
+ raster.getSamples(0, 0, mbox3, mbox3, 0, samples);
+ */
+
+ Object raster = getDataMethod.invoke(playground, new Object[] {});
+ getSamplesMethod.invoke(raster, new Object[] {
+ new Integer(0),
+ new Integer(0),
+ new Integer(mbox3),
+ new Integer(mbox3),
+ new Integer(0),
+ samples
+ });
+
+ //int w = metrics.charWidth(c);
+ int minX = 1000, maxX = 0;
+ int minY = 1000, maxY = 0;
+ boolean pixelFound = false;
+
+ for (int y = 0; y < mbox3; y++) {
+ for (int x = 0; x < mbox3; x++) {
+ //int sample = raster.getSample(x, y, 0); // maybe?
+ int sample = samples[y * mbox3 + x] & 0xff;
+ // or int samples[] = raster.getPixel(x, y, null);
+
+ //if (sample == 0) { // or just not white? hmm
+ if (sample != 255) {
+ if (x < minX) minX = x;
+ if (y < minY) minY = y;
+ if (x > maxX) maxX = x;
+ if (y > maxY) maxY = y;
+ pixelFound = true;
+ //System.out.println(x + " " + y + " = " + sample);
+ }
+ }
+ }
+
+ if (!pixelFound) {
+ //System.out.println("no pixels found in unicode char " + c +
+ // "(" + PApplet.hex(c) + ")");
+ // this was dumb that it was set to 20 & 30, because for small
+ // fonts, those guys don't exist
+ minX = minY = 0; //20;
+ maxX = maxY = 0; //30;
+
+ // this will create a 1 pixel white (clear) character..
+ // maybe better to set one to -1 so nothing is added?
+ /*
+ } else {
+ System.out.println(PApplet.hex(c) + " has bounds " +
+ minX + ", " + minY + " to " +
+ maxX + ", " + maxY);
+ */
+ }
+
+ value[index] = c;
+ height[index] = (maxY - minY) + 1;
+ width[index] = (maxX - minX) + 1;
+ setWidth[index] = metrics.charWidth(c);
+ //System.out.println((char)c + " " + setWidth[index]);
+
+ // cache locations of the ascii charset
+ //if (value[i] < 128) ascii[value[i]] = i;
+ if (c < 128) ascii[c] = index;
+
+ // offset from vertical location of baseline
+ // of where the char was drawn (size*2)
+ topExtent[index] = size*2 - minY;
+
+ // offset from left of where coord was drawn
+ leftExtent[index] = minX - size;
+
+ if (c == 'd') {
+ ascent = topExtent[index];
+ }
+ if (c == 'p') {
+ descent = -topExtent[index] + height[index];
+ }
+
+ if (width[index] > maxWidthHeight) maxWidthHeight = width[index];
+ if (height[index] > maxWidthHeight) maxWidthHeight = height[index];
+
+ bitmaps[index] = new PImage(new int[width[index] * height[index]],
+ width[index], height[index], ALPHA);
+
+ for (int y = minY; y <= maxY; y++) {
+ for (int x = minX; x <= maxX; x++) {
+ int val = 255 - (samples[y * mbox3 + x] & 0xff);
+ int pindex = (y - minY) * width[index] + (x - minX);
+ bitmaps[index].pixels[pindex] = val;
+ }
+ }
+ index++;
+ }
+ charCount = index;
+
+ // foreign font, so just make ascent the max topExtent
+ if ((ascent == 0) && (descent == 0)) {
+ for (int i = 0; i < charCount; i++) {
+ char cc = (char) value[i];
+ if (Character.isWhitespace(cc) ||
+ (cc == '\u00A0') || (cc == '\u2007') || (cc == '\u202F')) {
+ continue;
+ }
+ if (topExtent[i] > ascent) {
+ ascent = topExtent[i];
+ }
+ int d = -topExtent[i] + height[i];
+ if (d > descent) {
+ descent = d;
+ }
+ }
+ }
+ // size for image/texture is next power of 2 over largest char
+ mbox2 = (int)
+ Math.pow(2, Math.ceil(Math.log(maxWidthHeight) / Math.log(2)));
+ twidth = theight = mbox2;
+
+ // shove the smaller PImage data into textures of next-power-of-2 size,
+ // so that this font can be used immediately by p5.
+ images = new PImage[charCount];
+ for (int i = 0; i < charCount; i++) {
+ images[i] = new PImage(new int[mbox2*mbox2], mbox2, mbox2, ALPHA);
+ for (int y = 0; y < height[i]; y++) {
+ System.arraycopy(bitmaps[i].pixels, y*width[i],
+ images[i].pixels, y*mbox2,
+ width[i]);
+ }
+ bitmaps[i] = null;
+ }
+
+ } catch (Exception e) { // catch-all for reflection stuff
+ e.printStackTrace();
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+
+ /**
+ * Get a list of the fonts installed on the system that can be used
+ * by Java. Not all fonts can be used in Java, in fact it's mostly
+ * only TrueType fonts. OpenType fonts with CFF data such as Adobe's
+ * OpenType fonts seem to have trouble (even though they're sort of
+ * TrueType fonts as well, or may have a .ttf extension). Regular
+ * PostScript fonts seem to work O.K. though.
+ *
+ * Not recommended for use in applets, but this is implemented
+ * in PFont because the Java methods to access this information
+ * have changed between 1.1 and 1.4, and the 1.4 method is
+ * typical of the sort of undergraduate-level over-abstraction
+ * that the seems to have made its way into the Java API after 1.1.
+ */
+ static public String[] list() {
+ if (PApplet.javaVersion < 1.3f) {
+ // make this reflection too, since compilers complain about the
+ // deprecation, and it's bound to stop working in 1.6 or something
+ //return Toolkit.getDefaultToolkit().getFontList();
+ try {
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ Method getFontListMethod =
+ tk.getClass().getMethod("getFontList", (Class[]) null);
+ return (String[]) getFontListMethod.invoke(tk, (Object[]) null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return new String[] { };
+ }
+ }
+
+ // getFontList is deprecated in 1.4, so this has to be used
+ try {
+ //GraphicsEnvironment ge =
+ // GraphicsEnvironment.getLocalGraphicsEnvironment();
+ Class geClass = Class.forName("java.awt.GraphicsEnvironment");
+ Method glgeMethod =
+ geClass.getMethod("getLocalGraphicsEnvironment", (Class[]) null);
+ Object ge = glgeMethod.invoke((Class[]) null, (Object[]) null);
+
+ //Font fonts[] = ge.getAllFonts();
+ Method gafMethod = geClass.getMethod("getAllFonts", (Class[]) null);
+ Font fonts[] = (Font[]) gafMethod.invoke(ge, (Object[]) null);
+ String list[] = new String[fonts.length];
+ for (int i = 0; i < list.length; i++) {
+ list[i] = fonts[i].getName();
+ }
+ return list;
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("Error inside PFont.list()");
+ }
+ }
+}
diff --git a/core/PGraphics.java b/core/src/processing/core/PGraphics.java
similarity index 96%
rename from core/PGraphics.java
rename to core/src/processing/core/PGraphics.java
index 6a8d2a615..086e6a94a 100644
--- a/core/PGraphics.java
+++ b/core/src/processing/core/PGraphics.java
@@ -1,3672 +1,3672 @@
-/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
-
-/*
- Part of the Processing project - http://processing.org
-
- Copyright (c) 2004-06 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.awt.*;
-import java.awt.image.*;
-
-
-/**
- * Main graphics and rendering context, as well as
- * the base API implementation for processing "core".
- *
- * As of beta, this class is semi-disabled.
- */
-public class PGraphics extends PImage implements PConstants {
-
- /***
- * Parent applet as passed in by the constructor. If null, then
- * no MemoryImageSource will be used or updated, saving memory.
- */
- PApplet parent;
-
- /// width minus one (useful for many calculations)
- public int width1;
-
- /// height minus one (useful for many calculations)
- public int height1;
-
- /// width * height (useful for many calculations)
- public int pixelCount;
-
- /// true if defaults() has been called a first time
- boolean defaultsInited;
-
- // ........................................................
-
- // specifics for java memoryimagesource
- DirectColorModel cm;
- MemoryImageSource mis;
- public Image image;
-
- // ........................................................
-
- // used by recordRaw()
- public PGraphics raw;
-
- // ........................................................
-
- // needs to happen before background() is called
- // and resize.. so it's gotta be outside
- protected boolean hints[] = new boolean[HINT_COUNT];
-
- // ........................................................
-
- // underscored_names are used for private functions or variables
-
- /** The current colorMode */
- public int colorMode; // = RGB;
-
- /** Max value for red (or hue) set by colorMode */
- public float colorModeX; // = 255;
-
- /** Max value for green (or saturation) set by colorMode */
- public float colorModeY; // = 255;
-
- /** Max value for blue (or value) set by colorMode */
- public float colorModeZ; // = 255;
-
- /** Max value for alpha set by colorMode */
- public float colorModeA; // = 255;
-
- /** True if colors are not in the range 0..1 */
- boolean colorScale; // = true;
-
- /** True if colorMode(RGB, 255) */
- boolean colorRgb255; // = true;
-
- // ........................................................
-
- /**
- * true if tint() is enabled (read-only).
- * Using tint/tintColor seems a better option for naming than
- * tintEnabled/tint because the latter seems ugly, even though
- * g.tint as the actual color seems a little more intuitive,
- * it's just that g.tintEnabled is even more unintuitive.
- * Same goes for fill and stroke et al.
- */
- public boolean tint;
-
- /** tint that was last set (read-only) */
- public int tintColor;
-
- protected boolean tintAlpha;
- protected float tintR, tintG, tintB, tintA;
- protected int tintRi, tintGi, tintBi, tintAi;
-
- // ........................................................
-
- /** true if fill() is enabled, (read-only) */
- public boolean fill;
-
- /** fill that was last set (read-only) */
- public int fillColor;
-
- protected boolean fillAlpha;
- protected float fillR, fillG, fillB, fillA;
- protected int fillRi, fillGi, fillBi, fillAi;
-
- // ........................................................
-
- /** true if stroke() is enabled, (read-only) */
- public boolean stroke;
-
- /** stroke that was last set (read-only) */
- public int strokeColor;
-
- protected boolean strokeAlpha;
- protected float strokeR, strokeG, strokeB, strokeA;
- protected int strokeRi, strokeGi, strokeBi, strokeAi;
-
- // ........................................................
-
- /** Last background color that was set, zero if an image */
- public int backgroundColor;
-
- float backgroundR, backgroundG, backgroundB;
- int backgroundRi, backgroundGi, backgroundBi;
-
- // ........................................................
-
- // internal color for setting/calculating
- protected float calcR, calcG, calcB, calcA;
- int calcRi, calcGi, calcBi, calcAi;
- int calcColor;
- boolean calcAlpha;
-
- /** The last rgb value converted to HSB */
- int cacheHsbKey;
- /** Result of the last conversion to HSB */
- float cacheHsbValue[] = new float[3]; // inits to zero
-
- // ........................................................
-
- /** Last value set by strokeWeight() (read-only) */
- public float strokeWeight;
-
- /**
- * Set by strokeJoin() (read-only). This has a default setting
- * so that strokeJoin() need not be called by defaults,
- * because subclasses may not implement it (i.e. PGraphicsGL)
- */
- public int strokeJoin = MITER;
-
- /**
- * Set by strokeCap() (read-only). This has a default setting
- * so that strokeCap() need not be called by defaults,
- * because subclasses may not implement it (i.e. PGraphicsGL)
- */
- public int strokeCap = ROUND;
-
- // ........................................................
-
- /**
- * Model transformation of the form m[row][column],
- * which is a "column vector" (as opposed to "row vector") matrix.
- */
- public float m00, m01, m02, m03;
- public float m10, m11, m12, m13;
- public float m20, m21, m22, m23;
- public float m30, m31, m32, m33;
-
- static final int MATRIX_STACK_DEPTH = 32;
- float matrixStack[][] = new float[MATRIX_STACK_DEPTH][16];
- int matrixStackDepth;
-
- // ........................................................
-
- Path path;
-
- // ........................................................
-
- /**
- * Type of shape passed to beginShape(),
- * zero if no shape is currently being drawn.
- */
- protected int shape;
-
- // vertices
- static final int DEFAULT_VERTICES = 512;
- protected float vertices[][] =
- new float[DEFAULT_VERTICES][VERTEX_FIELD_COUNT];
- protected int vertexCount; // total number of vertices
-
-
- // ........................................................
-
- protected boolean bezierInited = false;
- public int bezierDetail = 20;
- // msjvm complained when bezier_basis was final
- protected float bezier_basis[][] = {
- { -1, 3, -3, 1},
- { 3, -6, 3, 0},
- { -3, 3, 0, 0},
- { 1, 0, 0, 0}
- };
-
- protected PMatrix bezierBasis =
- new PMatrix(-1, 3, -3, 1,
- 3, -6, 3, 0,
- -3, 3, 0, 0,
- 1, 0, 0, 0);
-
- protected float bezier_forward[][]; // = new float[4][4];
- protected float bezier_draw[][]; // = new float[4][4];
-
- // ........................................................
-
- protected boolean curve_inited = false;
- protected int curveDetail = 20;
- // catmull-rom basis matrix, perhaps with optional s parameter
- public float curveTightness = 0;
- protected float curve_basis[][]; // = new float[4][4];
- protected float curve_forward[][]; // = new float[4][4];
- protected float curve_draw[][];
-
- protected PMatrix bezierBasisInverse;
- protected PMatrix curveToBezierMatrix;
-
- // ........................................................
-
- // spline vertices
-
- static final int DEFAULT_SPLINE_VERTICES = 128;
- protected float splineVertices[][];
- protected int splineVertexCount;
-
- // ........................................................
-
- // precalculate sin/cos lookup tables [toxi]
- // circle resolution is determined from the actual used radii
- // passed to ellipse() method. this will automatically take any
- // scale transformations into account too
-
- // [toxi 031031]
- // changed table's precision to 0.5 degree steps
- // introduced new vars for more flexible code
- static final float sinLUT[];
- static final float cosLUT[];
- static final float SINCOS_PRECISION = 0.5f;
- static final int SINCOS_LENGTH = (int) (360f / SINCOS_PRECISION);
- static {
- sinLUT = new float[SINCOS_LENGTH];
- cosLUT = new float[SINCOS_LENGTH];
- for (int i = 0; i < SINCOS_LENGTH; i++) {
- sinLUT[i] = (float) Math.sin(i * DEG_TO_RAD * SINCOS_PRECISION);
- cosLUT[i] = (float) Math.cos(i * DEG_TO_RAD * SINCOS_PRECISION);
- }
- }
-
- // ........................................................
-
- /** The current rect mode (read-only) */
- public int rectMode;
-
- /** The current ellipse mode (read-only) */
- public int ellipseMode;
-
- /** The current text font (read-only) */
- public PFont textFont;
-
- /** The current font if a Java version of it is installed */
- public Font textFontNative;
-
- /** Metrics for the current native Java font */
- public FontMetrics textFontNativeMetrics;
-
- /** The current text align (read-only) */
- public int textAlign;
-
- /** The current text mode (read-only) */
- public int textMode;
-
- /** The current text size (read-only) */
- public float textSize;
-
- /** The current text leading (read-only) */
- public float textLeading;
-
- /** Last text position, because text often mixed on lines together */
- public float textX, textY, textZ;
-
- /**
- * Internal buffer used by the text() functions
- * because the String object is slow
- */
- protected char textBuffer[] = new char[8 * 1024];
- protected char textWidthBuffer[] = new char[8 * 1024];
-
-
- //////////////////////////////////////////////////////////////
-
- // VARIABLES FOR 3D (used to prevent the need for a subclass)
-
-
- /** The modelview matrix. */
- public PMatrix modelview;
-
- /** Inverse modelview matrix, used for lighting. */
- public PMatrix modelviewInv;
-
- /**
- * The camera matrix, the modelview will be set to this on beginDraw.
- */
- public PMatrix camera;
-
- /** Inverse camera matrix */
- public PMatrix cameraInv;
-
- // ........................................................
-
- // Material properties
-
- public float ambientR, ambientG, ambientB;
- public float specularR, specularG, specularB, specularA;
- public float emissiveR, emissiveG, emissiveB;
- public float shininess;
-
- // ........................................................
-
- /** Camera field of view (in radians, as of rev 86) */
- public float cameraFOV;
-
- /** Position of the camera */
- public float cameraX, cameraY, cameraZ;
-
- public float cameraNear, cameraFar;
- public float cameraAspect;
-
- // projection matrix
- public PMatrix projection; // = new PMatrix();
-
- // ........................................................
-
- /// the stencil buffer
- public int stencil[];
-
- /// depth buffer
- public float zbuffer[];
-
- // ........................................................
-
- /** Maximum lights by default is 8, which is arbitrary,
- but is the minimum defined by OpenGL */
- public static final int MAX_LIGHTS = 8;
-
- public int lightCount = 0;
-
- /** Light types */
- public int lightType[];
-
- /** Light positions */
- public float lightPosition[][];
- //public float lightsX[], lightsY[], lightsZ[];
-
- /** Light direction (normalized vector) */
- public float lightNormal[][];
- //public float lightsNX[], lightsNY[], lightsNZ[];
-
- /** Light falloff */
- public float lightFalloffConstant[];
- public float lightFalloffLinear[];
- public float lightFalloffQuadratic[];
-
- /** Light spot angle */
- public float lightSpotAngle[];
-
- /** Cosine of light spot angle */
- public float lightSpotAngleCos[];
-
- /** Light spot concentration */
- public float lightSpotConcentration[];
-
- /** Diffuse colors for lights.
- * For an ambient light, this will hold the ambient color.
- * Internally these are stored as numbers between 0 and 1. */
- public float lightDiffuse[][];
-
- /** Specular colors for lights.
- Internally these are stored as numbers between 0 and 1. */
- public float lightSpecular[][];
-
- /** Current specular color for lighting */
- public float currentLightSpecular[];
-
- /** Current light falloff */
- public float currentLightFalloffConstant;
- public float currentLightFalloffLinear;
- public float currentLightFalloffQuadratic;
-
- // ........................................................
-
- /**
- * Sets whether texture coordinates passed to
- * vertex() calls will be based on coordinates that are
- * based on the IMAGE or NORMALIZED.
- */
- public int textureMode;
-
- /**
- * Current horizontal coordinate for texture, will always
- * be between 0 and 1, even if using textureMode(IMAGE).
- */
- public float textureU;
-
- /** Current vertical coordinate for texture, see above. */
- public float textureV;
-
- /** Current image being used as a texture */
- public PImage textureImage;
-
- // ........................................................
-
- /**
- * Normals
- */
- public float normalX, normalY, normalZ;
- public int normalMode;
- public int normalCount;
-
- // ........................................................
-
- // [toxi031031] new & faster sphere code w/ support flexibile resolutions
- // will be set by sphereDetail() or 1st call to sphere()
- public int sphereDetail = 0;
-
-
-
- //////////////////////////////////////////////////////////////
-
- // INTERNAL
-
-
- /**
- * Constructor for the PGraphics object.
- * This prototype only exists because of annoying
- * java compilers, and should not be used.
- */
- public PGraphics() { }
-
-
- /**
- * Constructor for the PGraphics object. Use this to ensure that
- * the defaults get set properly. In a subclass, use this(w, h)
- * as the first line of a subclass' constructor to properly set
- * the internal fields and defaults.
- *
- * @param iwidth viewport width
- * @param iheight viewport height
- */
- public PGraphics(int iwidth, int iheight) {
- this(iwidth, iheight, null);
- //resize(iwidth, iheight);
- }
-
-
- /**
- * Constructor for the PGraphics object. Use this to ensure that
- * the defaults get set properly. In a subclass, use this(w, h)
- * as the first line of a subclass' constructor to properly set
- * the internal fields and defaults.
- *
- * @param iwidth viewport width
- * @param iheight viewport height
- */
- public PGraphics(int iwidth, int iheight, PApplet applet) {
- if (applet != null) {
- this.parent = applet;
- applet.addListeners();
- }
- resize(iwidth, iheight);
- }
-
-
- /**
- * Called in repsonse to a resize event, handles setting the
- * new width and height internally, as well as re-allocating
- * the pixel buffer for the new size.
- *
- * Note that this will nuke any cameraMode() settings.
- */
- public void resize(int iwidth, int iheight) { // ignore
- //System.out.println("resize " + iwidth + " " + iheight);
-
- width = iwidth;
- height = iheight;
- width1 = width - 1;
- height1 = height - 1;
-
- allocate();
-
- // clear the screen with the old background color
- //background(backgroundColor);
- }
-
-
- /**
- * Parent thread has requested that visual action be taken.
- */
- public void requestDisplay(PApplet parent) { // ignore
- parent.handleDisplay();
- }
-
-
- // broken out because of subclassing
- protected void allocate() {
- pixelCount = width * height;
- pixels = new int[pixelCount];
-
- // because of a java 1.1 bug, pixels must be registered as
- // opaque before their first run, the memimgsrc will flicker
- // and run very slowly.
- backgroundColor |= 0xff000000; // just for good measure
- for (int i = 0; i < pixelCount; i++) pixels[i] = backgroundColor;
- //for (int i = 0; i < pixelCount; i++) pixels[i] = 0xffffffff;
-
- if (parent != null) {
- cm = new DirectColorModel(32, 0x00ff0000, 0x0000ff00, 0x000000ff);;
- mis = new MemoryImageSource(width, height, pixels, 0, width);
- mis.setFullBufferUpdates(true);
- mis.setAnimated(true);
- image = Toolkit.getDefaultToolkit().createImage(mis);
- }
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // FRAME
-
-
- /**
- * Former function, now called beginDraw.
- * @deprecated
- */
- /*
- public void beginFrame() { // ignore
- System.err.println("beginFrame() is now beginDraw(), please use that instead");
- beginDraw();
- }
- */
-
-
- /**
- * Former function, now called endDraw.
- * @deprecated
- */
- /*
- public void endFrame() { // ignore
- System.err.println("endFrame() is now endDraw(), please use that instead");
- endDraw();
- }
- */
-
-
- /**
- * Prepares the PGraphics for drawing.
- *
- * For the most part, hints are temporary api quirks,
- * for which a proper api hasn't been properly worked out.
- * for instance SMOOTH_IMAGES existed because smooth()
- * wasn't yet implemented, but it will soon go away.
- *
- * They also exist for obscure features in the graphics
- * engine, like enabling/disabling single pixel lines
- * that ignore the zbuffer, the way they do in alphabot.
- *
- * Current hint options:
- *
- * Differences between beginShape() and line() and point() methods.
- *
- * beginShape() is intended to be more flexible at the expense of being
- * a little more complicated to use. it handles more complicated shapes
- * that can consist of many connected lines (so you get joins) or lines
- * mixed with curves.
- *
- * The line() and point() command are for the far more common cases
- * (particularly for our audience) that simply need to draw a line
- * or a point on the screen.
- *
- * From the code side of things, line() may or may not call beginShape()
- * to do the drawing. In the beta code, they do, but in the alpha code,
- * they did not. they might be implemented one way or the other depending
- * on tradeoffs of runtime efficiency vs. implementation efficiency &mdash
- * meaning the speed that things run at vs. the speed it takes me to write
- * the code and maintain it. for beta, the latter is most important so
- * that's how things are implemented.
- */
- public void beginShape(int kind) {
- shape = kind;
-
- // reset vertex, line and triangle information
- // every shape is rendered at endShape();
- vertexCount = 0;
-
- splineVertexCount = 0;
- //spline_vertices_flat = true;
-
- //strokeChanged = false;
- //fillChanged = false;
- //normalChanged = false;
- }
-
-
- public void normal(float nx, float ny, float nz) {
- depthError("normal");
- }
-
- public void textureMode(int mode) {
- depthError("textureMode");
- }
-
- public void texture(PImage image) {
- depthError("texture");
- }
-
-
- public void vertex(float x, float y) {
- splineVertexCount = 0;
- //float vertex[];
-
- if (vertexCount == vertices.length) {
- float temp[][] = new float[vertexCount<<1][VERTEX_FIELD_COUNT];
- System.arraycopy(vertices, 0, temp, 0, vertexCount);
- vertices = temp;
- //message(CHATTER, "allocating more vertices " + vertices.length);
- }
- // not everyone needs this, but just easier to store rather
- // than adding another moving part to the code...
- vertices[vertexCount][MX] = x;
- vertices[vertexCount][MY] = y;
- vertexCount++;
-
- switch (shape) {
-
- case POINTS:
- point(x, y);
- break;
-
- case LINES:
- if ((vertexCount % 2) == 0) {
- line(vertices[vertexCount-2][MX],
- vertices[vertexCount-2][MY], x, y);
- }
- break;
-
- case LINE_STRIP:
- case LINE_LOOP:
- if (vertexCount == 1) {
- path = new Path();
- path.moveTo(x, y);
- } else {
- path.lineTo(x, y);
- }
- break;
-
- case TRIANGLES:
- if ((vertexCount % 3) == 0) {
- triangle(vertices[vertexCount - 3][MX],
- vertices[vertexCount - 3][MY],
- vertices[vertexCount - 2][MX],
- vertices[vertexCount - 2][MY],
- x, y);
- }
- break;
-
- case TRIANGLE_STRIP:
- if (vertexCount == 3) {
- triangle(vertices[0][MX], vertices[0][MY],
- vertices[1][MX], vertices[1][MY],
- x, y);
- } else if (vertexCount > 3) {
- path = new Path();
- // when vertexCount == 4, draw an un-closed triangle
- // for indices 2, 3, 1
- path.moveTo(vertices[vertexCount - 2][MX],
- vertices[vertexCount - 2][MY]);
- path.lineTo(vertices[vertexCount - 1][MX],
- vertices[vertexCount - 1][MY]);
- path.lineTo(vertices[vertexCount - 3][MX],
- vertices[vertexCount - 3][MY]);
- draw_shape(path);
- }
- break;
-
- case TRIANGLE_FAN:
- if (vertexCount == 3) {
- triangle(vertices[0][MX], vertices[0][MY],
- vertices[1][MX], vertices[1][MY],
- x, y);
- } else if (vertexCount > 3) {
- path = new Path();
- // when vertexCount > 3, draw an un-closed triangle
- // for indices 0 (center), previous, current
- path.moveTo(vertices[0][MX],
- vertices[0][MY]);
- path.lineTo(vertices[vertexCount - 2][MX],
- vertices[vertexCount - 2][MY]);
- path.lineTo(x, y);
- draw_shape(path);
- }
- break;
-
- case QUADS:
- if ((vertexCount % 4) == 0) {
- quad(vertices[vertexCount - 4][MX],
- vertices[vertexCount - 4][MY],
- vertices[vertexCount - 3][MX],
- vertices[vertexCount - 3][MY],
- vertices[vertexCount - 2][MX],
- vertices[vertexCount - 2][MY],
- x, y);
- }
- break;
-
- case QUAD_STRIP:
- // 0---2---4
- // | | |
- // 1---3---5
- if (vertexCount == 4) {
- // note difference in winding order:
- quad(vertices[0][MX], vertices[0][MY],
- vertices[2][MX], vertices[2][MY],
- x, y,
- vertices[1][MX], vertices[1][MY]);
-
- } else if (vertexCount > 4) {
- path = new Path();
- // when vertexCount == 5, draw an un-closed triangle
- // for indices 2, 4, 5, 3
- path.moveTo(vertices[vertexCount - 3][MX],
- vertices[vertexCount - 3][MY]);
- path.lineTo(vertices[vertexCount - 1][MX],
- vertices[vertexCount - 1][MY]);
- path.lineTo(x, y);
- path.lineTo(vertices[vertexCount - 2][MX],
- vertices[vertexCount - 2][MY]);
- draw_shape(path);
- }
- break;
-
- case POLYGON:
- //case CONCAVE_POLYGON:
- //case CONVEX_POLYGON:
- if (vertexCount == 1) {
- path = new Path();
- path.moveTo(x, y);
- } else {
- path.lineTo(x, y);
- }
- break;
- }
- }
-
-
- public void vertex(float x, float y, float z) {
- depthErrorXYZ("vertex");
- }
-
-
- public void vertex(float x, float y, float u, float v) {
- throw new RuntimeException("vertex() with u, v coordinates " +
- "can only be used with OPENGL or P3D");
- }
-
-
- public void vertex(float x, float y, float z, float u, float v) {
- throw new RuntimeException("vertex() with u, v coordinates " +
- "can only be used with OPENGL or P3D");
- }
-
-
- public void bezierVertex(float x1, float y1,
- float x2, float y2,
- float x3, float y3) {
- // if there hasn't yet been a call to vertex(), throw an error
-
- // otherwise, draw a bezier segment to this point
- }
-
-
- protected void bezier_vertex(float x, float y) {
- vertexCount = 0;
-
- if (splineVertices == null) {
- splineVertices = new float[DEFAULT_SPLINE_VERTICES][VERTEX_FIELD_COUNT];
- }
-
- // if more than 128 points, shift everything back to the beginning
- if (splineVertexCount == DEFAULT_SPLINE_VERTICES) {
- System.arraycopy(splineVertices[DEFAULT_SPLINE_VERTICES - 3], 0,
- splineVertices[0], 0, VERTEX_FIELD_COUNT);
- System.arraycopy(splineVertices[DEFAULT_SPLINE_VERTICES - 2], 0,
- splineVertices[1], 0, VERTEX_FIELD_COUNT);
- splineVertexCount = 3;
- }
- splineVertices[splineVertexCount][MX] = x;
- splineVertices[splineVertexCount][MY] = y;
- splineVertexCount++;
-
- switch (shape) {
- case LINE_LOOP:
- case LINE_STRIP:
- case POLYGON:
- if (splineVertexCount == 1) {
- path.moveTo(x, y);
-
- } else if (splineVertexCount >= 4) {
- path.curveTo(splineVertices[splineVertexCount-3][MX],
- splineVertices[splineVertexCount-3][MY],
- splineVertices[splineVertexCount-2][MX],
- splineVertices[splineVertexCount-2][MY],
- x, y);
- }
- break;
-
- default:
- throw new RuntimeException("bezierVertex() can only be used with " +
- "LINE_LOOP and POLYGON shapes");
- }
- }
-
-
- public void bezierVertex(float x1, float y1, float z1,
- float x2, float y2, float z2,
- float x3, float y3, float z3) {
- depthErrorXYZ("bezierVertex");
- }
-
-
- /**
- * See notes with the curve() function.
- */
- public void curveVertex(float x, float y) {
- //throw new RuntimeException("curveVertex() temporarily disabled");
- // TODO get matrix setup happening
- }
-
-
- /**
- * See notes with the curve() function.
- */
- public void curveVertex(float x, float y, float z) {
- depthErrorXYZ("curveVertex");
- }
-
-
- public void endShape() {
- shape = 0;
-
- switch (shape) {
- case LINE_STRIP:
- stroke_shape(path);
- break;
-
- case LINE_LOOP:
- path.closePath();
- stroke_shape(path);
- break;
-
- case POLYGON:
- path.closePath();
- draw_shape(path);
- break;
- }
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // COMPOUND PATHS
-
-
- /**
- * Begin a new path. This can be used after beginShape() to draw
- * a compound path (i.e. to draw shape with a hole on the interior)
- * For instance, to draw a shape that has a hole in its interior,
- * the format would be:
- *
- * For instance, to convert the following example:
- * Identical to typing:
- *
- * (This function is not optimized, since it's not expected to
- * be called all that often. there are many juicy and obvious
- * opimizations in here, but it's probably better to keep the
- * code more readable)
- */
- protected void curve_mode(int segments, float s) {
- curveDetail = segments;
-
- if (curve_basis == null) {
- // allocate these when used, to save startup time
- curve_basis = new float[4][4];
- curve_forward = new float[4][4];
- curve_draw = new float[4][4];
- curve_inited = true;
- }
-
- float c[][] = curve_basis;
-
- c[0][0] = s-1; c[0][1] = s+3; c[0][2] = -3-s; c[0][3] = 1-s;
- c[1][0] = 2*(1-s); c[1][1] = -5-s; c[1][2] = 2*(s+2); c[1][3] = s-1;
- c[2][0] = s-1; c[2][1] = 0; c[2][2] = 1-s; c[2][3] = 0;
- c[3][0] = 0; c[3][1] = 2; c[3][2] = 0; c[3][3] = 0;
-
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- c[i][j] /= 2f;
- }
- }
- setup_spline_forward(segments, curve_forward);
-
- if (bezierBasisInverse == null) {
- bezierBasisInverse = new PMatrix(bezierBasis).invert();
- }
-
- // hack here to get PGraphics2 working
- curveToBezierMatrix = new PMatrix(c[0][0], c[0][1], c[0][2], c[0][3],
- c[1][0], c[1][1], c[1][2], c[1][3],
- c[2][0], c[2][1], c[2][2], c[2][3],
- c[3][0], c[3][1], c[3][2], c[3][3]);
- curveToBezierMatrix.preApply(bezierBasisInverse);
-
- // multiply the basis and forward diff matrices together
- // saves much time since this needn't be done for each curve
- mult_spline_matrix(curve_forward, curve_basis, curve_draw, 4);
- }
-
-
- /**
- * Draws a segment of Catmull-Rom curve.
- *
- * As of 0070, this function no longer doubles the first and
- * last points. The curves are a bit more boring, but it's more
- * mathematically correct, and properly mirrored in curvePoint().
- *
- * Identical to typing out:
- * the x0, y0, z0 points are the point that's being used as
- * the start, and also as the accumulator. for bezier curves,
- * the x1, y1, z1 are the first point drawn, and added to.
- * for catmull-rom curves, the first control point (x2, y2, z2)
- * is the first drawn point, and is accumulated to.
- */
- protected void spline2_segment(int offset, int start,
- float m[][], int segments) {
- float x1 = splineVertices[offset][MX];
- float y1 = splineVertices[offset][MY];
-
- float x2 = splineVertices[offset+1][MX];
- float y2 = splineVertices[offset+1][MY];
-
- float x3 = splineVertices[offset+2][MX];
- float y3 = splineVertices[offset+2][MY];
-
- float x4 = splineVertices[offset+3][MX];
- float y4 = splineVertices[offset+3][MY];
-
- float x0 = splineVertices[start][MX];
- float y0 = splineVertices[start][MY];
-
- float xplot1 = m[1][0]*x1 + m[1][1]*x2 + m[1][2]*x3 + m[1][3]*x4;
- float xplot2 = m[2][0]*x1 + m[2][1]*x2 + m[2][2]*x3 + m[2][3]*x4;
- float xplot3 = m[3][0]*x1 + m[3][1]*x2 + m[3][2]*x3 + m[3][3]*x4;
-
- float yplot1 = m[1][0]*y1 + m[1][1]*y2 + m[1][2]*y3 + m[1][3]*y4;
- float yplot2 = m[2][0]*y1 + m[2][1]*y2 + m[2][2]*y3 + m[2][3]*y4;
- float yplot3 = m[3][0]*y1 + m[3][1]*y2 + m[3][2]*y3 + m[3][3]*y4;
-
- // vertex() will reset splineVertexCount, so save it
- int splineVertexSaved = splineVertexCount;
- vertex(x0, y0);
- for (int j = 0; j < segments; j++) {
- x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
- y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
- vertex(x0, y0);
- }
- splineVertexCount = splineVertexSaved;
- }
-
-
- protected void spline3_segment(int offset, int start,
- float m[][], int segments) {
- float x1 = splineVertices[offset+0][MX];
- float y1 = splineVertices[offset+0][MY];
- float z1 = splineVertices[offset+0][MZ];
-
- float x2 = splineVertices[offset+1][MX];
- float y2 = splineVertices[offset+1][MY];
- float z2 = splineVertices[offset+1][MZ];
-
- float x3 = splineVertices[offset+2][MX];
- float y3 = splineVertices[offset+2][MY];
- float z3 = splineVertices[offset+2][MZ];
-
- float x4 = splineVertices[offset+3][MX];
- float y4 = splineVertices[offset+3][MY];
- float z4 = splineVertices[offset+3][MZ];
-
- float x0 = splineVertices[start][MX];
- float y0 = splineVertices[start][MY];
- float z0 = splineVertices[start][MZ];
-
- float xplot1 = m[1][0]*x1 + m[1][1]*x2 + m[1][2]*x3 + m[1][3]*x4;
- float xplot2 = m[2][0]*x1 + m[2][1]*x2 + m[2][2]*x3 + m[2][3]*x4;
- float xplot3 = m[3][0]*x1 + m[3][1]*x2 + m[3][2]*x3 + m[3][3]*x4;
-
- float yplot1 = m[1][0]*y1 + m[1][1]*y2 + m[1][2]*y3 + m[1][3]*y4;
- float yplot2 = m[2][0]*y1 + m[2][1]*y2 + m[2][2]*y3 + m[2][3]*y4;
- float yplot3 = m[3][0]*y1 + m[3][1]*y2 + m[3][2]*y3 + m[3][3]*y4;
-
- float zplot1 = m[1][0]*z1 + m[1][1]*z2 + m[1][2]*z3 + m[1][3]*z4;
- float zplot2 = m[2][0]*z1 + m[2][1]*z2 + m[2][2]*z3 + m[2][3]*z4;
- float zplot3 = m[3][0]*z1 + m[3][1]*z2 + m[3][2]*z3 + m[3][3]*z4;
-
- // vertex() will reset splineVertexCount, so save it
- int cvertexSaved = splineVertexCount;
- vertex(x0, y0, z0);
- for (int j = 0; j < segments; j++) {
- x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
- y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
- z0 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
- vertex(x0, y0, z0);
- }
- splineVertexCount = cvertexSaved;
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // IMAGE
-
-
- public void image(PImage image, float x, float y) {
- imageImpl(image,
- x, y, x+image.width, y+image.height,
- 0, 0, image.width, image.height);
- }
-
-
- public void image(PImage image,
- float x, float y, float c, float d) {
- image(image, x, y, c, d, 0, 0, image.width, image.height);
- }
-
-
- /**
- * u, v coordinates are always based on image space location,
- * regardless of the current textureMode().
- */
- public void image(PImage image,
- float a, float b, float c, float d,
- int u1, int v1, int u2, int v2) {
- if (imageMode == CORNER) {
- if (c < 0) { // reset a negative width
- a += c; c = -c;
- }
- if (d < 0) { // reset a negative height
- b += d; d = -d;
- }
-
- imageImpl(image,
- a, b, a + c, b + d,
- u1, v1, u2, v2);
-
- } else if (imageMode == CORNERS) {
- if (c < a) { // reverse because x2 < x1
- float temp = a; a = c; c = temp;
- }
- if (d < b) { // reverse because y2 < y1
- float temp = b; b = d; d = temp;
- }
-
- imageImpl(image,
- a, b, c, d,
- u1, v1, u2, v2);
- }
- }
-
-
- /**
- * Expects x1, y1, x2, y2 coordinates where (x2 >= x1) and (y2 >= y1).
- * If tint() has been called, the image will be colored.
- */
- protected void imageImpl(PImage image,
- float x1, float y1, float x2, float y2,
- int u1, int v1, int u2, int v2) {
- // TODO blit an image to the screen
- System.err.println("unimplemented imageImpl() in PGraphics");
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // TEXT/FONTS
-
-
- /**
- * Sets the alignment of the text to one of LEFT, CENTER, or RIGHT.
- */
- public void textAlign(int align) {
- textAlign = align;
- }
-
-
- /**
- * Returns the ascent of the current font at the current size.
- * This is a method, rather than a variable inside the PGraphics object
- * because it requires calculation.
- */
- public float textAscent() {
- if (textFont == null) {
- throw new RuntimeException("use textFont() before textAscent()");
- }
-
- return textFont.ascent() *
- ((textMode == SCREEN) ? textFont.size : textSize);
- }
-
-
- /**
- * Returns the descent of the current font at the current size.
- * This is a method, rather than a variable inside the PGraphics object
- * because it requires calculation.
- */
- public float textDescent() {
- if (textFont != null) {
- return textFont.descent() *
- ((textMode == SCREEN) ? textFont.size : textSize);
-
- } else {
- throw new RuntimeException("use textFont() before textDescent()");
- }
- }
-
-
- /**
- * Sets the current font. The font's size will be the "natural"
- * size of this font (the size that was set when using "Create Font").
- * The leading will also be reset.
- */
- public void textFont(PFont which) {
- if (which != null) {
- textFont = which;
- textFontNative = which.font;
- //textFontNativeMetrics = null;
- // changed for rev 0104 for textMode(SHAPE) in opengl
- if (textFontNative != null) {
- textFontNativeMetrics =
- Toolkit.getDefaultToolkit().getFontMetrics(textFontNative);
- }
- textSize(which.size);
-
- } else {
- throw new RuntimeException("a null PFont was passed to textFont()");
- }
- }
-
-
- /**
- * Useful function to set the font and size at the same time.
- */
- public void textFont(PFont which, float size) {
- textFont(which);
- textSize(size);
- }
-
-
- /**
- * Set the text leading to a specific value. If using a custom
- * value for the text leading, you'll have to call textLeading()
- * again after any calls to textSize().
- */
- public void textLeading(float leading) {
- textLeading = leading;
- }
-
-
- /**
- * Sets the text rendering/placement to be either SCREEN (direct
- * to the screen, exact coordinates, only use the font's original size)
- * or MODEL (the default, where text is manipulated by translate() and
- * can have a textSize). The text size cannot be set when using
- * textMode(SCREEN), because it uses the pixels directly from the font.
- */
- public void textMode(int mode) {
- // CENTER and MODEL overlap (they're both 3)
- if ((mode == LEFT) || (mode == RIGHT)) {
- throw new RuntimeException("textMode() is now textAlign() " +
- "in Processing beta");
- }
- if ((mode != SCREEN) && (mode != MODEL)) {
- throw new RuntimeException("Only textMode(SCREEN) or textMode(MODEL) " +
- "are available with this renderer.");
- }
-
- //if (textFont != null) {
- textMode = mode;
-
- // reset the font to its natural size
- // (helps with width calculations and all that)
- //if (textMode == SCREEN) {
- //textSize(textFont.size);
- //}
-
- //} else {
- //throw new RuntimeException("use textFont() before textMode()");
- //}
- }
-
-
- /**
- * Sets the text size, also resets the value for the leading.
- */
- public void textSize(float size) {
- if (textFont != null) {
- if ((textMode == SCREEN) && (size != textFont.size)) {
- throw new RuntimeException("textSize() cannot be used with " +
- "textMode(SCREEN)");
- }
- textSize = size;
- //textLeading = textSize *
- // ((textFont.ascent() + textFont.descent()) * 1.275f);
- textLeading = (textAscent() + textDescent()) * 1.275f;
-
- } else {
- throw new RuntimeException("Use textFont() before textSize()");
- }
- }
-
-
- // ........................................................
-
-
- public float textWidth(char c) {
- textBuffer[0] = c;
- return textWidthImpl(textBuffer, 0, 1);
- }
-
-
- /**
- * Return the width of a line of text. If the text has multiple
- * lines, this returns the length of the longest line.
- */
- public float textWidth(String str) {
- if (textFont == null) {
- throw new RuntimeException("use textFont() before textWidth()");
- }
-
- int length = str.length();
- if (length > textWidthBuffer.length) {
- textWidthBuffer = new char[length + 10];
- }
- str.getChars(0, length, textWidthBuffer, 0);
-
- float wide = 0;
- int index = 0;
- int start = 0;
-
- while (index < length) {
- if (textWidthBuffer[index] == '\n') {
- wide = Math.max(wide, textWidthImpl(textWidthBuffer, start, index));
- start = index+1;
- }
- index++;
- }
- if (start < length) {
- wide = Math.max(wide, textWidthImpl(textWidthBuffer, start, index));
- }
- return wide;
- }
-
-
- /**
- * Implementation of returning the text width of
- * the chars [start, stop) in the buffer.
- * Unlike the previous version that was inside PFont, this will
- * return the size not of a 1 pixel font, but the actual current size.
- */
- protected float textWidthImpl(char buffer[], int start, int stop) {
- float wide = 0;
- for (int i = start; i < stop; i++) {
- // could add kerning here, but it just ain't implemented
- wide += textFont.width(buffer[i]) * textSize;
- }
- return wide;
- }
-
-
- // ........................................................
-
-
- /**
- * Write text where we just left off.
- */
- public void text(char c) {
- text(c, textX, textY, textZ);
- }
-
-
- /**
- * Draw a single character on screen.
- * Extremely slow when used with textMode(SCREEN) and Java 2D,
- * because beginPixels has to be called first and updatePixels last.
- */
- public void text(char c, float x, float y) {
- if (textFont == null) {
- throw new RuntimeException("use textFont() before text()");
- }
-
- if (textMode == SCREEN) beginPixels();
-
- textBuffer[0] = c;
- textLineImpl(textBuffer, 0, 1, x, y);
-
- if (textMode == SCREEN) endPixels();
- }
-
-
- /**
- * Draw a single character on screen (with a z coordinate)
- */
- public void text(char c, float x, float y, float z) {
- if ((z != 0) && (textMode == SCREEN)) {
- String msg = "textMode(SCREEN) cannot have a z coordinate";
- throw new RuntimeException(msg);
- }
-
- if (z != 0) translate(0, 0, z); // slowness, badness
-
- text(c, x, y);
- textZ = z;
-
- if (z != 0) translate(0, 0, -z);
- }
-
-
- /**
- * Write text where we just left off.
- */
- public void text(String str) {
- text(str, textX, textY, textZ);
- }
-
-
- /**
- * Draw a chunk of text.
- * Newlines that are \n (Unix newline or linefeed char, ascii 10)
- * are honored, but \r (carriage return, Windows and Mac OS) are
- * ignored.
- */
- public void text(String str, float x, float y) {
- if (textFont == null) {
- throw new RuntimeException("use textFont() before text()");
- }
-
- if (textMode == SCREEN) beginPixels();
-
- int length = str.length();
- if (length > textBuffer.length) {
- textBuffer = new char[length + 10];
- }
- str.getChars(0, length, textBuffer, 0);
-
- int start = 0;
- int index = 0;
- while (index < length) {
- if (textBuffer[index] == '\n') {
- textLineImpl(textBuffer, start, index, x, y);
- start = index + 1;
- y += textLeading;
- }
- index++;
- }
- if (start < length) {
- textLineImpl(textBuffer, start, index, x, y);
- }
- if (textMode == SCREEN) endPixels();
- }
-
-
- /**
- * Same as above but with a z coordinate.
- */
- public void text(String str, float x, float y, float z) {
- if ((z != 0) && (textMode == SCREEN)) {
- String msg = "textMode(SCREEN) cannot have a z coordinate";
- throw new RuntimeException(msg);
- }
-
- if (z != 0) translate(0, 0, z); // slow!
-
- text(str, x, y);
- textZ = z;
-
- if (z != 0) translate(0, 0, -z);
- }
-
-
- /**
- * Handles placement of a text line, then calls textLinePlaced
- * to actually render at the specific point.
- */
- protected void textLineImpl(char buffer[], int start, int stop,
- float x, float y) {
- if (textAlign == CENTER) {
- x -= textWidthImpl(buffer, start, stop) / 2f;
-
- } else if (textAlign == RIGHT) {
- x -= textWidthImpl(buffer, start, stop);
- }
- textLinePlacedImpl(buffer, start, stop, x, y);
- }
-
-
- protected void textLinePlacedImpl(char buffer[], int start, int stop,
- float x, float y) {
- for (int index = start; index < stop; index++) {
- textCharImpl(buffer[index], x, y); //, 0); //z);
-
- // this doesn't account for kerning
- x += textWidth(buffer[index]);
- }
- textX = x;
- textY = y;
- textZ = 0; // this will get set by the caller if non-zero
- }
-
-
- /**
- * Draw text in a box that is constrained to a particular size.
- * The current rectMode() determines what the coordinates mean
- * (whether x1/y1/x2/y2 or x/y/w/h).
- *
- * Given an (x, y, z) coordinate, returns the x position of where
- * that point would be placed on screen, once affected by translate(),
- * scale(), or any other transformations.
- */
- public float screenX(float x, float y, float z) {
- depthErrorXYZ("screenX");
- return 0;
- }
-
-
- /**
- * Maps a three dimensional point to its placement on-screen.
- *
- * Given an (x, y, z) coordinate, returns the y position of where
- * that point would be placed on screen, once affected by translate(),
- * scale(), or any other transformations.
- */
- public float screenY(float x, float y, float z) {
- depthErrorXYZ("screenY");
- return 0;
- }
-
-
- /**
- * Maps a three dimensional point to its placement on-screen.
- *
- * Given an (x, y, z) coordinate, returns its z value.
- * This value can be used to determine if an (x, y, z) coordinate
- * is in front or in back of another (x, y, z) coordinate.
- * The units are based on how the zbuffer is set up, and don't
- * relate to anything "real". They're only useful for in
- * comparison to another value obtained from screenZ(),
- * or directly out of the zbuffer[].
- */
- public float screenZ(float x, float y, float z) {
- depthErrorXYZ("screenZ");
- return 0;
- }
-
-
- /**
- * Returns the model space x value for an x, y, z coordinate.
- *
- * This will give you a coordinate after it has been transformed
- * by translate(), rotate(), and camera(), but not yet transformed
- * by the projection matrix. For instance, his can be useful for
- * figuring out how points in 3D space relate to the edge
- * coordinates of a shape.
- */
- public float modelX(float x, float y, float z) {
- depthError("modelX");
- return 0;
- }
-
-
- /**
- * Returns the model space y value for an x, y, z coordinate.
- */
- public float modelY(float x, float y, float z) {
- depthError("modelY");
- return 0;
- }
-
-
- /**
- * Returns the model space z value for an x, y, z coordinate.
- */
- public float modelZ(float x, float y, float z) {
- depthError("modelZ");
- return 0;
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // COLOR
-
-
- public void colorMode(int mode) {
- colorMode(mode, colorModeX, colorModeY, colorModeZ, colorModeA);
- }
-
-
- public void colorMode(int mode, float max) {
- colorMode(mode, max, max, max, max);
- }
-
-
- /**
- * Set the colorMode and the maximum values for (r, g, b)
- * or (h, s, b).
- *
- * Note that this doesn't set the maximum for the alpha value,
- * which might be confusing if for instance you switched to
- *
- * Handled here with its own function since this is indepenent
- * of the color mode.
- *
- * Strangely the old version of this code ignored the alpha
- * value. not sure if that was a bug or what.
- *
- * Note, no need for a bounds check since it's a 32 bit number.
- */
- protected void colorCalcARGB(int argb, float alpha) {
- calcColor = argb;
- if (alpha == colorModeA) {
- calcAi = (argb >> 24) & 0xff;
- } else {
- calcAi = (int) (((argb >> 24) & 0xff) * (alpha / colorModeA));
- }
- calcRi = (argb >> 16) & 0xff;
- calcGi = (argb >> 8) & 0xff;
- calcBi = argb & 0xff;
- calcA = (float)calcAi / 255.0f;
- calcR = (float)calcRi / 255.0f;
- calcG = (float)calcGi / 255.0f;
- calcB = (float)calcBi / 255.0f;
- calcAlpha = (calcAi != 255);
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public void strokeWeight(float weight) {
- strokeWeight = weight;
- }
-
-
- public void strokeJoin(int join) {
- strokeJoin = join;
- }
-
-
- public void strokeCap(int cap) {
- strokeCap = cap;
- }
-
-
- public void noStroke() {
- stroke = false;
- }
-
-
- /**
- * Set the tint to either a grayscale or ARGB value.
- * See notes attached to the fill() function.
- */
- public void stroke(int rgb) {
- if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
- stroke((float) rgb);
-
- } else {
- colorCalcARGB(rgb, colorModeA);
- strokeFromCalc();
- }
- }
-
-
- public void stroke(int rgb, float alpha) {
- if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
- stroke((float) rgb, alpha);
-
- } else {
- colorCalcARGB(rgb, alpha);
- strokeFromCalc();
- }
- }
-
-
- public void stroke(float gray) {
- colorCalc(gray);
- strokeFromCalc();
- }
-
-
- public void stroke(float gray, float alpha) {
- colorCalc(gray, alpha);
- strokeFromCalc();
- }
-
-
- public void stroke(float x, float y, float z) {
- colorCalc(x, y, z);
- strokeFromCalc();
- }
-
-
- public void stroke(float x, float y, float z, float a) {
- colorCalc(x, y, z, a);
- strokeFromCalc();
- }
-
-
- protected void strokeFromCalc() {
- stroke = true;
- //strokeChanged = true;
- strokeR = calcR;
- strokeG = calcG;
- strokeB = calcB;
- strokeA = calcA;
- strokeRi = calcRi;
- strokeGi = calcGi;
- strokeBi = calcBi;
- strokeAi = calcAi;
- strokeColor = calcColor;
- strokeAlpha = calcAlpha;
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public void noTint() {
- tint = false;
- }
-
-
- /**
- * Set the tint to either a grayscale or ARGB value. See notes
- * attached to the fill() function.
- */
- public void tint(int rgb) {
- if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
- tint((float) rgb);
-
- } else {
- colorCalcARGB(rgb, colorModeA);
- tintFromCalc();
- }
- }
-
- public void tint(int rgb, float alpha) {
- if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
- tint((float) rgb, alpha);
-
- } else {
- colorCalcARGB(rgb, alpha);
- tintFromCalc();
- }
- }
-
- public void tint(float gray) {
- colorCalc(gray);
- tintFromCalc();
- }
-
-
- public void tint(float gray, float alpha) {
- colorCalc(gray, alpha);
- tintFromCalc();
- }
-
-
- public void tint(float x, float y, float z) {
- colorCalc(x, y, z);
- tintFromCalc();
- }
-
-
- public void tint(float x, float y, float z, float a) {
- colorCalc(x, y, z, a);
- tintFromCalc();
- }
-
-
- protected void tintFromCalc() {
- tint = true;
- tintR = calcR;
- tintG = calcG;
- tintB = calcB;
- tintA = calcA;
- tintRi = calcRi;
- tintGi = calcGi;
- tintBi = calcBi;
- tintAi = calcAi;
- tintColor = calcColor;
- tintAlpha = calcAlpha;
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public void noFill() {
- fill = false;
- }
-
-
- /**
- * Set the fill to either a grayscale value or an ARGB int.
- *
- * The problem with this code is that it has to detect between
- * these two situations automatically. This is done by checking
- * to see if the high bits (the alpha for 0xAA000000) is set,
- * and if not, whether the color value that follows is less than
- * colorModeX (the first param passed to colorMode).
- *
- * This auto-detect would break in the following situation:
- *
- * Note that background() should be called before any
- * transformations occur, because some implementations may
- * require the current transformation matrix to be identity
- * before drawing.
- */
- public void background(int rgb) {
- if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
- background((float) rgb);
-
- } else {
- colorCalcARGB(rgb, colorModeA);
- backgroundFromCalc();
- }
- clear();
- }
-
-
- /**
- * Set the background to a grayscale value, based on the
- * current colorMode.
- */
- public void background(float gray) {
- colorCalc(gray);
- backgroundFromCalc();
- clear();
- }
-
-
- /**
- * Set the background to an r, g, b or h, s, b value,
- * based on the current colorMode.
- */
- public void background(float x, float y, float z) {
- colorCalc(x, y, z);
- backgroundFromCalc();
- clear();
- }
-
-
- protected void backgroundFromCalc() {
- backgroundR = calcR;
- backgroundG = calcG;
- backgroundB = calcB;
- backgroundRi = calcRi;
- backgroundGi = calcGi;
- backgroundBi = calcBi;
- backgroundColor = calcColor;
- }
-
-
- /**
- * Takes an RGB or ARGB image and sets it as the background.
- *
- * Note that even if the image is set as RGB, the high 8 bits of
- * each pixel should be set opaque (0xFF000000), because the image data
- * will be copied directly to the screen, and non-opaque background
- * images may have strange behavior. Using image.filter(OPAQUE)
- * will handle this easily.
- *
- * When using 3D, this will also clear out the zbuffer and
- * stencil buffer if they exist.
- */
- public void background(PImage image) {
- if ((image.width != width) || (image.height != height)) {
- throw new RuntimeException("background image must be " +
- "the same size as your application");
- }
- if ((image.format != RGB) && (image.format != ARGB)) {
- throw new RuntimeException("background images should be RGB or ARGB");
- }
-
- // zero this out since it's an image
- backgroundColor = 0;
-
- // blit image to the screen
- System.arraycopy(image.pixels, 0, pixels, 0, pixels.length);
- }
-
-
- /**
- * Clears pixel buffer.
- *
- * Subclasses (PGraphics3) will also clear the
- * stencil and zbuffer if they exist.
- */
- protected void clear() {
- for (int i = 0; i < pixelCount; i++) {
- pixels[i] = backgroundColor;
- }
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // MESSAGES / ERRORS / LOGGING
-
-
- protected void depthError(String method) {
- throw new RuntimeException(method + "() can only be used " +
- "with P3D or OPENGL.");
- }
-
- protected void depthErrorXYZ(String method) {
- throw new RuntimeException(method + "(x, y, z) can only be used with " +
- "OPENGL or P3D, use " +
- method + "(x, y) instead.");
- }
-
-
- //////////////////////////////////////////////////////////////
-
- // COLOR MANIPULATION
-
- // these functions are really slow, but easy to use
- // if folks are advanced enough to want something faster,
- // they can write it themselves (not difficult)
-
-
- public final int color(int gray) { // ignore
- if (((gray & 0xff000000) == 0) && (gray <= colorModeX)) {
- if (colorRgb255) {
- // bounds checking to make sure the numbers aren't to high or low
- if (gray > 255) gray = 255; else if (gray < 0) gray = 0;
- return 0xff000000 | (gray << 16) | (gray << 8) | gray;
- } else {
- colorCalc(gray);
- }
- } else {
- colorCalcARGB(gray, colorModeA);
- }
- return calcColor;
- }
-
- public final int color(float gray) { // ignore
- colorCalc(gray);
- return calcColor;
- }
-
-
- /**
- * @param gray can be packed ARGB or a gray in this case
- */
- public final int color(int gray, int alpha) { // ignore
- if (colorRgb255) {
- // bounds checking to make sure the numbers aren't to high or low
- if (gray > 255) gray = 255; else if (gray < 0) gray = 0;
- if (alpha > 255) alpha = 255; else if (alpha < 0) alpha = 0;
-
- return ((alpha & 0xff) << 24) | (gray << 16) | (gray << 8) | gray;
- }
- colorCalc(gray, alpha);
- return calcColor;
- }
-
- /**
- * @param rgb can be packed ARGB or a gray in this case
- */
- public final int color(int rgb, float alpha) { // ignore
- if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
- colorCalc(rgb, alpha);
- } else {
- colorCalcARGB(rgb, alpha);
- }
- return calcColor;
- }
-
- public final int color(float gray, float alpha) { // ignore
- colorCalc(gray, alpha);
- return calcColor;
- }
-
-
- public final int color(int x, int y, int z) { // ignore
- if (colorRgb255) {
- // bounds checking to make sure the numbers aren't to high or low
- 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;
- }
- colorCalc(x, y, z);
- return calcColor;
- }
-
- public final int color(float x, float y, float z) { // ignore
- colorCalc(x, y, z);
- return calcColor;
- }
-
-
- public final int color(int x, int y, int z, int a) { // ignore
- if (colorRgb255) {
- // bounds checking to make sure the numbers aren't to high or low
- 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;
- }
- colorCalc(x, y, z, a);
- return calcColor;
- }
-
- public final int color(float x, float y, float z, float a) { // ignore
- colorCalc(x, y, z, a);
- return calcColor;
- }
-
-
- public final float alpha(int what) {
- float c = (what >> 24) & 0xff;
- if (colorModeA == 255) return c;
- return (c / 255.0f) * colorModeA;
- }
-
- public final float red(int what) {
- float c = (what >> 16) & 0xff;
- if (colorRgb255) return c;
- return (c / 255.0f) * colorModeX;
- }
-
- public final float green(int what) {
- float c = (what >> 8) & 0xff;
- if (colorRgb255) return c;
- return (c / 255.0f) * colorModeY;
- }
-
- public final float blue(int what) {
- float c = (what) & 0xff;
- if (colorRgb255) return c;
- return (c / 255.0f) * colorModeZ;
- }
-
-
- public final float hue(int what) {
- if (what != cacheHsbKey) {
- Color.RGBtoHSB((what >> 16) & 0xff, (what >> 8) & 0xff,
- what & 0xff, cacheHsbValue);
- cacheHsbKey = what;
- }
- return cacheHsbValue[0] * colorModeX;
- }
-
- public final float saturation(int what) {
- if (what != cacheHsbKey) {
- Color.RGBtoHSB((what >> 16) & 0xff, (what >> 8) & 0xff,
- what & 0xff, cacheHsbValue);
- cacheHsbKey = what;
- }
- return cacheHsbValue[1] * colorModeY;
- }
-
- public final float brightness(int what) {
- if (what != cacheHsbKey) {
- Color.RGBtoHSB((what >> 16) & 0xff, (what >> 8) & 0xff,
- what & 0xff, cacheHsbValue);
- cacheHsbKey = what;
- }
- return cacheHsbValue[2] * colorModeZ;
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // PATH
-
- class Path {
-
- public void moveTo(float x, float y) { // ignore
- }
-
- public void lineTo(float x, float y) { // ignore
- }
-
- public void curveTo(float x1, float y1, // ignore
- float x2, float y2,
- float x3, float y3) {
- }
-
- public void closePath() { // ignore
- }
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- /**
- * Use with caution on PGraphics. This should not be used with
- * the base PGraphics that's tied to a PApplet, but it can be used
- * with user-created PGraphics objects that are drawn to the screen.
- */
- public void mask(int alpha[]) { // ignore
- super.mask(alpha);
- }
-
-
- /**
- * Use with caution on PGraphics. This should not be used with
- * the base PGraphics that's tied to a PApplet, but it can be used
- * with user-created PGraphics objects that are drawn to the screen.
- */
- public void mask(PImage alpha) { // ignore
- super.mask(alpha);
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public void beginRaw(PGraphics raw) {
- this.raw = raw;
- raw.beginDraw();
- }
-
-
- public void endRaw() {
- if (raw != null) {
- // for 3D, need to flush any geometry that's been stored for sorting
- raw.flush();
-
- // just like beginDraw, this will have to be called because
- // endDraw() will be happening outside of draw()
- raw.endDraw();
- raw.dispose();
- raw = null;
- }
- }
-
-
- /**
- * Handle any takedown for this graphics context.
- *
- * This is called when a sketch is shut down and this renderer was
- * specified using the size() command, or inside endRecord() and
- * endRaw(), in order to shut things off.
- */
- public void dispose() { // ignore
- }
-
-
- /**
- * Return true if this renderer should be drawn to the screen.
- * Overridden for subclasses like PDF so that an enormous window
- * doesn't open up.
- * showFrame, displayable, isVisible, visible, shouldDisplay,
- * what to call this?
- */
- public boolean displayable() {
- return true;
- }
-}
+/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2004-06 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.awt.*;
+import java.awt.image.*;
+
+
+/**
+ * Main graphics and rendering context, as well as
+ * the base API implementation for processing "core".
+ *
+ * As of beta, this class is semi-disabled.
+ */
+public class PGraphics extends PImage implements PConstants {
+
+ /***
+ * Parent applet as passed in by the constructor. If null, then
+ * no MemoryImageSource will be used or updated, saving memory.
+ */
+ PApplet parent;
+
+ /// width minus one (useful for many calculations)
+ public int width1;
+
+ /// height minus one (useful for many calculations)
+ public int height1;
+
+ /// width * height (useful for many calculations)
+ public int pixelCount;
+
+ /// true if defaults() has been called a first time
+ boolean defaultsInited;
+
+ // ........................................................
+
+ // specifics for java memoryimagesource
+ DirectColorModel cm;
+ MemoryImageSource mis;
+ public Image image;
+
+ // ........................................................
+
+ // used by recordRaw()
+ public PGraphics raw;
+
+ // ........................................................
+
+ // needs to happen before background() is called
+ // and resize.. so it's gotta be outside
+ protected boolean hints[] = new boolean[HINT_COUNT];
+
+ // ........................................................
+
+ // underscored_names are used for private functions or variables
+
+ /** The current colorMode */
+ public int colorMode; // = RGB;
+
+ /** Max value for red (or hue) set by colorMode */
+ public float colorModeX; // = 255;
+
+ /** Max value for green (or saturation) set by colorMode */
+ public float colorModeY; // = 255;
+
+ /** Max value for blue (or value) set by colorMode */
+ public float colorModeZ; // = 255;
+
+ /** Max value for alpha set by colorMode */
+ public float colorModeA; // = 255;
+
+ /** True if colors are not in the range 0..1 */
+ boolean colorScale; // = true;
+
+ /** True if colorMode(RGB, 255) */
+ boolean colorRgb255; // = true;
+
+ // ........................................................
+
+ /**
+ * true if tint() is enabled (read-only).
+ * Using tint/tintColor seems a better option for naming than
+ * tintEnabled/tint because the latter seems ugly, even though
+ * g.tint as the actual color seems a little more intuitive,
+ * it's just that g.tintEnabled is even more unintuitive.
+ * Same goes for fill and stroke et al.
+ */
+ public boolean tint;
+
+ /** tint that was last set (read-only) */
+ public int tintColor;
+
+ protected boolean tintAlpha;
+ protected float tintR, tintG, tintB, tintA;
+ protected int tintRi, tintGi, tintBi, tintAi;
+
+ // ........................................................
+
+ /** true if fill() is enabled, (read-only) */
+ public boolean fill;
+
+ /** fill that was last set (read-only) */
+ public int fillColor;
+
+ protected boolean fillAlpha;
+ protected float fillR, fillG, fillB, fillA;
+ protected int fillRi, fillGi, fillBi, fillAi;
+
+ // ........................................................
+
+ /** true if stroke() is enabled, (read-only) */
+ public boolean stroke;
+
+ /** stroke that was last set (read-only) */
+ public int strokeColor;
+
+ protected boolean strokeAlpha;
+ protected float strokeR, strokeG, strokeB, strokeA;
+ protected int strokeRi, strokeGi, strokeBi, strokeAi;
+
+ // ........................................................
+
+ /** Last background color that was set, zero if an image */
+ public int backgroundColor;
+
+ float backgroundR, backgroundG, backgroundB;
+ int backgroundRi, backgroundGi, backgroundBi;
+
+ // ........................................................
+
+ // internal color for setting/calculating
+ protected float calcR, calcG, calcB, calcA;
+ int calcRi, calcGi, calcBi, calcAi;
+ int calcColor;
+ boolean calcAlpha;
+
+ /** The last rgb value converted to HSB */
+ int cacheHsbKey;
+ /** Result of the last conversion to HSB */
+ float cacheHsbValue[] = new float[3]; // inits to zero
+
+ // ........................................................
+
+ /** Last value set by strokeWeight() (read-only) */
+ public float strokeWeight;
+
+ /**
+ * Set by strokeJoin() (read-only). This has a default setting
+ * so that strokeJoin() need not be called by defaults,
+ * because subclasses may not implement it (i.e. PGraphicsGL)
+ */
+ public int strokeJoin = MITER;
+
+ /**
+ * Set by strokeCap() (read-only). This has a default setting
+ * so that strokeCap() need not be called by defaults,
+ * because subclasses may not implement it (i.e. PGraphicsGL)
+ */
+ public int strokeCap = ROUND;
+
+ // ........................................................
+
+ /**
+ * Model transformation of the form m[row][column],
+ * which is a "column vector" (as opposed to "row vector") matrix.
+ */
+ public float m00, m01, m02, m03;
+ public float m10, m11, m12, m13;
+ public float m20, m21, m22, m23;
+ public float m30, m31, m32, m33;
+
+ static final int MATRIX_STACK_DEPTH = 32;
+ float matrixStack[][] = new float[MATRIX_STACK_DEPTH][16];
+ int matrixStackDepth;
+
+ // ........................................................
+
+ Path path;
+
+ // ........................................................
+
+ /**
+ * Type of shape passed to beginShape(),
+ * zero if no shape is currently being drawn.
+ */
+ protected int shape;
+
+ // vertices
+ static final int DEFAULT_VERTICES = 512;
+ protected float vertices[][] =
+ new float[DEFAULT_VERTICES][VERTEX_FIELD_COUNT];
+ protected int vertexCount; // total number of vertices
+
+
+ // ........................................................
+
+ protected boolean bezierInited = false;
+ public int bezierDetail = 20;
+ // msjvm complained when bezier_basis was final
+ protected float bezier_basis[][] = {
+ { -1, 3, -3, 1},
+ { 3, -6, 3, 0},
+ { -3, 3, 0, 0},
+ { 1, 0, 0, 0}
+ };
+
+ protected PMatrix bezierBasis =
+ new PMatrix(-1, 3, -3, 1,
+ 3, -6, 3, 0,
+ -3, 3, 0, 0,
+ 1, 0, 0, 0);
+
+ protected float bezier_forward[][]; // = new float[4][4];
+ protected float bezier_draw[][]; // = new float[4][4];
+
+ // ........................................................
+
+ protected boolean curve_inited = false;
+ protected int curveDetail = 20;
+ // catmull-rom basis matrix, perhaps with optional s parameter
+ public float curveTightness = 0;
+ protected float curve_basis[][]; // = new float[4][4];
+ protected float curve_forward[][]; // = new float[4][4];
+ protected float curve_draw[][];
+
+ protected PMatrix bezierBasisInverse;
+ protected PMatrix curveToBezierMatrix;
+
+ // ........................................................
+
+ // spline vertices
+
+ static final int DEFAULT_SPLINE_VERTICES = 128;
+ protected float splineVertices[][];
+ protected int splineVertexCount;
+
+ // ........................................................
+
+ // precalculate sin/cos lookup tables [toxi]
+ // circle resolution is determined from the actual used radii
+ // passed to ellipse() method. this will automatically take any
+ // scale transformations into account too
+
+ // [toxi 031031]
+ // changed table's precision to 0.5 degree steps
+ // introduced new vars for more flexible code
+ static final float sinLUT[];
+ static final float cosLUT[];
+ static final float SINCOS_PRECISION = 0.5f;
+ static final int SINCOS_LENGTH = (int) (360f / SINCOS_PRECISION);
+ static {
+ sinLUT = new float[SINCOS_LENGTH];
+ cosLUT = new float[SINCOS_LENGTH];
+ for (int i = 0; i < SINCOS_LENGTH; i++) {
+ sinLUT[i] = (float) Math.sin(i * DEG_TO_RAD * SINCOS_PRECISION);
+ cosLUT[i] = (float) Math.cos(i * DEG_TO_RAD * SINCOS_PRECISION);
+ }
+ }
+
+ // ........................................................
+
+ /** The current rect mode (read-only) */
+ public int rectMode;
+
+ /** The current ellipse mode (read-only) */
+ public int ellipseMode;
+
+ /** The current text font (read-only) */
+ public PFont textFont;
+
+ /** The current font if a Java version of it is installed */
+ public Font textFontNative;
+
+ /** Metrics for the current native Java font */
+ public FontMetrics textFontNativeMetrics;
+
+ /** The current text align (read-only) */
+ public int textAlign;
+
+ /** The current text mode (read-only) */
+ public int textMode;
+
+ /** The current text size (read-only) */
+ public float textSize;
+
+ /** The current text leading (read-only) */
+ public float textLeading;
+
+ /** Last text position, because text often mixed on lines together */
+ public float textX, textY, textZ;
+
+ /**
+ * Internal buffer used by the text() functions
+ * because the String object is slow
+ */
+ protected char textBuffer[] = new char[8 * 1024];
+ protected char textWidthBuffer[] = new char[8 * 1024];
+
+
+ //////////////////////////////////////////////////////////////
+
+ // VARIABLES FOR 3D (used to prevent the need for a subclass)
+
+
+ /** The modelview matrix. */
+ public PMatrix modelview;
+
+ /** Inverse modelview matrix, used for lighting. */
+ public PMatrix modelviewInv;
+
+ /**
+ * The camera matrix, the modelview will be set to this on beginDraw.
+ */
+ public PMatrix camera;
+
+ /** Inverse camera matrix */
+ public PMatrix cameraInv;
+
+ // ........................................................
+
+ // Material properties
+
+ public float ambientR, ambientG, ambientB;
+ public float specularR, specularG, specularB, specularA;
+ public float emissiveR, emissiveG, emissiveB;
+ public float shininess;
+
+ // ........................................................
+
+ /** Camera field of view (in radians, as of rev 86) */
+ public float cameraFOV;
+
+ /** Position of the camera */
+ public float cameraX, cameraY, cameraZ;
+
+ public float cameraNear, cameraFar;
+ public float cameraAspect;
+
+ // projection matrix
+ public PMatrix projection; // = new PMatrix();
+
+ // ........................................................
+
+ /// the stencil buffer
+ public int stencil[];
+
+ /// depth buffer
+ public float zbuffer[];
+
+ // ........................................................
+
+ /** Maximum lights by default is 8, which is arbitrary,
+ but is the minimum defined by OpenGL */
+ public static final int MAX_LIGHTS = 8;
+
+ public int lightCount = 0;
+
+ /** Light types */
+ public int lightType[];
+
+ /** Light positions */
+ public float lightPosition[][];
+ //public float lightsX[], lightsY[], lightsZ[];
+
+ /** Light direction (normalized vector) */
+ public float lightNormal[][];
+ //public float lightsNX[], lightsNY[], lightsNZ[];
+
+ /** Light falloff */
+ public float lightFalloffConstant[];
+ public float lightFalloffLinear[];
+ public float lightFalloffQuadratic[];
+
+ /** Light spot angle */
+ public float lightSpotAngle[];
+
+ /** Cosine of light spot angle */
+ public float lightSpotAngleCos[];
+
+ /** Light spot concentration */
+ public float lightSpotConcentration[];
+
+ /** Diffuse colors for lights.
+ * For an ambient light, this will hold the ambient color.
+ * Internally these are stored as numbers between 0 and 1. */
+ public float lightDiffuse[][];
+
+ /** Specular colors for lights.
+ Internally these are stored as numbers between 0 and 1. */
+ public float lightSpecular[][];
+
+ /** Current specular color for lighting */
+ public float currentLightSpecular[];
+
+ /** Current light falloff */
+ public float currentLightFalloffConstant;
+ public float currentLightFalloffLinear;
+ public float currentLightFalloffQuadratic;
+
+ // ........................................................
+
+ /**
+ * Sets whether texture coordinates passed to
+ * vertex() calls will be based on coordinates that are
+ * based on the IMAGE or NORMALIZED.
+ */
+ public int textureMode;
+
+ /**
+ * Current horizontal coordinate for texture, will always
+ * be between 0 and 1, even if using textureMode(IMAGE).
+ */
+ public float textureU;
+
+ /** Current vertical coordinate for texture, see above. */
+ public float textureV;
+
+ /** Current image being used as a texture */
+ public PImage textureImage;
+
+ // ........................................................
+
+ /**
+ * Normals
+ */
+ public float normalX, normalY, normalZ;
+ public int normalMode;
+ public int normalCount;
+
+ // ........................................................
+
+ // [toxi031031] new & faster sphere code w/ support flexibile resolutions
+ // will be set by sphereDetail() or 1st call to sphere()
+ public int sphereDetail = 0;
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // INTERNAL
+
+
+ /**
+ * Constructor for the PGraphics object.
+ * This prototype only exists because of annoying
+ * java compilers, and should not be used.
+ */
+ public PGraphics() { }
+
+
+ /**
+ * Constructor for the PGraphics object. Use this to ensure that
+ * the defaults get set properly. In a subclass, use this(w, h)
+ * as the first line of a subclass' constructor to properly set
+ * the internal fields and defaults.
+ *
+ * @param iwidth viewport width
+ * @param iheight viewport height
+ */
+ public PGraphics(int iwidth, int iheight) {
+ this(iwidth, iheight, null);
+ //resize(iwidth, iheight);
+ }
+
+
+ /**
+ * Constructor for the PGraphics object. Use this to ensure that
+ * the defaults get set properly. In a subclass, use this(w, h)
+ * as the first line of a subclass' constructor to properly set
+ * the internal fields and defaults.
+ *
+ * @param iwidth viewport width
+ * @param iheight viewport height
+ */
+ public PGraphics(int iwidth, int iheight, PApplet applet) {
+ if (applet != null) {
+ this.parent = applet;
+ applet.addListeners();
+ }
+ resize(iwidth, iheight);
+ }
+
+
+ /**
+ * Called in repsonse to a resize event, handles setting the
+ * new width and height internally, as well as re-allocating
+ * the pixel buffer for the new size.
+ *
+ * Note that this will nuke any cameraMode() settings.
+ */
+ public void resize(int iwidth, int iheight) { // ignore
+ //System.out.println("resize " + iwidth + " " + iheight);
+
+ width = iwidth;
+ height = iheight;
+ width1 = width - 1;
+ height1 = height - 1;
+
+ allocate();
+
+ // clear the screen with the old background color
+ //background(backgroundColor);
+ }
+
+
+ /**
+ * Parent thread has requested that visual action be taken.
+ */
+ public void requestDisplay(PApplet parent) { // ignore
+ parent.handleDisplay();
+ }
+
+
+ // broken out because of subclassing
+ protected void allocate() {
+ pixelCount = width * height;
+ pixels = new int[pixelCount];
+
+ // because of a java 1.1 bug, pixels must be registered as
+ // opaque before their first run, the memimgsrc will flicker
+ // and run very slowly.
+ backgroundColor |= 0xff000000; // just for good measure
+ for (int i = 0; i < pixelCount; i++) pixels[i] = backgroundColor;
+ //for (int i = 0; i < pixelCount; i++) pixels[i] = 0xffffffff;
+
+ if (parent != null) {
+ cm = new DirectColorModel(32, 0x00ff0000, 0x0000ff00, 0x000000ff);;
+ mis = new MemoryImageSource(width, height, pixels, 0, width);
+ mis.setFullBufferUpdates(true);
+ mis.setAnimated(true);
+ image = Toolkit.getDefaultToolkit().createImage(mis);
+ }
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // FRAME
+
+
+ /**
+ * Former function, now called beginDraw.
+ * @deprecated
+ */
+ /*
+ public void beginFrame() { // ignore
+ System.err.println("beginFrame() is now beginDraw(), please use that instead");
+ beginDraw();
+ }
+ */
+
+
+ /**
+ * Former function, now called endDraw.
+ * @deprecated
+ */
+ /*
+ public void endFrame() { // ignore
+ System.err.println("endFrame() is now endDraw(), please use that instead");
+ endDraw();
+ }
+ */
+
+
+ /**
+ * Prepares the PGraphics for drawing.
+ *
+ * For the most part, hints are temporary api quirks,
+ * for which a proper api hasn't been properly worked out.
+ * for instance SMOOTH_IMAGES existed because smooth()
+ * wasn't yet implemented, but it will soon go away.
+ *
+ * They also exist for obscure features in the graphics
+ * engine, like enabling/disabling single pixel lines
+ * that ignore the zbuffer, the way they do in alphabot.
+ *
+ * Current hint options:
+ *
+ * Differences between beginShape() and line() and point() methods.
+ *
+ * beginShape() is intended to be more flexible at the expense of being
+ * a little more complicated to use. it handles more complicated shapes
+ * that can consist of many connected lines (so you get joins) or lines
+ * mixed with curves.
+ *
+ * The line() and point() command are for the far more common cases
+ * (particularly for our audience) that simply need to draw a line
+ * or a point on the screen.
+ *
+ * From the code side of things, line() may or may not call beginShape()
+ * to do the drawing. In the beta code, they do, but in the alpha code,
+ * they did not. they might be implemented one way or the other depending
+ * on tradeoffs of runtime efficiency vs. implementation efficiency &mdash
+ * meaning the speed that things run at vs. the speed it takes me to write
+ * the code and maintain it. for beta, the latter is most important so
+ * that's how things are implemented.
+ */
+ public void beginShape(int kind) {
+ shape = kind;
+
+ // reset vertex, line and triangle information
+ // every shape is rendered at endShape();
+ vertexCount = 0;
+
+ splineVertexCount = 0;
+ //spline_vertices_flat = true;
+
+ //strokeChanged = false;
+ //fillChanged = false;
+ //normalChanged = false;
+ }
+
+
+ public void normal(float nx, float ny, float nz) {
+ depthError("normal");
+ }
+
+ public void textureMode(int mode) {
+ depthError("textureMode");
+ }
+
+ public void texture(PImage image) {
+ depthError("texture");
+ }
+
+
+ public void vertex(float x, float y) {
+ splineVertexCount = 0;
+ //float vertex[];
+
+ if (vertexCount == vertices.length) {
+ float temp[][] = new float[vertexCount<<1][VERTEX_FIELD_COUNT];
+ System.arraycopy(vertices, 0, temp, 0, vertexCount);
+ vertices = temp;
+ //message(CHATTER, "allocating more vertices " + vertices.length);
+ }
+ // not everyone needs this, but just easier to store rather
+ // than adding another moving part to the code...
+ vertices[vertexCount][MX] = x;
+ vertices[vertexCount][MY] = y;
+ vertexCount++;
+
+ switch (shape) {
+
+ case POINTS:
+ point(x, y);
+ break;
+
+ case LINES:
+ if ((vertexCount % 2) == 0) {
+ line(vertices[vertexCount-2][MX],
+ vertices[vertexCount-2][MY], x, y);
+ }
+ break;
+
+ case LINE_STRIP:
+ case LINE_LOOP:
+ if (vertexCount == 1) {
+ path = new Path();
+ path.moveTo(x, y);
+ } else {
+ path.lineTo(x, y);
+ }
+ break;
+
+ case TRIANGLES:
+ if ((vertexCount % 3) == 0) {
+ triangle(vertices[vertexCount - 3][MX],
+ vertices[vertexCount - 3][MY],
+ vertices[vertexCount - 2][MX],
+ vertices[vertexCount - 2][MY],
+ x, y);
+ }
+ break;
+
+ case TRIANGLE_STRIP:
+ if (vertexCount == 3) {
+ triangle(vertices[0][MX], vertices[0][MY],
+ vertices[1][MX], vertices[1][MY],
+ x, y);
+ } else if (vertexCount > 3) {
+ path = new Path();
+ // when vertexCount == 4, draw an un-closed triangle
+ // for indices 2, 3, 1
+ path.moveTo(vertices[vertexCount - 2][MX],
+ vertices[vertexCount - 2][MY]);
+ path.lineTo(vertices[vertexCount - 1][MX],
+ vertices[vertexCount - 1][MY]);
+ path.lineTo(vertices[vertexCount - 3][MX],
+ vertices[vertexCount - 3][MY]);
+ draw_shape(path);
+ }
+ break;
+
+ case TRIANGLE_FAN:
+ if (vertexCount == 3) {
+ triangle(vertices[0][MX], vertices[0][MY],
+ vertices[1][MX], vertices[1][MY],
+ x, y);
+ } else if (vertexCount > 3) {
+ path = new Path();
+ // when vertexCount > 3, draw an un-closed triangle
+ // for indices 0 (center), previous, current
+ path.moveTo(vertices[0][MX],
+ vertices[0][MY]);
+ path.lineTo(vertices[vertexCount - 2][MX],
+ vertices[vertexCount - 2][MY]);
+ path.lineTo(x, y);
+ draw_shape(path);
+ }
+ break;
+
+ case QUADS:
+ if ((vertexCount % 4) == 0) {
+ quad(vertices[vertexCount - 4][MX],
+ vertices[vertexCount - 4][MY],
+ vertices[vertexCount - 3][MX],
+ vertices[vertexCount - 3][MY],
+ vertices[vertexCount - 2][MX],
+ vertices[vertexCount - 2][MY],
+ x, y);
+ }
+ break;
+
+ case QUAD_STRIP:
+ // 0---2---4
+ // | | |
+ // 1---3---5
+ if (vertexCount == 4) {
+ // note difference in winding order:
+ quad(vertices[0][MX], vertices[0][MY],
+ vertices[2][MX], vertices[2][MY],
+ x, y,
+ vertices[1][MX], vertices[1][MY]);
+
+ } else if (vertexCount > 4) {
+ path = new Path();
+ // when vertexCount == 5, draw an un-closed triangle
+ // for indices 2, 4, 5, 3
+ path.moveTo(vertices[vertexCount - 3][MX],
+ vertices[vertexCount - 3][MY]);
+ path.lineTo(vertices[vertexCount - 1][MX],
+ vertices[vertexCount - 1][MY]);
+ path.lineTo(x, y);
+ path.lineTo(vertices[vertexCount - 2][MX],
+ vertices[vertexCount - 2][MY]);
+ draw_shape(path);
+ }
+ break;
+
+ case POLYGON:
+ //case CONCAVE_POLYGON:
+ //case CONVEX_POLYGON:
+ if (vertexCount == 1) {
+ path = new Path();
+ path.moveTo(x, y);
+ } else {
+ path.lineTo(x, y);
+ }
+ break;
+ }
+ }
+
+
+ public void vertex(float x, float y, float z) {
+ depthErrorXYZ("vertex");
+ }
+
+
+ public void vertex(float x, float y, float u, float v) {
+ throw new RuntimeException("vertex() with u, v coordinates " +
+ "can only be used with OPENGL or P3D");
+ }
+
+
+ public void vertex(float x, float y, float z, float u, float v) {
+ throw new RuntimeException("vertex() with u, v coordinates " +
+ "can only be used with OPENGL or P3D");
+ }
+
+
+ public void bezierVertex(float x1, float y1,
+ float x2, float y2,
+ float x3, float y3) {
+ // if there hasn't yet been a call to vertex(), throw an error
+
+ // otherwise, draw a bezier segment to this point
+ }
+
+
+ protected void bezier_vertex(float x, float y) {
+ vertexCount = 0;
+
+ if (splineVertices == null) {
+ splineVertices = new float[DEFAULT_SPLINE_VERTICES][VERTEX_FIELD_COUNT];
+ }
+
+ // if more than 128 points, shift everything back to the beginning
+ if (splineVertexCount == DEFAULT_SPLINE_VERTICES) {
+ System.arraycopy(splineVertices[DEFAULT_SPLINE_VERTICES - 3], 0,
+ splineVertices[0], 0, VERTEX_FIELD_COUNT);
+ System.arraycopy(splineVertices[DEFAULT_SPLINE_VERTICES - 2], 0,
+ splineVertices[1], 0, VERTEX_FIELD_COUNT);
+ splineVertexCount = 3;
+ }
+ splineVertices[splineVertexCount][MX] = x;
+ splineVertices[splineVertexCount][MY] = y;
+ splineVertexCount++;
+
+ switch (shape) {
+ case LINE_LOOP:
+ case LINE_STRIP:
+ case POLYGON:
+ if (splineVertexCount == 1) {
+ path.moveTo(x, y);
+
+ } else if (splineVertexCount >= 4) {
+ path.curveTo(splineVertices[splineVertexCount-3][MX],
+ splineVertices[splineVertexCount-3][MY],
+ splineVertices[splineVertexCount-2][MX],
+ splineVertices[splineVertexCount-2][MY],
+ x, y);
+ }
+ break;
+
+ default:
+ throw new RuntimeException("bezierVertex() can only be used with " +
+ "LINE_LOOP and POLYGON shapes");
+ }
+ }
+
+
+ public void bezierVertex(float x1, float y1, float z1,
+ float x2, float y2, float z2,
+ float x3, float y3, float z3) {
+ depthErrorXYZ("bezierVertex");
+ }
+
+
+ /**
+ * See notes with the curve() function.
+ */
+ public void curveVertex(float x, float y) {
+ //throw new RuntimeException("curveVertex() temporarily disabled");
+ // TODO get matrix setup happening
+ }
+
+
+ /**
+ * See notes with the curve() function.
+ */
+ public void curveVertex(float x, float y, float z) {
+ depthErrorXYZ("curveVertex");
+ }
+
+
+ public void endShape() {
+ shape = 0;
+
+ switch (shape) {
+ case LINE_STRIP:
+ stroke_shape(path);
+ break;
+
+ case LINE_LOOP:
+ path.closePath();
+ stroke_shape(path);
+ break;
+
+ case POLYGON:
+ path.closePath();
+ draw_shape(path);
+ break;
+ }
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // COMPOUND PATHS
+
+
+ /**
+ * Begin a new path. This can be used after beginShape() to draw
+ * a compound path (i.e. to draw shape with a hole on the interior)
+ * For instance, to draw a shape that has a hole in its interior,
+ * the format would be:
+ *
+ * For instance, to convert the following example:
+ * Identical to typing:
+ *
+ * (This function is not optimized, since it's not expected to
+ * be called all that often. there are many juicy and obvious
+ * opimizations in here, but it's probably better to keep the
+ * code more readable)
+ */
+ protected void curve_mode(int segments, float s) {
+ curveDetail = segments;
+
+ if (curve_basis == null) {
+ // allocate these when used, to save startup time
+ curve_basis = new float[4][4];
+ curve_forward = new float[4][4];
+ curve_draw = new float[4][4];
+ curve_inited = true;
+ }
+
+ float c[][] = curve_basis;
+
+ c[0][0] = s-1; c[0][1] = s+3; c[0][2] = -3-s; c[0][3] = 1-s;
+ c[1][0] = 2*(1-s); c[1][1] = -5-s; c[1][2] = 2*(s+2); c[1][3] = s-1;
+ c[2][0] = s-1; c[2][1] = 0; c[2][2] = 1-s; c[2][3] = 0;
+ c[3][0] = 0; c[3][1] = 2; c[3][2] = 0; c[3][3] = 0;
+
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ c[i][j] /= 2f;
+ }
+ }
+ setup_spline_forward(segments, curve_forward);
+
+ if (bezierBasisInverse == null) {
+ bezierBasisInverse = new PMatrix(bezierBasis).invert();
+ }
+
+ // hack here to get PGraphics2 working
+ curveToBezierMatrix = new PMatrix(c[0][0], c[0][1], c[0][2], c[0][3],
+ c[1][0], c[1][1], c[1][2], c[1][3],
+ c[2][0], c[2][1], c[2][2], c[2][3],
+ c[3][0], c[3][1], c[3][2], c[3][3]);
+ curveToBezierMatrix.preApply(bezierBasisInverse);
+
+ // multiply the basis and forward diff matrices together
+ // saves much time since this needn't be done for each curve
+ mult_spline_matrix(curve_forward, curve_basis, curve_draw, 4);
+ }
+
+
+ /**
+ * Draws a segment of Catmull-Rom curve.
+ *
+ * As of 0070, this function no longer doubles the first and
+ * last points. The curves are a bit more boring, but it's more
+ * mathematically correct, and properly mirrored in curvePoint().
+ *
+ * Identical to typing out:
+ * the x0, y0, z0 points are the point that's being used as
+ * the start, and also as the accumulator. for bezier curves,
+ * the x1, y1, z1 are the first point drawn, and added to.
+ * for catmull-rom curves, the first control point (x2, y2, z2)
+ * is the first drawn point, and is accumulated to.
+ */
+ protected void spline2_segment(int offset, int start,
+ float m[][], int segments) {
+ float x1 = splineVertices[offset][MX];
+ float y1 = splineVertices[offset][MY];
+
+ float x2 = splineVertices[offset+1][MX];
+ float y2 = splineVertices[offset+1][MY];
+
+ float x3 = splineVertices[offset+2][MX];
+ float y3 = splineVertices[offset+2][MY];
+
+ float x4 = splineVertices[offset+3][MX];
+ float y4 = splineVertices[offset+3][MY];
+
+ float x0 = splineVertices[start][MX];
+ float y0 = splineVertices[start][MY];
+
+ float xplot1 = m[1][0]*x1 + m[1][1]*x2 + m[1][2]*x3 + m[1][3]*x4;
+ float xplot2 = m[2][0]*x1 + m[2][1]*x2 + m[2][2]*x3 + m[2][3]*x4;
+ float xplot3 = m[3][0]*x1 + m[3][1]*x2 + m[3][2]*x3 + m[3][3]*x4;
+
+ float yplot1 = m[1][0]*y1 + m[1][1]*y2 + m[1][2]*y3 + m[1][3]*y4;
+ float yplot2 = m[2][0]*y1 + m[2][1]*y2 + m[2][2]*y3 + m[2][3]*y4;
+ float yplot3 = m[3][0]*y1 + m[3][1]*y2 + m[3][2]*y3 + m[3][3]*y4;
+
+ // vertex() will reset splineVertexCount, so save it
+ int splineVertexSaved = splineVertexCount;
+ vertex(x0, y0);
+ for (int j = 0; j < segments; j++) {
+ x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
+ y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
+ vertex(x0, y0);
+ }
+ splineVertexCount = splineVertexSaved;
+ }
+
+
+ protected void spline3_segment(int offset, int start,
+ float m[][], int segments) {
+ float x1 = splineVertices[offset+0][MX];
+ float y1 = splineVertices[offset+0][MY];
+ float z1 = splineVertices[offset+0][MZ];
+
+ float x2 = splineVertices[offset+1][MX];
+ float y2 = splineVertices[offset+1][MY];
+ float z2 = splineVertices[offset+1][MZ];
+
+ float x3 = splineVertices[offset+2][MX];
+ float y3 = splineVertices[offset+2][MY];
+ float z3 = splineVertices[offset+2][MZ];
+
+ float x4 = splineVertices[offset+3][MX];
+ float y4 = splineVertices[offset+3][MY];
+ float z4 = splineVertices[offset+3][MZ];
+
+ float x0 = splineVertices[start][MX];
+ float y0 = splineVertices[start][MY];
+ float z0 = splineVertices[start][MZ];
+
+ float xplot1 = m[1][0]*x1 + m[1][1]*x2 + m[1][2]*x3 + m[1][3]*x4;
+ float xplot2 = m[2][0]*x1 + m[2][1]*x2 + m[2][2]*x3 + m[2][3]*x4;
+ float xplot3 = m[3][0]*x1 + m[3][1]*x2 + m[3][2]*x3 + m[3][3]*x4;
+
+ float yplot1 = m[1][0]*y1 + m[1][1]*y2 + m[1][2]*y3 + m[1][3]*y4;
+ float yplot2 = m[2][0]*y1 + m[2][1]*y2 + m[2][2]*y3 + m[2][3]*y4;
+ float yplot3 = m[3][0]*y1 + m[3][1]*y2 + m[3][2]*y3 + m[3][3]*y4;
+
+ float zplot1 = m[1][0]*z1 + m[1][1]*z2 + m[1][2]*z3 + m[1][3]*z4;
+ float zplot2 = m[2][0]*z1 + m[2][1]*z2 + m[2][2]*z3 + m[2][3]*z4;
+ float zplot3 = m[3][0]*z1 + m[3][1]*z2 + m[3][2]*z3 + m[3][3]*z4;
+
+ // vertex() will reset splineVertexCount, so save it
+ int cvertexSaved = splineVertexCount;
+ vertex(x0, y0, z0);
+ for (int j = 0; j < segments; j++) {
+ x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
+ y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
+ z0 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
+ vertex(x0, y0, z0);
+ }
+ splineVertexCount = cvertexSaved;
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // IMAGE
+
+
+ public void image(PImage image, float x, float y) {
+ imageImpl(image,
+ x, y, x+image.width, y+image.height,
+ 0, 0, image.width, image.height);
+ }
+
+
+ public void image(PImage image,
+ float x, float y, float c, float d) {
+ image(image, x, y, c, d, 0, 0, image.width, image.height);
+ }
+
+
+ /**
+ * u, v coordinates are always based on image space location,
+ * regardless of the current textureMode().
+ */
+ public void image(PImage image,
+ float a, float b, float c, float d,
+ int u1, int v1, int u2, int v2) {
+ if (imageMode == CORNER) {
+ if (c < 0) { // reset a negative width
+ a += c; c = -c;
+ }
+ if (d < 0) { // reset a negative height
+ b += d; d = -d;
+ }
+
+ imageImpl(image,
+ a, b, a + c, b + d,
+ u1, v1, u2, v2);
+
+ } else if (imageMode == CORNERS) {
+ if (c < a) { // reverse because x2 < x1
+ float temp = a; a = c; c = temp;
+ }
+ if (d < b) { // reverse because y2 < y1
+ float temp = b; b = d; d = temp;
+ }
+
+ imageImpl(image,
+ a, b, c, d,
+ u1, v1, u2, v2);
+ }
+ }
+
+
+ /**
+ * Expects x1, y1, x2, y2 coordinates where (x2 >= x1) and (y2 >= y1).
+ * If tint() has been called, the image will be colored.
+ */
+ protected void imageImpl(PImage image,
+ float x1, float y1, float x2, float y2,
+ int u1, int v1, int u2, int v2) {
+ // TODO blit an image to the screen
+ System.err.println("unimplemented imageImpl() in PGraphics");
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // TEXT/FONTS
+
+
+ /**
+ * Sets the alignment of the text to one of LEFT, CENTER, or RIGHT.
+ */
+ public void textAlign(int align) {
+ textAlign = align;
+ }
+
+
+ /**
+ * Returns the ascent of the current font at the current size.
+ * This is a method, rather than a variable inside the PGraphics object
+ * because it requires calculation.
+ */
+ public float textAscent() {
+ if (textFont == null) {
+ throw new RuntimeException("use textFont() before textAscent()");
+ }
+
+ return textFont.ascent() *
+ ((textMode == SCREEN) ? textFont.size : textSize);
+ }
+
+
+ /**
+ * Returns the descent of the current font at the current size.
+ * This is a method, rather than a variable inside the PGraphics object
+ * because it requires calculation.
+ */
+ public float textDescent() {
+ if (textFont != null) {
+ return textFont.descent() *
+ ((textMode == SCREEN) ? textFont.size : textSize);
+
+ } else {
+ throw new RuntimeException("use textFont() before textDescent()");
+ }
+ }
+
+
+ /**
+ * Sets the current font. The font's size will be the "natural"
+ * size of this font (the size that was set when using "Create Font").
+ * The leading will also be reset.
+ */
+ public void textFont(PFont which) {
+ if (which != null) {
+ textFont = which;
+ textFontNative = which.font;
+ //textFontNativeMetrics = null;
+ // changed for rev 0104 for textMode(SHAPE) in opengl
+ if (textFontNative != null) {
+ textFontNativeMetrics =
+ Toolkit.getDefaultToolkit().getFontMetrics(textFontNative);
+ }
+ textSize(which.size);
+
+ } else {
+ throw new RuntimeException("a null PFont was passed to textFont()");
+ }
+ }
+
+
+ /**
+ * Useful function to set the font and size at the same time.
+ */
+ public void textFont(PFont which, float size) {
+ textFont(which);
+ textSize(size);
+ }
+
+
+ /**
+ * Set the text leading to a specific value. If using a custom
+ * value for the text leading, you'll have to call textLeading()
+ * again after any calls to textSize().
+ */
+ public void textLeading(float leading) {
+ textLeading = leading;
+ }
+
+
+ /**
+ * Sets the text rendering/placement to be either SCREEN (direct
+ * to the screen, exact coordinates, only use the font's original size)
+ * or MODEL (the default, where text is manipulated by translate() and
+ * can have a textSize). The text size cannot be set when using
+ * textMode(SCREEN), because it uses the pixels directly from the font.
+ */
+ public void textMode(int mode) {
+ // CENTER and MODEL overlap (they're both 3)
+ if ((mode == LEFT) || (mode == RIGHT)) {
+ throw new RuntimeException("textMode() is now textAlign() " +
+ "in Processing beta");
+ }
+ if ((mode != SCREEN) && (mode != MODEL)) {
+ throw new RuntimeException("Only textMode(SCREEN) or textMode(MODEL) " +
+ "are available with this renderer.");
+ }
+
+ //if (textFont != null) {
+ textMode = mode;
+
+ // reset the font to its natural size
+ // (helps with width calculations and all that)
+ //if (textMode == SCREEN) {
+ //textSize(textFont.size);
+ //}
+
+ //} else {
+ //throw new RuntimeException("use textFont() before textMode()");
+ //}
+ }
+
+
+ /**
+ * Sets the text size, also resets the value for the leading.
+ */
+ public void textSize(float size) {
+ if (textFont != null) {
+ if ((textMode == SCREEN) && (size != textFont.size)) {
+ throw new RuntimeException("textSize() cannot be used with " +
+ "textMode(SCREEN)");
+ }
+ textSize = size;
+ //textLeading = textSize *
+ // ((textFont.ascent() + textFont.descent()) * 1.275f);
+ textLeading = (textAscent() + textDescent()) * 1.275f;
+
+ } else {
+ throw new RuntimeException("Use textFont() before textSize()");
+ }
+ }
+
+
+ // ........................................................
+
+
+ public float textWidth(char c) {
+ textBuffer[0] = c;
+ return textWidthImpl(textBuffer, 0, 1);
+ }
+
+
+ /**
+ * Return the width of a line of text. If the text has multiple
+ * lines, this returns the length of the longest line.
+ */
+ public float textWidth(String str) {
+ if (textFont == null) {
+ throw new RuntimeException("use textFont() before textWidth()");
+ }
+
+ int length = str.length();
+ if (length > textWidthBuffer.length) {
+ textWidthBuffer = new char[length + 10];
+ }
+ str.getChars(0, length, textWidthBuffer, 0);
+
+ float wide = 0;
+ int index = 0;
+ int start = 0;
+
+ while (index < length) {
+ if (textWidthBuffer[index] == '\n') {
+ wide = Math.max(wide, textWidthImpl(textWidthBuffer, start, index));
+ start = index+1;
+ }
+ index++;
+ }
+ if (start < length) {
+ wide = Math.max(wide, textWidthImpl(textWidthBuffer, start, index));
+ }
+ return wide;
+ }
+
+
+ /**
+ * Implementation of returning the text width of
+ * the chars [start, stop) in the buffer.
+ * Unlike the previous version that was inside PFont, this will
+ * return the size not of a 1 pixel font, but the actual current size.
+ */
+ protected float textWidthImpl(char buffer[], int start, int stop) {
+ float wide = 0;
+ for (int i = start; i < stop; i++) {
+ // could add kerning here, but it just ain't implemented
+ wide += textFont.width(buffer[i]) * textSize;
+ }
+ return wide;
+ }
+
+
+ // ........................................................
+
+
+ /**
+ * Write text where we just left off.
+ */
+ public void text(char c) {
+ text(c, textX, textY, textZ);
+ }
+
+
+ /**
+ * Draw a single character on screen.
+ * Extremely slow when used with textMode(SCREEN) and Java 2D,
+ * because beginPixels has to be called first and updatePixels last.
+ */
+ public void text(char c, float x, float y) {
+ if (textFont == null) {
+ throw new RuntimeException("use textFont() before text()");
+ }
+
+ if (textMode == SCREEN) beginPixels();
+
+ textBuffer[0] = c;
+ textLineImpl(textBuffer, 0, 1, x, y);
+
+ if (textMode == SCREEN) endPixels();
+ }
+
+
+ /**
+ * Draw a single character on screen (with a z coordinate)
+ */
+ public void text(char c, float x, float y, float z) {
+ if ((z != 0) && (textMode == SCREEN)) {
+ String msg = "textMode(SCREEN) cannot have a z coordinate";
+ throw new RuntimeException(msg);
+ }
+
+ if (z != 0) translate(0, 0, z); // slowness, badness
+
+ text(c, x, y);
+ textZ = z;
+
+ if (z != 0) translate(0, 0, -z);
+ }
+
+
+ /**
+ * Write text where we just left off.
+ */
+ public void text(String str) {
+ text(str, textX, textY, textZ);
+ }
+
+
+ /**
+ * Draw a chunk of text.
+ * Newlines that are \n (Unix newline or linefeed char, ascii 10)
+ * are honored, but \r (carriage return, Windows and Mac OS) are
+ * ignored.
+ */
+ public void text(String str, float x, float y) {
+ if (textFont == null) {
+ throw new RuntimeException("use textFont() before text()");
+ }
+
+ if (textMode == SCREEN) beginPixels();
+
+ int length = str.length();
+ if (length > textBuffer.length) {
+ textBuffer = new char[length + 10];
+ }
+ str.getChars(0, length, textBuffer, 0);
+
+ int start = 0;
+ int index = 0;
+ while (index < length) {
+ if (textBuffer[index] == '\n') {
+ textLineImpl(textBuffer, start, index, x, y);
+ start = index + 1;
+ y += textLeading;
+ }
+ index++;
+ }
+ if (start < length) {
+ textLineImpl(textBuffer, start, index, x, y);
+ }
+ if (textMode == SCREEN) endPixels();
+ }
+
+
+ /**
+ * Same as above but with a z coordinate.
+ */
+ public void text(String str, float x, float y, float z) {
+ if ((z != 0) && (textMode == SCREEN)) {
+ String msg = "textMode(SCREEN) cannot have a z coordinate";
+ throw new RuntimeException(msg);
+ }
+
+ if (z != 0) translate(0, 0, z); // slow!
+
+ text(str, x, y);
+ textZ = z;
+
+ if (z != 0) translate(0, 0, -z);
+ }
+
+
+ /**
+ * Handles placement of a text line, then calls textLinePlaced
+ * to actually render at the specific point.
+ */
+ protected void textLineImpl(char buffer[], int start, int stop,
+ float x, float y) {
+ if (textAlign == CENTER) {
+ x -= textWidthImpl(buffer, start, stop) / 2f;
+
+ } else if (textAlign == RIGHT) {
+ x -= textWidthImpl(buffer, start, stop);
+ }
+ textLinePlacedImpl(buffer, start, stop, x, y);
+ }
+
+
+ protected void textLinePlacedImpl(char buffer[], int start, int stop,
+ float x, float y) {
+ for (int index = start; index < stop; index++) {
+ textCharImpl(buffer[index], x, y); //, 0); //z);
+
+ // this doesn't account for kerning
+ x += textWidth(buffer[index]);
+ }
+ textX = x;
+ textY = y;
+ textZ = 0; // this will get set by the caller if non-zero
+ }
+
+
+ /**
+ * Draw text in a box that is constrained to a particular size.
+ * The current rectMode() determines what the coordinates mean
+ * (whether x1/y1/x2/y2 or x/y/w/h).
+ *
+ * Given an (x, y, z) coordinate, returns the x position of where
+ * that point would be placed on screen, once affected by translate(),
+ * scale(), or any other transformations.
+ */
+ public float screenX(float x, float y, float z) {
+ depthErrorXYZ("screenX");
+ return 0;
+ }
+
+
+ /**
+ * Maps a three dimensional point to its placement on-screen.
+ *
+ * Given an (x, y, z) coordinate, returns the y position of where
+ * that point would be placed on screen, once affected by translate(),
+ * scale(), or any other transformations.
+ */
+ public float screenY(float x, float y, float z) {
+ depthErrorXYZ("screenY");
+ return 0;
+ }
+
+
+ /**
+ * Maps a three dimensional point to its placement on-screen.
+ *
+ * Given an (x, y, z) coordinate, returns its z value.
+ * This value can be used to determine if an (x, y, z) coordinate
+ * is in front or in back of another (x, y, z) coordinate.
+ * The units are based on how the zbuffer is set up, and don't
+ * relate to anything "real". They're only useful for in
+ * comparison to another value obtained from screenZ(),
+ * or directly out of the zbuffer[].
+ */
+ public float screenZ(float x, float y, float z) {
+ depthErrorXYZ("screenZ");
+ return 0;
+ }
+
+
+ /**
+ * Returns the model space x value for an x, y, z coordinate.
+ *
+ * This will give you a coordinate after it has been transformed
+ * by translate(), rotate(), and camera(), but not yet transformed
+ * by the projection matrix. For instance, his can be useful for
+ * figuring out how points in 3D space relate to the edge
+ * coordinates of a shape.
+ */
+ public float modelX(float x, float y, float z) {
+ depthError("modelX");
+ return 0;
+ }
+
+
+ /**
+ * Returns the model space y value for an x, y, z coordinate.
+ */
+ public float modelY(float x, float y, float z) {
+ depthError("modelY");
+ return 0;
+ }
+
+
+ /**
+ * Returns the model space z value for an x, y, z coordinate.
+ */
+ public float modelZ(float x, float y, float z) {
+ depthError("modelZ");
+ return 0;
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // COLOR
+
+
+ public void colorMode(int mode) {
+ colorMode(mode, colorModeX, colorModeY, colorModeZ, colorModeA);
+ }
+
+
+ public void colorMode(int mode, float max) {
+ colorMode(mode, max, max, max, max);
+ }
+
+
+ /**
+ * Set the colorMode and the maximum values for (r, g, b)
+ * or (h, s, b).
+ *
+ * Note that this doesn't set the maximum for the alpha value,
+ * which might be confusing if for instance you switched to
+ *
+ * Handled here with its own function since this is indepenent
+ * of the color mode.
+ *
+ * Strangely the old version of this code ignored the alpha
+ * value. not sure if that was a bug or what.
+ *
+ * Note, no need for a bounds check since it's a 32 bit number.
+ */
+ protected void colorCalcARGB(int argb, float alpha) {
+ calcColor = argb;
+ if (alpha == colorModeA) {
+ calcAi = (argb >> 24) & 0xff;
+ } else {
+ calcAi = (int) (((argb >> 24) & 0xff) * (alpha / colorModeA));
+ }
+ calcRi = (argb >> 16) & 0xff;
+ calcGi = (argb >> 8) & 0xff;
+ calcBi = argb & 0xff;
+ calcA = (float)calcAi / 255.0f;
+ calcR = (float)calcRi / 255.0f;
+ calcG = (float)calcGi / 255.0f;
+ calcB = (float)calcBi / 255.0f;
+ calcAlpha = (calcAi != 255);
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public void strokeWeight(float weight) {
+ strokeWeight = weight;
+ }
+
+
+ public void strokeJoin(int join) {
+ strokeJoin = join;
+ }
+
+
+ public void strokeCap(int cap) {
+ strokeCap = cap;
+ }
+
+
+ public void noStroke() {
+ stroke = false;
+ }
+
+
+ /**
+ * Set the tint to either a grayscale or ARGB value.
+ * See notes attached to the fill() function.
+ */
+ public void stroke(int rgb) {
+ if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
+ stroke((float) rgb);
+
+ } else {
+ colorCalcARGB(rgb, colorModeA);
+ strokeFromCalc();
+ }
+ }
+
+
+ public void stroke(int rgb, float alpha) {
+ if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
+ stroke((float) rgb, alpha);
+
+ } else {
+ colorCalcARGB(rgb, alpha);
+ strokeFromCalc();
+ }
+ }
+
+
+ public void stroke(float gray) {
+ colorCalc(gray);
+ strokeFromCalc();
+ }
+
+
+ public void stroke(float gray, float alpha) {
+ colorCalc(gray, alpha);
+ strokeFromCalc();
+ }
+
+
+ public void stroke(float x, float y, float z) {
+ colorCalc(x, y, z);
+ strokeFromCalc();
+ }
+
+
+ public void stroke(float x, float y, float z, float a) {
+ colorCalc(x, y, z, a);
+ strokeFromCalc();
+ }
+
+
+ protected void strokeFromCalc() {
+ stroke = true;
+ //strokeChanged = true;
+ strokeR = calcR;
+ strokeG = calcG;
+ strokeB = calcB;
+ strokeA = calcA;
+ strokeRi = calcRi;
+ strokeGi = calcGi;
+ strokeBi = calcBi;
+ strokeAi = calcAi;
+ strokeColor = calcColor;
+ strokeAlpha = calcAlpha;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public void noTint() {
+ tint = false;
+ }
+
+
+ /**
+ * Set the tint to either a grayscale or ARGB value. See notes
+ * attached to the fill() function.
+ */
+ public void tint(int rgb) {
+ if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
+ tint((float) rgb);
+
+ } else {
+ colorCalcARGB(rgb, colorModeA);
+ tintFromCalc();
+ }
+ }
+
+ public void tint(int rgb, float alpha) {
+ if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
+ tint((float) rgb, alpha);
+
+ } else {
+ colorCalcARGB(rgb, alpha);
+ tintFromCalc();
+ }
+ }
+
+ public void tint(float gray) {
+ colorCalc(gray);
+ tintFromCalc();
+ }
+
+
+ public void tint(float gray, float alpha) {
+ colorCalc(gray, alpha);
+ tintFromCalc();
+ }
+
+
+ public void tint(float x, float y, float z) {
+ colorCalc(x, y, z);
+ tintFromCalc();
+ }
+
+
+ public void tint(float x, float y, float z, float a) {
+ colorCalc(x, y, z, a);
+ tintFromCalc();
+ }
+
+
+ protected void tintFromCalc() {
+ tint = true;
+ tintR = calcR;
+ tintG = calcG;
+ tintB = calcB;
+ tintA = calcA;
+ tintRi = calcRi;
+ tintGi = calcGi;
+ tintBi = calcBi;
+ tintAi = calcAi;
+ tintColor = calcColor;
+ tintAlpha = calcAlpha;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public void noFill() {
+ fill = false;
+ }
+
+
+ /**
+ * Set the fill to either a grayscale value or an ARGB int.
+ *
+ * The problem with this code is that it has to detect between
+ * these two situations automatically. This is done by checking
+ * to see if the high bits (the alpha for 0xAA000000) is set,
+ * and if not, whether the color value that follows is less than
+ * colorModeX (the first param passed to colorMode).
+ *
+ * This auto-detect would break in the following situation:
+ *
+ * Note that background() should be called before any
+ * transformations occur, because some implementations may
+ * require the current transformation matrix to be identity
+ * before drawing.
+ */
+ public void background(int rgb) {
+ if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
+ background((float) rgb);
+
+ } else {
+ colorCalcARGB(rgb, colorModeA);
+ backgroundFromCalc();
+ }
+ clear();
+ }
+
+
+ /**
+ * Set the background to a grayscale value, based on the
+ * current colorMode.
+ */
+ public void background(float gray) {
+ colorCalc(gray);
+ backgroundFromCalc();
+ clear();
+ }
+
+
+ /**
+ * Set the background to an r, g, b or h, s, b value,
+ * based on the current colorMode.
+ */
+ public void background(float x, float y, float z) {
+ colorCalc(x, y, z);
+ backgroundFromCalc();
+ clear();
+ }
+
+
+ protected void backgroundFromCalc() {
+ backgroundR = calcR;
+ backgroundG = calcG;
+ backgroundB = calcB;
+ backgroundRi = calcRi;
+ backgroundGi = calcGi;
+ backgroundBi = calcBi;
+ backgroundColor = calcColor;
+ }
+
+
+ /**
+ * Takes an RGB or ARGB image and sets it as the background.
+ *
+ * Note that even if the image is set as RGB, the high 8 bits of
+ * each pixel should be set opaque (0xFF000000), because the image data
+ * will be copied directly to the screen, and non-opaque background
+ * images may have strange behavior. Using image.filter(OPAQUE)
+ * will handle this easily.
+ *
+ * When using 3D, this will also clear out the zbuffer and
+ * stencil buffer if they exist.
+ */
+ public void background(PImage image) {
+ if ((image.width != width) || (image.height != height)) {
+ throw new RuntimeException("background image must be " +
+ "the same size as your application");
+ }
+ if ((image.format != RGB) && (image.format != ARGB)) {
+ throw new RuntimeException("background images should be RGB or ARGB");
+ }
+
+ // zero this out since it's an image
+ backgroundColor = 0;
+
+ // blit image to the screen
+ System.arraycopy(image.pixels, 0, pixels, 0, pixels.length);
+ }
+
+
+ /**
+ * Clears pixel buffer.
+ *
+ * Subclasses (PGraphics3) will also clear the
+ * stencil and zbuffer if they exist.
+ */
+ protected void clear() {
+ for (int i = 0; i < pixelCount; i++) {
+ pixels[i] = backgroundColor;
+ }
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // MESSAGES / ERRORS / LOGGING
+
+
+ protected void depthError(String method) {
+ throw new RuntimeException(method + "() can only be used " +
+ "with P3D or OPENGL.");
+ }
+
+ protected void depthErrorXYZ(String method) {
+ throw new RuntimeException(method + "(x, y, z) can only be used with " +
+ "OPENGL or P3D, use " +
+ method + "(x, y) instead.");
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // COLOR MANIPULATION
+
+ // these functions are really slow, but easy to use
+ // if folks are advanced enough to want something faster,
+ // they can write it themselves (not difficult)
+
+
+ public final int color(int gray) { // ignore
+ if (((gray & 0xff000000) == 0) && (gray <= colorModeX)) {
+ if (colorRgb255) {
+ // bounds checking to make sure the numbers aren't to high or low
+ if (gray > 255) gray = 255; else if (gray < 0) gray = 0;
+ return 0xff000000 | (gray << 16) | (gray << 8) | gray;
+ } else {
+ colorCalc(gray);
+ }
+ } else {
+ colorCalcARGB(gray, colorModeA);
+ }
+ return calcColor;
+ }
+
+ public final int color(float gray) { // ignore
+ colorCalc(gray);
+ return calcColor;
+ }
+
+
+ /**
+ * @param gray can be packed ARGB or a gray in this case
+ */
+ public final int color(int gray, int alpha) { // ignore
+ if (colorRgb255) {
+ // bounds checking to make sure the numbers aren't to high or low
+ if (gray > 255) gray = 255; else if (gray < 0) gray = 0;
+ if (alpha > 255) alpha = 255; else if (alpha < 0) alpha = 0;
+
+ return ((alpha & 0xff) << 24) | (gray << 16) | (gray << 8) | gray;
+ }
+ colorCalc(gray, alpha);
+ return calcColor;
+ }
+
+ /**
+ * @param rgb can be packed ARGB or a gray in this case
+ */
+ public final int color(int rgb, float alpha) { // ignore
+ if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
+ colorCalc(rgb, alpha);
+ } else {
+ colorCalcARGB(rgb, alpha);
+ }
+ return calcColor;
+ }
+
+ public final int color(float gray, float alpha) { // ignore
+ colorCalc(gray, alpha);
+ return calcColor;
+ }
+
+
+ public final int color(int x, int y, int z) { // ignore
+ if (colorRgb255) {
+ // bounds checking to make sure the numbers aren't to high or low
+ 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;
+ }
+ colorCalc(x, y, z);
+ return calcColor;
+ }
+
+ public final int color(float x, float y, float z) { // ignore
+ colorCalc(x, y, z);
+ return calcColor;
+ }
+
+
+ public final int color(int x, int y, int z, int a) { // ignore
+ if (colorRgb255) {
+ // bounds checking to make sure the numbers aren't to high or low
+ 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;
+ }
+ colorCalc(x, y, z, a);
+ return calcColor;
+ }
+
+ public final int color(float x, float y, float z, float a) { // ignore
+ colorCalc(x, y, z, a);
+ return calcColor;
+ }
+
+
+ public final float alpha(int what) {
+ float c = (what >> 24) & 0xff;
+ if (colorModeA == 255) return c;
+ return (c / 255.0f) * colorModeA;
+ }
+
+ public final float red(int what) {
+ float c = (what >> 16) & 0xff;
+ if (colorRgb255) return c;
+ return (c / 255.0f) * colorModeX;
+ }
+
+ public final float green(int what) {
+ float c = (what >> 8) & 0xff;
+ if (colorRgb255) return c;
+ return (c / 255.0f) * colorModeY;
+ }
+
+ public final float blue(int what) {
+ float c = (what) & 0xff;
+ if (colorRgb255) return c;
+ return (c / 255.0f) * colorModeZ;
+ }
+
+
+ public final float hue(int what) {
+ if (what != cacheHsbKey) {
+ Color.RGBtoHSB((what >> 16) & 0xff, (what >> 8) & 0xff,
+ what & 0xff, cacheHsbValue);
+ cacheHsbKey = what;
+ }
+ return cacheHsbValue[0] * colorModeX;
+ }
+
+ public final float saturation(int what) {
+ if (what != cacheHsbKey) {
+ Color.RGBtoHSB((what >> 16) & 0xff, (what >> 8) & 0xff,
+ what & 0xff, cacheHsbValue);
+ cacheHsbKey = what;
+ }
+ return cacheHsbValue[1] * colorModeY;
+ }
+
+ public final float brightness(int what) {
+ if (what != cacheHsbKey) {
+ Color.RGBtoHSB((what >> 16) & 0xff, (what >> 8) & 0xff,
+ what & 0xff, cacheHsbValue);
+ cacheHsbKey = what;
+ }
+ return cacheHsbValue[2] * colorModeZ;
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // PATH
+
+ class Path {
+
+ public void moveTo(float x, float y) { // ignore
+ }
+
+ public void lineTo(float x, float y) { // ignore
+ }
+
+ public void curveTo(float x1, float y1, // ignore
+ float x2, float y2,
+ float x3, float y3) {
+ }
+
+ public void closePath() { // ignore
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ /**
+ * Use with caution on PGraphics. This should not be used with
+ * the base PGraphics that's tied to a PApplet, but it can be used
+ * with user-created PGraphics objects that are drawn to the screen.
+ */
+ public void mask(int alpha[]) { // ignore
+ super.mask(alpha);
+ }
+
+
+ /**
+ * Use with caution on PGraphics. This should not be used with
+ * the base PGraphics that's tied to a PApplet, but it can be used
+ * with user-created PGraphics objects that are drawn to the screen.
+ */
+ public void mask(PImage alpha) { // ignore
+ super.mask(alpha);
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public void beginRaw(PGraphics raw) {
+ this.raw = raw;
+ raw.beginDraw();
+ }
+
+
+ public void endRaw() {
+ if (raw != null) {
+ // for 3D, need to flush any geometry that's been stored for sorting
+ raw.flush();
+
+ // just like beginDraw, this will have to be called because
+ // endDraw() will be happening outside of draw()
+ raw.endDraw();
+ raw.dispose();
+ raw = null;
+ }
+ }
+
+
+ /**
+ * Handle any takedown for this graphics context.
+ *
+ * This is called when a sketch is shut down and this renderer was
+ * specified using the size() command, or inside endRecord() and
+ * endRaw(), in order to shut things off.
+ */
+ public void dispose() { // ignore
+ }
+
+
+ /**
+ * Return true if this renderer should be drawn to the screen.
+ * Overridden for subclasses like PDF so that an enormous window
+ * doesn't open up.
+ * showFrame, displayable, isVisible, visible, shouldDisplay,
+ * what to call this?
+ */
+ public boolean displayable() {
+ return true;
+ }
+}
diff --git a/core/PGraphics3.java b/core/src/processing/core/PGraphics3.java
similarity index 96%
rename from core/PGraphics3.java
rename to core/src/processing/core/PGraphics3.java
index b51c2e1e2..0182d4923 100644
--- a/core/PGraphics3.java
+++ b/core/src/processing/core/PGraphics3.java
@@ -1,4008 +1,4008 @@
-/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
-
-/*
- Part of the Processing project - http://processing.org
-
- Copyright (c) 2004-06 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.awt.*;
-import java.awt.image.*;
-
-
-/**
- * Subclass of PGraphics that handles 3D rendering.
- *
- * Implementation notes:
- *
- * cache all the points of the sphere in a static array
- * top and bottom are just a bunch of triangles that land
- * in the center point
- *
- * sphere is a series of concentric circles who radii vary
- * along the shape, based on, er.. cos or something
- *
- * Note that the camera matrix is *not* the perspective matrix,
- * it is in front of the modelview matrix (hence the name "model"
- * and "view" for that matrix).
- *
- * beginCamera() specifies that all coordinate transforms until endCamera()
- * should be pre-applied in inverse to the camera transform matrix.
- * Note that this is only challenging when a user specifies an arbitrary
- * matrix with applyMatrix(). Then that matrix will need to be inverted,
- * which may not be possible. But take heart, if a user is applying a
- * non-invertible matrix to the camera transform, then he is clearly
- * up to no good, and we can wash our hands of those bad intentions.
- *
- * begin/endCamera clauses do not automatically reset the camera transform
- * matrix. That's because we set up a nice default camera transform int
- * setup(), and we expect it to hold through draw(). So we don't reset
- * the camera transform matrix at the top of draw(). That means that an
- * innocuous-looking clause like
- *
- * Note that this will destroy any settings to scale(), translate(),
- * or whatever, because the final camera matrix will be copied
- * (not multiplied) into the modelview.
- */
- public void endCamera() {
- if (!manipulatingCamera) {
- throw new RuntimeException("Cannot call endCamera() " +
- "without first calling beginCamera()");
- }
- // reset the modelview to use this new camera matrix
- modelview.set(camera);
- modelviewInv.set(cameraInv);
-
- // set matrix mode back to modelview
- forwardTransform = modelview;
- reverseTransform = modelviewInv;
-
- // all done
- manipulatingCamera = false;
- }
-
-
- /**
- * Set camera to the default settings.
- *
- * Processing camera behavior:
- *
- * Camera behavior can be split into two separate components, camera
- * transformation, and projection. The transformation corresponds to the
- * physical location, orientation, and scale of the camera. In a physical
- * camera metaphor, this is what can manipulated by handling the camera
- * body (with the exception of scale, which doesn't really have a physcial
- * analog). The projection corresponds to what can be changed by
- * manipulating the lens.
- *
- * We maintain separate matrices to represent the camera transform and
- * projection. An important distinction between the two is that the camera
- * transform should be invertible, where the projection matrix should not,
- * since it serves to map three dimensions to two. It is possible to bake
- * the two matrices into a single one just by multiplying them together,
- * but it isn't a good idea, since lighting, z-ordering, and z-buffering
- * all demand a true camera z coordinate after modelview and camera
- * transforms have been applied but before projection. If the camera
- * transform and projection are combined there is no way to recover a
- * good camera-space z-coordinate from a model coordinate.
- *
- * Fortunately, there are no functions that manipulate both camera
- * transformation and projection.
- *
- * camera() sets the camera position, orientation, and center of the scene.
- * It replaces the camera transform with a new one. This is different from
- * gluLookAt(), but I think the only reason that GLU's lookat doesn't fully
- * replace the camera matrix with the new one, but instead multiplies it,
- * is that GL doesn't enforce the separation of camera transform and
- * projection, so it wouldn't be safe (you'd probably stomp your projection).
- *
- * The transformation functions are the same ones used to manipulate the
- * modelview matrix (scale, translate, rotate, etc.). But they are bracketed
- * with beginCamera(), endCamera() to indicate that they should apply
- * (in inverse), to the camera transformation matrix.
- *
- * This differs considerably from camera transformation in OpenGL.
- * OpenGL only lets you say, apply everything from here out to the
- * projection or modelview matrix. This makes it very hard to treat camera
- * manipulation as if it were a physical camera. Imagine that you want to
- * move your camera 100 units forward. In OpenGL, you need to apply the
- * inverse of that transformation or else you'll move your scene 100 units
- * forward--whether or not you've specified modelview or projection matrix.
- * Remember they're just multiplied by model coods one after another.
- * So in order to treat a camera like a physical camera, it is necessary
- * to pre-apply inverse transforms to a matrix that will be applied to model
- * coordinates. OpenGL provides nothing of this sort, but Processing does!
- * This is the camera transform matrix.
- */
- public void camera() {
- camera(cameraX, cameraY, cameraZ,
- cameraX, cameraY, 0,
- 0, 1, 0);
- }
-
-
- /**
- * More flexible method for dealing with camera().
- *
- * The actual call is like gluLookat. Here's the real skinny on
- * what does what:
- *
- * Now, beginCamera(); and endCamera(); are useful if you want to move
- * the camera around using transforms like translate(), etc. They will
- * wipe out any coordinate system transforms that occur before them in
- * draw(), but they will not automatically wipe out the camera transform.
- * This means that they should be at the top of draw(). It also means
- * that the following:
- *
- * Implementation partially based on Mesa's matrix.c.
- */
- public void ortho(float left, float right,
- float bottom, float top,
- float near, float far) {
- float x = 2.0f / (right - left);
- float y = 2.0f / (top - bottom);
- float z = -2.0f / (far - near);
-
- float tx = -(right + left) / (right - left);
- float ty = -(top + bottom) / (top - bottom);
- float tz = -(far + near) / (far - near);
-
- projection.set(x, 0, 0, tx,
- 0, y, 0, ty,
- 0, 0, z, tz,
- 0, 0, 0, 1);
- }
-
-
- /**
- * Calls perspective() with Processing's standard coordinate projection.
- *
- * Projection functions:
- *
- * This behavior is pretty much familiar from OpenGL, except where
- * functions replace matrices, rather than multiplying against the
- * previous.
- *
- */
- public void perspective() {
- perspective(cameraFOV, cameraAspect, cameraNear, cameraFar);
- }
-
-
- /**
- * Similar to gluPerspective(). Implementation based on Mesa's glu.c
- */
- public void perspective(float fov, float aspect, float zNear, float zFar) {
- //float ymax = zNear * tan(fovy * PI / 360.0f);
- float ymax = zNear * tan(fov / 2.0f);
- float ymin = -ymax;
-
- float xmin = ymin * aspect;
- float xmax = ymax * aspect;
-
- frustum(xmin, xmax, ymin, ymax, zNear, zFar);
- }
-
-
- /**
- * Same as glFrustum(), except that it wipes out (rather than
- * multiplies against) the current perspective matrix.
- *
- * Implementation based on the explanation in the OpenGL blue book.
- */
- public void frustum(float left, float right, float bottom,
- float top, float znear, float zfar) {
- //System.out.println(projection);
- projection.set((2*znear)/(right-left), 0, (right+left)/(right-left), 0,
- 0, (2*znear)/(top-bottom), (top+bottom)/(top-bottom), 0,
- 0, 0, -(zfar+znear)/(zfar-znear),-(2*zfar*znear)/(zfar-znear),
- 0, 0, -1, 0);
- }
-
-
- /**
- * Print the current projection matrix.
- */
- public void printProjection() {
- projection.print();
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // SCREEN AND OBJECT COORDINATES
-
-
- public float screenX(float x, float y) {
- return screenX(x, y, 0);
- }
-
-
- public float screenY(float x, float y) {
- return screenY(x, y, 0);
- }
-
-
- public float screenX(float x, float y, float z) {
- float ax =
- modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
- float ay =
- modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
- float az =
- modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
- float aw =
- modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
-
- float ox =
- projection.m00*ax + projection.m01*ay +
- projection.m02*az + projection.m03*aw;
- float ow =
- projection.m30*ax + projection.m31*ay +
- projection.m32*az + projection.m33*aw;
-
- if (ow != 0) ox /= ow;
- return width * (1 + ox) / 2.0f;
- }
-
-
- public float screenY(float x, float y, float z) {
- float ax =
- modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
- float ay =
- modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
- float az =
- modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
- float aw =
- modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
-
- float oy =
- projection.m10*ax + projection.m11*ay +
- projection.m12*az + projection.m13*aw;
- float ow =
- projection.m30*ax + projection.m31*ay +
- projection.m32*az + projection.m33*aw;
-
- if (ow != 0) oy /= ow;
- return height * (1 + oy) / 2.0f;
- }
-
-
- public float screenZ(float x, float y, float z) {
- float ax =
- modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
- float ay =
- modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
- float az =
- modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
- float aw =
- modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
-
- float oz =
- projection.m20*ax + projection.m21*ay +
- projection.m22*az + projection.m23*aw;
- float ow =
- projection.m30*ax + projection.m31*ay +
- projection.m32*az + projection.m33*aw;
-
- if (ow != 0) oz /= ow;
- return (oz + 1) / 2.0f;
- }
-
-
- public float modelX(float x, float y, float z) {
- float ax =
- modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
- float aw =
- modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
- return (aw != 0) ? ax / aw : ax;
- }
-
-
- public float modelY(float x, float y, float z) {
- float ay =
- modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
- float aw =
- modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
- return (aw != 0) ? ay / aw : ay;
- }
-
-
- public float modelZ(float x, float y, float z) {
- float az =
- modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
- float aw =
- modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
- return (aw != 0) ? az / aw : az;
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
-
- // strokeWeight() doesn't really work properly either,
- // but that will be dealt with in some other way.
-
-
- public void strokeJoin(int join) {
- String msg = "strokeJoin() not available with P3D";
- throw new RuntimeException(msg);
- }
-
-
- public void strokeCap(int cap) {
- String msg = "strokeCap() not available with P3D";
- throw new RuntimeException(msg);
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
-
- protected void fillFromCalc() {
- super.fillFromCalc();
- ambientFromCalc();
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public void ambient(int rgb) {
- if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
- ambient((float) rgb);
-
- } else {
- colorCalcARGB(rgb, colorModeA);
- ambientFromCalc();
- }
- }
-
-
- public void ambient(float gray) {
- colorCalc(gray);
- ambientFromCalc();
- }
-
-
- public void ambient(float x, float y, float z) {
- colorCalc(x, y, z);
- ambientFromCalc();
- }
-
-
- protected void ambientFromCalc() {
- ambientR = calcR;
- ambientG = calcG;
- ambientB = calcB;
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public void specular(int rgb) {
- if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
- specular((float) rgb);
-
- } else {
- colorCalcARGB(rgb, colorModeA);
- specularFromCalc();
- }
- }
-
-
- public void specular(float gray) {
- colorCalc(gray);
- specularFromCalc();
- }
-
-
- public void specular(float gray, float alpha) {
- colorCalc(gray, alpha);
- specularFromCalc();
- }
-
-
- public void specular(float x, float y, float z) {
- colorCalc(x, y, z);
- specularFromCalc();
- }
-
-
- public void specular(float x, float y, float z, float a) {
- colorCalc(x, y, z, a);
- specularFromCalc();
- }
-
-
- protected void specularFromCalc() {
- specularR = calcR;
- specularG = calcG;
- specularB = calcB;
- specularA = calcA;
- //specularRi = calcRi;
- //specularGi = calcGi;
- //specularBi = calcBi;
- //specularAi = calcAi;
- }
-
-
- public void shininess(float shine) {
- shininess = shine;
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public void emissive(int rgb) {
- if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
- emissive((float) rgb);
-
- } else {
- colorCalcARGB(rgb, colorModeA);
- emissiveFromCalc();
- }
- }
-
-
- public void emissive(float gray) {
- colorCalc(gray);
- emissiveFromCalc();
- }
-
-
- public void emissive(float x, float y, float z) {
- colorCalc(x, y, z);
- emissiveFromCalc();
- }
-
-
- protected void emissiveFromCalc() {
- emissiveR = calcR;
- emissiveG = calcG;
- emissiveB = calcB;
- //emissiveRi = calcRi;
- //emissiveGi = calcGi;
- //emissiveBi = calcBi;
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- /**
- * Sets up an ambient and directional light.
- *
- * Note that even if the image is set as RGB, the high 8 bits of
- * each pixel must be set (0xFF000000), because the image data will
- * be copied directly to the screen.
- *
- * Also clears out the zbuffer and stencil buffer if they exist.
- */
- public void background(PImage image) {
- super.background(image);
-
- for (int i = 0; i < pixelCount; i++) {
- zbuffer[i] = MAX_FLOAT;
- stencil[i] = 0;
- }
- }
-
-
- /**
- * Clears pixel buffer.
- *
- * With P3D and OPENGL, this also clears the stencil and zbuffer.
- */
- public void clear() {
- //System.out.println("PGraphics3.clear(" +
- // PApplet.hex(backgroundColor) + ")");
- for (int i = 0; i < pixelCount; i++) {
- pixels[i] = backgroundColor;
- zbuffer[i] = MAX_FLOAT;
- stencil[i] = 0;
- }
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // SMOOTH (not available, throws error)
-
- // although should this bother throwing an error?
- // could be a pain in the ass when trying to debug with opengl
-
-
- public void smooth() {
- String msg = "smooth() not available with P3D";
- throw new RuntimeException(msg);
- }
-
-
- public void noSmooth() {
- String msg = "noSmooth() not available with P3D";
- throw new RuntimeException(msg);
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // MATH (internal use only)
-
-
- /*
- private final float mag(float a, float b) {
- return (float)Math.sqrt(a*a + b*b);
- }
- */
-
- private final float mag(float a, float b, float c) {
- return (float)Math.sqrt(a*a + b*b + c*c);
- }
-
- private final float mag(float abc[]) {
- return (float)Math.sqrt(abc[0]*abc[0] + abc[1]*abc[1] + abc[2]*abc[2]);
- }
-
- private final float min(float a, float b) {
- return (a < b) ? a : b;
- }
-
- private final float max(float a, float b) {
- return (a > b) ? a : b;
- }
-
- /*
- private final float max(float a, float b, float c) {
- return Math.max(a, Math.max(b, c));
- }
-
- private final float sq(float a) {
- return a*a;
- }
- */
-
- private final float sqrt(float a) {
- return (float)Math.sqrt(a);
- }
-
- private final float pow(float a, float b) {
- return (float)Math.pow(a, b);
- }
-
- /*
- private final float abs(float a) {
- return (a < 0) ? -a : a;
- }
-
- private final float sin(float angle) {
- return (float)Math.sin(angle);
- }
- */
-
- private final float cos(float angle) {
- return (float)Math.cos(angle);
- }
-
- private final float tan(float angle) {
- return (float)Math.tan(angle);
- }
-
- private float dot(float ax, float ay, float az,
- float bx, float by, float bz) {
- return ax * bx + ay * by + az * bz;
- }
-}
-
+/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2004-06 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.awt.*;
+import java.awt.image.*;
+
+
+/**
+ * Subclass of PGraphics that handles 3D rendering.
+ *
+ * Implementation notes:
+ *
+ * cache all the points of the sphere in a static array
+ * top and bottom are just a bunch of triangles that land
+ * in the center point
+ *
+ * sphere is a series of concentric circles who radii vary
+ * along the shape, based on, er.. cos or something
+ *
+ * Note that the camera matrix is *not* the perspective matrix,
+ * it is in front of the modelview matrix (hence the name "model"
+ * and "view" for that matrix).
+ *
+ * beginCamera() specifies that all coordinate transforms until endCamera()
+ * should be pre-applied in inverse to the camera transform matrix.
+ * Note that this is only challenging when a user specifies an arbitrary
+ * matrix with applyMatrix(). Then that matrix will need to be inverted,
+ * which may not be possible. But take heart, if a user is applying a
+ * non-invertible matrix to the camera transform, then he is clearly
+ * up to no good, and we can wash our hands of those bad intentions.
+ *
+ * begin/endCamera clauses do not automatically reset the camera transform
+ * matrix. That's because we set up a nice default camera transform int
+ * setup(), and we expect it to hold through draw(). So we don't reset
+ * the camera transform matrix at the top of draw(). That means that an
+ * innocuous-looking clause like
+ *
+ * Note that this will destroy any settings to scale(), translate(),
+ * or whatever, because the final camera matrix will be copied
+ * (not multiplied) into the modelview.
+ */
+ public void endCamera() {
+ if (!manipulatingCamera) {
+ throw new RuntimeException("Cannot call endCamera() " +
+ "without first calling beginCamera()");
+ }
+ // reset the modelview to use this new camera matrix
+ modelview.set(camera);
+ modelviewInv.set(cameraInv);
+
+ // set matrix mode back to modelview
+ forwardTransform = modelview;
+ reverseTransform = modelviewInv;
+
+ // all done
+ manipulatingCamera = false;
+ }
+
+
+ /**
+ * Set camera to the default settings.
+ *
+ * Processing camera behavior:
+ *
+ * Camera behavior can be split into two separate components, camera
+ * transformation, and projection. The transformation corresponds to the
+ * physical location, orientation, and scale of the camera. In a physical
+ * camera metaphor, this is what can manipulated by handling the camera
+ * body (with the exception of scale, which doesn't really have a physcial
+ * analog). The projection corresponds to what can be changed by
+ * manipulating the lens.
+ *
+ * We maintain separate matrices to represent the camera transform and
+ * projection. An important distinction between the two is that the camera
+ * transform should be invertible, where the projection matrix should not,
+ * since it serves to map three dimensions to two. It is possible to bake
+ * the two matrices into a single one just by multiplying them together,
+ * but it isn't a good idea, since lighting, z-ordering, and z-buffering
+ * all demand a true camera z coordinate after modelview and camera
+ * transforms have been applied but before projection. If the camera
+ * transform and projection are combined there is no way to recover a
+ * good camera-space z-coordinate from a model coordinate.
+ *
+ * Fortunately, there are no functions that manipulate both camera
+ * transformation and projection.
+ *
+ * camera() sets the camera position, orientation, and center of the scene.
+ * It replaces the camera transform with a new one. This is different from
+ * gluLookAt(), but I think the only reason that GLU's lookat doesn't fully
+ * replace the camera matrix with the new one, but instead multiplies it,
+ * is that GL doesn't enforce the separation of camera transform and
+ * projection, so it wouldn't be safe (you'd probably stomp your projection).
+ *
+ * The transformation functions are the same ones used to manipulate the
+ * modelview matrix (scale, translate, rotate, etc.). But they are bracketed
+ * with beginCamera(), endCamera() to indicate that they should apply
+ * (in inverse), to the camera transformation matrix.
+ *
+ * This differs considerably from camera transformation in OpenGL.
+ * OpenGL only lets you say, apply everything from here out to the
+ * projection or modelview matrix. This makes it very hard to treat camera
+ * manipulation as if it were a physical camera. Imagine that you want to
+ * move your camera 100 units forward. In OpenGL, you need to apply the
+ * inverse of that transformation or else you'll move your scene 100 units
+ * forward--whether or not you've specified modelview or projection matrix.
+ * Remember they're just multiplied by model coods one after another.
+ * So in order to treat a camera like a physical camera, it is necessary
+ * to pre-apply inverse transforms to a matrix that will be applied to model
+ * coordinates. OpenGL provides nothing of this sort, but Processing does!
+ * This is the camera transform matrix.
+ */
+ public void camera() {
+ camera(cameraX, cameraY, cameraZ,
+ cameraX, cameraY, 0,
+ 0, 1, 0);
+ }
+
+
+ /**
+ * More flexible method for dealing with camera().
+ *
+ * The actual call is like gluLookat. Here's the real skinny on
+ * what does what:
+ *
+ * Now, beginCamera(); and endCamera(); are useful if you want to move
+ * the camera around using transforms like translate(), etc. They will
+ * wipe out any coordinate system transforms that occur before them in
+ * draw(), but they will not automatically wipe out the camera transform.
+ * This means that they should be at the top of draw(). It also means
+ * that the following:
+ *
+ * Implementation partially based on Mesa's matrix.c.
+ */
+ public void ortho(float left, float right,
+ float bottom, float top,
+ float near, float far) {
+ float x = 2.0f / (right - left);
+ float y = 2.0f / (top - bottom);
+ float z = -2.0f / (far - near);
+
+ float tx = -(right + left) / (right - left);
+ float ty = -(top + bottom) / (top - bottom);
+ float tz = -(far + near) / (far - near);
+
+ projection.set(x, 0, 0, tx,
+ 0, y, 0, ty,
+ 0, 0, z, tz,
+ 0, 0, 0, 1);
+ }
+
+
+ /**
+ * Calls perspective() with Processing's standard coordinate projection.
+ *
+ * Projection functions:
+ *
+ * This behavior is pretty much familiar from OpenGL, except where
+ * functions replace matrices, rather than multiplying against the
+ * previous.
+ *
+ */
+ public void perspective() {
+ perspective(cameraFOV, cameraAspect, cameraNear, cameraFar);
+ }
+
+
+ /**
+ * Similar to gluPerspective(). Implementation based on Mesa's glu.c
+ */
+ public void perspective(float fov, float aspect, float zNear, float zFar) {
+ //float ymax = zNear * tan(fovy * PI / 360.0f);
+ float ymax = zNear * tan(fov / 2.0f);
+ float ymin = -ymax;
+
+ float xmin = ymin * aspect;
+ float xmax = ymax * aspect;
+
+ frustum(xmin, xmax, ymin, ymax, zNear, zFar);
+ }
+
+
+ /**
+ * Same as glFrustum(), except that it wipes out (rather than
+ * multiplies against) the current perspective matrix.
+ *
+ * Implementation based on the explanation in the OpenGL blue book.
+ */
+ public void frustum(float left, float right, float bottom,
+ float top, float znear, float zfar) {
+ //System.out.println(projection);
+ projection.set((2*znear)/(right-left), 0, (right+left)/(right-left), 0,
+ 0, (2*znear)/(top-bottom), (top+bottom)/(top-bottom), 0,
+ 0, 0, -(zfar+znear)/(zfar-znear),-(2*zfar*znear)/(zfar-znear),
+ 0, 0, -1, 0);
+ }
+
+
+ /**
+ * Print the current projection matrix.
+ */
+ public void printProjection() {
+ projection.print();
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // SCREEN AND OBJECT COORDINATES
+
+
+ public float screenX(float x, float y) {
+ return screenX(x, y, 0);
+ }
+
+
+ public float screenY(float x, float y) {
+ return screenY(x, y, 0);
+ }
+
+
+ public float screenX(float x, float y, float z) {
+ float ax =
+ modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
+ float ay =
+ modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
+ float az =
+ modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
+ float aw =
+ modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
+
+ float ox =
+ projection.m00*ax + projection.m01*ay +
+ projection.m02*az + projection.m03*aw;
+ float ow =
+ projection.m30*ax + projection.m31*ay +
+ projection.m32*az + projection.m33*aw;
+
+ if (ow != 0) ox /= ow;
+ return width * (1 + ox) / 2.0f;
+ }
+
+
+ public float screenY(float x, float y, float z) {
+ float ax =
+ modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
+ float ay =
+ modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
+ float az =
+ modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
+ float aw =
+ modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
+
+ float oy =
+ projection.m10*ax + projection.m11*ay +
+ projection.m12*az + projection.m13*aw;
+ float ow =
+ projection.m30*ax + projection.m31*ay +
+ projection.m32*az + projection.m33*aw;
+
+ if (ow != 0) oy /= ow;
+ return height * (1 + oy) / 2.0f;
+ }
+
+
+ public float screenZ(float x, float y, float z) {
+ float ax =
+ modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
+ float ay =
+ modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
+ float az =
+ modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
+ float aw =
+ modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
+
+ float oz =
+ projection.m20*ax + projection.m21*ay +
+ projection.m22*az + projection.m23*aw;
+ float ow =
+ projection.m30*ax + projection.m31*ay +
+ projection.m32*az + projection.m33*aw;
+
+ if (ow != 0) oz /= ow;
+ return (oz + 1) / 2.0f;
+ }
+
+
+ public float modelX(float x, float y, float z) {
+ float ax =
+ modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
+ float aw =
+ modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
+ return (aw != 0) ? ax / aw : ax;
+ }
+
+
+ public float modelY(float x, float y, float z) {
+ float ay =
+ modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
+ float aw =
+ modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
+ return (aw != 0) ? ay / aw : ay;
+ }
+
+
+ public float modelZ(float x, float y, float z) {
+ float az =
+ modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
+ float aw =
+ modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
+ return (aw != 0) ? az / aw : az;
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ // strokeWeight() doesn't really work properly either,
+ // but that will be dealt with in some other way.
+
+
+ public void strokeJoin(int join) {
+ String msg = "strokeJoin() not available with P3D";
+ throw new RuntimeException(msg);
+ }
+
+
+ public void strokeCap(int cap) {
+ String msg = "strokeCap() not available with P3D";
+ throw new RuntimeException(msg);
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ protected void fillFromCalc() {
+ super.fillFromCalc();
+ ambientFromCalc();
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public void ambient(int rgb) {
+ if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
+ ambient((float) rgb);
+
+ } else {
+ colorCalcARGB(rgb, colorModeA);
+ ambientFromCalc();
+ }
+ }
+
+
+ public void ambient(float gray) {
+ colorCalc(gray);
+ ambientFromCalc();
+ }
+
+
+ public void ambient(float x, float y, float z) {
+ colorCalc(x, y, z);
+ ambientFromCalc();
+ }
+
+
+ protected void ambientFromCalc() {
+ ambientR = calcR;
+ ambientG = calcG;
+ ambientB = calcB;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public void specular(int rgb) {
+ if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
+ specular((float) rgb);
+
+ } else {
+ colorCalcARGB(rgb, colorModeA);
+ specularFromCalc();
+ }
+ }
+
+
+ public void specular(float gray) {
+ colorCalc(gray);
+ specularFromCalc();
+ }
+
+
+ public void specular(float gray, float alpha) {
+ colorCalc(gray, alpha);
+ specularFromCalc();
+ }
+
+
+ public void specular(float x, float y, float z) {
+ colorCalc(x, y, z);
+ specularFromCalc();
+ }
+
+
+ public void specular(float x, float y, float z, float a) {
+ colorCalc(x, y, z, a);
+ specularFromCalc();
+ }
+
+
+ protected void specularFromCalc() {
+ specularR = calcR;
+ specularG = calcG;
+ specularB = calcB;
+ specularA = calcA;
+ //specularRi = calcRi;
+ //specularGi = calcGi;
+ //specularBi = calcBi;
+ //specularAi = calcAi;
+ }
+
+
+ public void shininess(float shine) {
+ shininess = shine;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public void emissive(int rgb) {
+ if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
+ emissive((float) rgb);
+
+ } else {
+ colorCalcARGB(rgb, colorModeA);
+ emissiveFromCalc();
+ }
+ }
+
+
+ public void emissive(float gray) {
+ colorCalc(gray);
+ emissiveFromCalc();
+ }
+
+
+ public void emissive(float x, float y, float z) {
+ colorCalc(x, y, z);
+ emissiveFromCalc();
+ }
+
+
+ protected void emissiveFromCalc() {
+ emissiveR = calcR;
+ emissiveG = calcG;
+ emissiveB = calcB;
+ //emissiveRi = calcRi;
+ //emissiveGi = calcGi;
+ //emissiveBi = calcBi;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ /**
+ * Sets up an ambient and directional light.
+ *
+ * Note that even if the image is set as RGB, the high 8 bits of
+ * each pixel must be set (0xFF000000), because the image data will
+ * be copied directly to the screen.
+ *
+ * Also clears out the zbuffer and stencil buffer if they exist.
+ */
+ public void background(PImage image) {
+ super.background(image);
+
+ for (int i = 0; i < pixelCount; i++) {
+ zbuffer[i] = MAX_FLOAT;
+ stencil[i] = 0;
+ }
+ }
+
+
+ /**
+ * Clears pixel buffer.
+ *
+ * With P3D and OPENGL, this also clears the stencil and zbuffer.
+ */
+ public void clear() {
+ //System.out.println("PGraphics3.clear(" +
+ // PApplet.hex(backgroundColor) + ")");
+ for (int i = 0; i < pixelCount; i++) {
+ pixels[i] = backgroundColor;
+ zbuffer[i] = MAX_FLOAT;
+ stencil[i] = 0;
+ }
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // SMOOTH (not available, throws error)
+
+ // although should this bother throwing an error?
+ // could be a pain in the ass when trying to debug with opengl
+
+
+ public void smooth() {
+ String msg = "smooth() not available with P3D";
+ throw new RuntimeException(msg);
+ }
+
+
+ public void noSmooth() {
+ String msg = "noSmooth() not available with P3D";
+ throw new RuntimeException(msg);
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // MATH (internal use only)
+
+
+ /*
+ private final float mag(float a, float b) {
+ return (float)Math.sqrt(a*a + b*b);
+ }
+ */
+
+ private final float mag(float a, float b, float c) {
+ return (float)Math.sqrt(a*a + b*b + c*c);
+ }
+
+ private final float mag(float abc[]) {
+ return (float)Math.sqrt(abc[0]*abc[0] + abc[1]*abc[1] + abc[2]*abc[2]);
+ }
+
+ private final float min(float a, float b) {
+ return (a < b) ? a : b;
+ }
+
+ private final float max(float a, float b) {
+ return (a > b) ? a : b;
+ }
+
+ /*
+ private final float max(float a, float b, float c) {
+ return Math.max(a, Math.max(b, c));
+ }
+
+ private final float sq(float a) {
+ return a*a;
+ }
+ */
+
+ private final float sqrt(float a) {
+ return (float)Math.sqrt(a);
+ }
+
+ private final float pow(float a, float b) {
+ return (float)Math.pow(a, b);
+ }
+
+ /*
+ private final float abs(float a) {
+ return (a < 0) ? -a : a;
+ }
+
+ private final float sin(float angle) {
+ return (float)Math.sin(angle);
+ }
+ */
+
+ private final float cos(float angle) {
+ return (float)Math.cos(angle);
+ }
+
+ private final float tan(float angle) {
+ return (float)Math.tan(angle);
+ }
+
+ private float dot(float ax, float ay, float az,
+ float bx, float by, float bz) {
+ return ax * bx + ay * by + az * bz;
+ }
+}
+
diff --git a/core/PGraphicsJava.java b/core/src/processing/core/PGraphicsJava.java
similarity index 96%
rename from core/PGraphicsJava.java
rename to core/src/processing/core/PGraphicsJava.java
index ae6a010fe..8d199ec3d 100644
--- a/core/PGraphicsJava.java
+++ b/core/src/processing/core/PGraphicsJava.java
@@ -1,1245 +1,1245 @@
-/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
-
-/*
- Part of the Processing project - http://processing.org
-
- Copyright (c) 2005-06 Ben Fry and Casey Reas
-
- 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.awt.*;
-import java.awt.geom.*;
-import java.awt.image.*;
-
-
-/**
- * Subclass for PGraphics that implements the graphics API
- * in Java 1.3+ using Java 2D.
- */
-public class PGraphicsJava extends PGraphics {
-
- public Graphics2D g2;
- GeneralPath gpath;
-
- int transformCount;
- AffineTransform transformStack[] =
- new AffineTransform[MATRIX_STACK_DEPTH];
- double transform[] = new double[6];
-
- Line2D.Float line = new Line2D.Float();
- Ellipse2D.Float ellipse = new Ellipse2D.Float();
- Rectangle2D.Float rect = new Rectangle2D.Float();
- Arc2D.Float arc = new Arc2D.Float();
-
- protected Color tintColorObject;
- protected Color fillColorObject;
- protected Color strokeColorObject;
-
-
-
- //////////////////////////////////////////////////////////////
-
- // INTERNAL
-
-
- /**
- * Constructor for the PGraphicsJava object.
- * This prototype only exists because of annoying
- * java compilers, and should not be used.
- */
- public PGraphicsJava() { }
-
-
- /**
- * Constructor for the PGraphics object. Use this to ensure that
- * the defaults get set properly. In a subclass, use this(w, h)
- * as the first line of a subclass' constructor to properly set
- * the internal fields and defaults.
- *
- * @param iwidth viewport width
- * @param iheight viewport height
- */
- public PGraphicsJava(int iwidth, int iheight, PApplet parent) {
- super(iwidth, iheight, parent);
- //resize(iwidth, iheight);
- }
-
-
- /**
- * Called in repsonse to a resize event, handles setting the
- * new width and height internally, as well as re-allocating
- * the pixel buffer for the new size.
- *
- * Note that this will nuke any cameraMode() settings.
- */
- public void resize(int iwidth, int iheight) { // ignore
- //System.out.println("resize " + iwidth + " " + iheight);
-
- width = iwidth;
- height = iheight;
- width1 = width - 1;
- height1 = height - 1;
-
- allocate();
-
- // clear the screen with the old background color
- //background(backgroundColor);
- }
-
-
- // broken out because of subclassing for opengl
- protected void allocate() {
- image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
- g2 = (Graphics2D) image.getGraphics();
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // FRAME
-
-
- // turn off mis.newPixels
- public void endDraw() {
- // moving this back here (post-68) because of macosx thread problem
- //mis.newPixels(pixels, cm, 0, width);
-
- // need to mark pixels as needing an update, without calling
- // its own endPixels, since that's crazy slow
- //endPixels();
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // SHAPES
-
-
- public void vertex(float x, float y) {
- splineVertexCount = 0;
- //float vertex[];
-
- if (vertexCount == vertices.length) {
- float temp[][] = new float[vertexCount<<1][VERTEX_FIELD_COUNT];
- System.arraycopy(vertices, 0, temp, 0, vertexCount);
- vertices = temp;
- //message(CHATTER, "allocating more vertices " + vertices.length);
- }
- // not everyone needs this, but just easier to store rather
- // than adding another moving part to the code...
- vertices[vertexCount][MX] = x;
- vertices[vertexCount][MY] = y;
- vertexCount++;
-
- switch (shape) {
-
- case POINTS:
- point(x, y);
- break;
-
- case LINES:
- if ((vertexCount % 2) == 0) {
- line(vertices[vertexCount-2][MX],
- vertices[vertexCount-2][MY], x, y);
- }
- break;
-
- case LINE_STRIP:
- case LINE_LOOP:
- if (gpath == null) {
- gpath = new GeneralPath();
- gpath.moveTo(x, y);
- } else {
- gpath.lineTo(x, y);
- }
- break;
-
- case TRIANGLES:
- if ((vertexCount % 3) == 0) {
- triangle(vertices[vertexCount - 3][MX],
- vertices[vertexCount - 3][MY],
- vertices[vertexCount - 2][MX],
- vertices[vertexCount - 2][MY],
- x, y);
- }
- break;
-
- case TRIANGLE_STRIP:
- if (vertexCount >= 3) {
- triangle(vertices[vertexCount - 2][MX],
- vertices[vertexCount - 2][MY],
- vertices[vertexCount - 1][MX],
- vertices[vertexCount - 1][MY],
- vertices[vertexCount - 3][MX],
- vertices[vertexCount - 3][MY]);
- }
- break;
-
- case TRIANGLE_FAN:
- if (vertexCount == 3) {
- triangle(vertices[0][MX], vertices[0][MY],
- vertices[1][MX], vertices[1][MY],
- x, y);
- } else if (vertexCount > 3) {
- gpath = new GeneralPath();
- // when vertexCount > 3, draw an un-closed triangle
- // for indices 0 (center), previous, current
- gpath.moveTo(vertices[0][MX],
- vertices[0][MY]);
- gpath.lineTo(vertices[vertexCount - 2][MX],
- vertices[vertexCount - 2][MY]);
- gpath.lineTo(x, y);
- draw_shape(gpath);
- }
- break;
-
- case QUADS:
- if ((vertexCount % 4) == 0) {
- quad(vertices[vertexCount - 4][MX],
- vertices[vertexCount - 4][MY],
- vertices[vertexCount - 3][MX],
- vertices[vertexCount - 3][MY],
- vertices[vertexCount - 2][MX],
- vertices[vertexCount - 2][MY],
- x, y);
- }
- break;
-
- case QUAD_STRIP:
- // 0---2---4
- // | | |
- // 1---3---5
- if ((vertexCount >= 4) && ((vertexCount % 2) == 0)) {
- quad(vertices[vertexCount - 4][MX],
- vertices[vertexCount - 4][MY],
- vertices[vertexCount - 2][MX],
- vertices[vertexCount - 2][MY],
- x, y,
- vertices[vertexCount - 3][MX],
- vertices[vertexCount - 3][MY]);
- }
- break;
-
- case POLYGON:
- if (gpath == null) {
- gpath = new GeneralPath();
- gpath.moveTo(x, y);
- } else {
- gpath.lineTo(x, y);
- }
- break;
- }
- }
-
-
- public void bezierVertex(float x1, float y1,
- float x2, float y2,
- float x3, float y3) {
- if (gpath == null) {
- throw new RuntimeException("Must call vertex() at least once " +
- "before using bezierVertex()");
- }
-
- switch (shape) {
- case LINE_LOOP:
- case LINE_STRIP:
- case POLYGON:
- gpath.curveTo(x1, y1, x2, y2, x3, y3);
- break;
-
- default:
- throw new RuntimeException("bezierVertex() can only be used with " +
- "LINE_STRIP, LINE_LOOP, or POLYGON");
- }
- }
-
-
- float curveX[] = new float[4];
- float curveY[] = new float[4];
-
- public void curveVertex(float x, float y) {
- if ((shape != LINE_LOOP) && (shape != LINE_STRIP) && (shape != POLYGON)) {
- throw new RuntimeException("curveVertex() can only be used with " +
- "LINE_LOOP, LINE_STRIP, and POLYGON shapes");
- }
-
- if (!curve_inited) curve_init();
- vertexCount = 0;
-
- if (splineVertices == null) {
- splineVertices = new float[DEFAULT_SPLINE_VERTICES][VERTEX_FIELD_COUNT];
- }
-
- // if more than 128 points, shift everything back to the beginning
- if (splineVertexCount == DEFAULT_SPLINE_VERTICES) {
- System.arraycopy(splineVertices[DEFAULT_SPLINE_VERTICES - 3], 0,
- splineVertices[0], 0, VERTEX_FIELD_COUNT);
- System.arraycopy(splineVertices[DEFAULT_SPLINE_VERTICES - 2], 0,
- splineVertices[1], 0, VERTEX_FIELD_COUNT);
- System.arraycopy(splineVertices[DEFAULT_SPLINE_VERTICES - 1], 0,
- splineVertices[2], 0, VERTEX_FIELD_COUNT);
- splineVertexCount = 3;
- }
-
- // this new guy will be the fourth point (or higher),
- // which means it's time to draw segments of the curve
- if (splineVertexCount >= 3) {
- curveX[0] = splineVertices[splineVertexCount-3][MX];
- curveY[0] = splineVertices[splineVertexCount-3][MY];
-
- curveX[1] = splineVertices[splineVertexCount-2][MX];
- curveY[1] = splineVertices[splineVertexCount-2][MY];
-
- curveX[2] = splineVertices[splineVertexCount-1][MX];
- curveY[2] = splineVertices[splineVertexCount-1][MY];
-
- curveX[3] = x;
- curveY[3] = y;
-
- curveToBezierMatrix.mult(curveX, curveX);
- curveToBezierMatrix.mult(curveY, curveY);
-
- // since the paths are continuous,
- // only the first point needs the actual moveto
- if (gpath == null) {
- gpath = new GeneralPath();
- gpath.moveTo(curveX[0], curveY[0]);
- }
-
- gpath.curveTo(curveX[1], curveY[1],
- curveX[2], curveY[2],
- curveX[3], curveY[3]);
- }
-
- // add the current point to the list
- splineVertices[splineVertexCount][MX] = x;
- splineVertices[splineVertexCount][MY] = y;
- splineVertexCount++;
- }
-
-
- public void beginShape(int kind) {
- super.beginShape(kind);
-
- // set gpath to null, because when mixing curves and straight
- // lines, vertexCount will be set back to zero, so vertexCount == 1
- // is no longer a good indicator of whether the shape is new.
- // this way, just check to see if gpath is null, and if it isn't
- // then just use it to continue the shape.
- gpath = null;
- }
-
-
- public void endShape() {
- if (gpath != null) { // make sure something has been drawn
- switch (shape) {
- case LINE_STRIP:
- stroke_shape(gpath);
- break;
-
- case LINE_LOOP:
- gpath.closePath();
- stroke_shape(gpath);
- break;
-
- case POLYGON:
- gpath.closePath();
- draw_shape(gpath);
- break;
- }
- }
- shape = 0;
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
-
- protected void fill_shape(Shape s) {
- if (fill) {
- g2.setColor(fillColorObject);
- g2.fill(s);
- }
- }
-
- protected void stroke_shape(Shape s) {
- if (stroke) {
- g2.setColor(strokeColorObject);
- g2.draw(s);
- }
- }
-
- protected void draw_shape(Shape s) {
- if (fill) {
- g2.setColor(fillColorObject);
- g2.fill(s);
- }
- if (stroke) {
- g2.setColor(strokeColorObject);
- g2.draw(s);
- }
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public void point(float x, float y) {
- line(x, y, x, y);
- }
-
-
- public void line(float x1, float y1, float x2, float y2) {
- //graphics.setColor(strokeColorObject);
- //graphics.drawLine(x1, y1, x2, y2);
- line.setLine(x1, y1, x2, y2);
- stroke_shape(line);
- }
-
-
- public void triangle(float x1, float y1, float x2, float y2,
- float x3, float y3) {
- gpath = new GeneralPath();
- gpath.moveTo(x1, y1);
- gpath.lineTo(x2, y2);
- gpath.lineTo(x3, y3);
- gpath.closePath();
-
- draw_shape(gpath);
- }
-
-
- public void quad(float x1, float y1, float x2, float y2,
- float x3, float y3, float x4, float y4) {
- GeneralPath gp = new GeneralPath();
- gp.moveTo(x1, y1);
- gp.lineTo(x2, y2);
- gp.lineTo(x3, y3);
- gp.lineTo(x4, y4);
- gp.closePath();
-
- draw_shape(gp);
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- protected void rectImpl(float x1, float y1, float x2, float y2) {
- rect.setFrame(x1, y1, x2-x1, y2-y1);
- draw_shape(rect);
- }
-
-
- protected void ellipseImpl(float x, float y, float w, float h) {
- ellipse.setFrame(x, y, w, h);
- draw_shape(ellipse);
- }
-
-
- protected void arcImpl(float x, float y, float w, float h,
- float start, float stop) {
- // 0 to 90 in java would be 0 to -90 for p5 renderer
- // but that won't work, so -90 to 0?
-
- if (stop - start >= TWO_PI) {
- start = 0;
- stop = 360;
-
- } else {
- start = -start * RAD_TO_DEG;
- stop = -stop * RAD_TO_DEG;
-
- // ok to do this because already checked for NaN
- while (start < 0) {
- start += 360;
- stop += 360;
- }
- if (start > stop) {
- float temp = start;
- start = stop;
- stop = temp;
- }
- }
- float span = stop - start;
-
- // stroke as Arc2D.OPEN, fill as Arc2D.PIE
- if (fill) {
- //System.out.println("filla");
- arc.setArc(x, y, w, h, start, span, Arc2D.PIE);
- fill_shape(arc);
- }
- if (stroke) {
- //System.out.println("strokey");
- arc.setArc(x, y, w, h, start, span, Arc2D.OPEN);
- stroke_shape(arc);
- }
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- /** Ignored (not needed) in Java 2D. */
- public void bezierDetail(int detail) {
- }
-
-
- /** Ignored (not needed) in Java 2D. */
- public void curveDetail(int detail) {
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- /**
- * Handle renderer-specific image drawing.
- */
- protected void imageImpl(PImage who,
- float x1, float y1, float x2, float y2,
- int u1, int v1, int u2, int v2) {
- if (who.cache == null) {
- who.cache = new ImageCache(who);
- who.endPixels(); // mark the whole thing for update
- }
-
- ImageCache cash = (ImageCache) who.cache;
- // if image previously was tinted, or the color changed
- // or the image was tinted, and tint is now disabled
- if ((tint && !cash.tinted) ||
- (tint && (cash.tintedColor != tintColor)) ||
- (!tint && cash.tinted)) {
- // for tint change, mark all pixels as needing update
- who.endPixels();
- }
-
- if (who.modified) {
- cash.update();
- who.modified = false;
- //System.out.println("image modified");
- }
-
- /*
- imageImplAWT(((ImageCache) who.cache).image,
- x1, y1, x2, y2,
- u1, v1, u2, v2);
- }
-
- // Second stage of image implementation. In this case, all that's
- // done is the AWT version of the image is drawn. This is broken out
- // separately because the PDF library needs to handle drawImage()
- // differently at this stage.
-
- protected void imageImplAWT(Image awtImage,
- float x1, float y1, float x2, float y2,
- int u1, int v1, int u2, int v2) {
- */
- g2.drawImage(((ImageCache) who.cache).image, //awtImage,
- (int) x1, (int) y1, (int) x2, (int) y2,
- u1, v1, u2, v2, null);
- }
-
-
- class ImageCache {
- PImage source;
- boolean tinted;
- int tintedColor;
- int tintedPixels[];
- BufferedImage image;
-
- public ImageCache(PImage source) {
- this.source = source;
- // even if RGB, set the image type to ARGB, because the
- // image may have an alpha value for its tint().
- int type = BufferedImage.TYPE_INT_ARGB;
- image = new BufferedImage(source.width, source.height, type);
- }
-
- public void update() { //boolean t, int argb) {
- if ((source.format == ARGB) || (source.format == RGB)) {
- if (tint) {
- // create tintedPixels[] if necessary
- if (tintedPixels == null) {
- tintedPixels = new int[source.width * source.height];
- }
-
- //int argb2 = tintColor;
- int a2 = (tintColor >> 24) & 0xff;
- int r2 = (tintColor >> 16) & 0xff;
- int g2 = (tintColor >> 8) & 0xff;
- int b2 = (tintColor) & 0xff;
- //System.out.println("a2 is " + a2);
-
- // multiply each of the color components into tintedPixels
- // if straight RGB image, don't bother multiplying
- // (also avoids problems if high bits not set)
- if (source.format == RGB) {
- int alpha = a2 << 24;
-
- for (int i = 0; i < tintedPixels.length; i++) {
- int argb1 = source.pixels[i];
- int r1 = (argb1 >> 16) & 0xff;
- int g1 = (argb1 >> 8) & 0xff;
- int b1 = (argb1) & 0xff;
-
- tintedPixels[i] = alpha |
- (((r2 * r1) & 0xff00) << 8) |
- ((g2 * g1) & 0xff00) |
- (((b2 * b1) & 0xff00) >> 8);
- }
-
- } else {
- for (int i = 0; i < tintedPixels.length; i++) {
- int argb1 = source.pixels[i];
- int a1 = (argb1 >> 24) & 0xff;
- int r1 = (argb1 >> 16) & 0xff;
- int g1 = (argb1 >> 8) & 0xff;
- int b1 = (argb1) & 0xff;
-
- tintedPixels[i] =
- (((a2 * a1) & 0xff00) << 16) |
- (((r2 * r1) & 0xff00) << 8) |
- ((g2 * g1) & 0xff00) |
- (((b2 * b1) & 0xff00) >> 8);
- }
- }
-
- tinted = true;
- tintedColor = tintColor;
-
- // finally, do a setRGB based on tintedPixels
- image.setRGB(0, 0, source.width, source.height,
- tintedPixels, 0, source.width);
-
- } else { // no tint
- // just do a setRGB like before
- // (and we'll just hope that the high bits are set)
- image.setRGB(0, 0, source.width, source.height,
- source.pixels, 0, source.width);
- }
-
- } else if (source.format == ALPHA) {
- if (tintedPixels == null) {
- tintedPixels = new int[source.width * source.height];
- }
-
- int lowbits = tintColor & 0x00ffffff;
- if (((tintColor >> 24) & 0xff) >= 254) {
- // no actual alpha to the tint, set the image's alpha
- // as the high 8 bits, and use the color as the low 24 bits
- for (int i = 0; i < tintedPixels.length; i++) {
- // don't bother with the math if value is zero
- tintedPixels[i] = (source.pixels[i] == 0) ?
- 0 : (source.pixels[i] << 24) | lowbits;
- }
-
- } else {
- // multiply each image alpha by the tint alpha
- int alphabits = (tintColor >> 24) & 0xff;
- for (int i = 0; i < tintedPixels.length; i++) {
- tintedPixels[i] = (source.pixels[i] == 0) ?
- 0 : (((alphabits * source.pixels[i]) & 0xFF00) << 16) | lowbits;
- }
- }
-
- // mark the pixels for next time
- tinted = true;
- tintedColor = tintColor;
-
- // finally, do a setRGB based on tintedPixels
- image.setRGB(0, 0, source.width, source.height,
- tintedPixels, 0, source.width);
- }
- }
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public float textAscent() {
- if (textFontNative == null) {
- return super.textAscent();
- }
- return textFontNativeMetrics.getAscent();
- }
-
-
- public float textDescent() {
- if (textFontNative == null) {
- return super.textDescent();
- }
- return textFontNativeMetrics.getDescent();
- }
-
-
- /**
- * Same as parent, but override for native version of the font.
- *
- * Unlike in PImage, where endPixels() only asks that the
- * update happens, in PGraphicsJava, this will happen immediately.
- */
- public void endPixels() {
- //endPixels(0, 0, width, height);
- WritableRaster raster = ((BufferedImage) image).getRaster();
- raster.setDataElements(0, 0, width, height, pixels);
- }
-
-
- /**
- * Update the pixels[] buffer to the PGraphics image.
- *
- * Unlike in PImage, where endPixels() only asks that the
- * update happens, in PGraphicsJava, this will happen immediately.
- */
- public void endPixels(int x, int y, int c, int d) {
- if ((x == 0) && (y == 0) && (c == width) && (d == height)) {
- endPixels();
- } else {
- throw new RuntimeException("endPixels(x, y, c, d) not implemented");
- }
- /*
- ((BufferedImage) image).setRGB(x, y,
- (imageMode == CORNER) ? c : (c - x),
- (imageMode == CORNER) ? d : (d - y),
- pixels, 0, width);
- WritableRaster raster = ((BufferedImage) image).getRaster();
- raster.setDataElements(x, y,
- (imageMode == CORNER) ? c : (c - x),
- (imageMode == CORNER) ? d : (d - y),
- pixels);
- */
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- static int getset[] = new int[1];
-
-
- public int get(int x, int y) {
- if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return 0;
- //return ((BufferedImage) image).getRGB(x, y);
- WritableRaster raster = ((BufferedImage) image).getRaster();
- raster.getDataElements(x, y, getset);
- return getset[0];
- }
-
-
- public PImage get(int x, int y, int w, int h) {
- if (imageMode == CORNERS) { // if CORNER, do nothing
- // w/h are x2/y2 in this case, bring em down to size
- w = (w - x);
- h = (h - x);
- }
-
- if (x < 0) {
- w += x; // clip off the left edge
- x = 0;
- }
- if (y < 0) {
- h += y; // clip off some of the height
- y = 0;
- }
-
- if (x + w > width) w = width - x;
- if (y + h > height) h = height - y;
-
- PImage output = new PImage(w, h);
- // oops, the last parameter is the scan size of the *target* buffer
- //((BufferedImage) image).getRGB(x, y, w, h, output.pixels, 0, w);
- WritableRaster raster = ((BufferedImage) image).getRaster();
- raster.getDataElements(x, y, w, h, output.pixels);
-
- return output;
- }
-
-
- /**
- * Grab a copy of the current pixel buffer.
- */
- public PImage get() {
- /*
- PImage outgoing = new PImage(width, height);
- ((BufferedImage) image).getRGB(0, 0, width, height,
- outgoing.pixels, 0, width);
- return outgoing;
- */
- return get(0, 0, width, height);
- }
-
-
- public void set(int x, int y, int argb) {
- if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return;
- //((BufferedImage) image).setRGB(x, y, argb);
- getset[0] = argb;
- WritableRaster raster = ((BufferedImage) image).getRaster();
- raster.setDataElements(x, y, getset);
- }
-
-
- // fully debugged
- /*
- public void set(int dx, int dy, PImage src) {
- int sx = 0;
- int sy = 0;
- int sw = src.width;
- int sh = src.height;
-
- if (dx < 0) { // off left edge
- sx -= dx;
- sw += dx;
- dx = 0;
- }
- if (dy < 0) { // off top edge
- sy -= dy;
- sh += dy;
- dy = 0;
- }
- if (dx + sw > width) { // off right edge
- sw = width - dx;
- }
- if (dy + sh > height) { // off bottom edge
- sh = height - dy;
- }
-
- //System.out.println(dx + " " + dy + " " +
- // sx + " " + sy + " " + sw + " " + sh + " " +
- // src.pixels + " " + 0 + " " + src.width);
- BufferedImage bi = (BufferedImage) image;
- bi.setRGB(dx, dy, sw, sh, src.pixels, sy*src.width + sx, src.width);
- }
- */
-
-
- protected void setImpl(int dx, int dy, int sx, int sy, int sw, int sh,
- PImage src) {
- //BufferedImage bi = (BufferedImage) image;
- //bi.setRGB(dx, dy, sw, sh, src.pixels, sy*src.width + sx, src.width);
-
- WritableRaster raster = ((BufferedImage) image).getRaster();
- if ((sx == 0) && (sy == 0) && (sw == src.width) && (sh == src.height)) {
- raster.setDataElements(dx, dy, src.width, src.height, src.pixels);
- } else {
- int mode = src.imageMode;
- src.imageMode = CORNERS;
- PImage temp = src.get(sx, sy, sw, sh);
- src.imageMode = mode;
- raster.setPixels(dx, dy, temp.width, temp.height, temp.pixels);
- }
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public void mask(int alpha[]) {
- throw new RuntimeException("mask() cannot be used with JAVA2D");
- }
-
-
- public void mask(PImage alpha) {
- throw new RuntimeException("mask() cannot be used with JAVA2D");
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public void filter(int kind) {
- beginPixels();
- super.filter(kind);
- endPixels();
- }
-
-
- public void filter(int kind, float param) {
- beginPixels();
- super.filter(kind, param);
- endPixels();
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public void copy(int sx, int sy, int sw, int sh,
- int dx, int dy, int dw, int dh) {
- if ((sw != dw) || (sh != dh)) {
- // use slow version if changing size
- copy(this, sx, sy, sw, sh, dx, dy, dw, dh);
- }
- if (imageMode == CORNERS) {
- sw -= sx;
- sh -= sy;
- }
- dx = dx - sx; // java2d's "dx" is the delta, not dest
- dy = dy - sy;
- g2.copyArea(sx, sy, sw, sh, dx, dy);
- }
-
-
- public void copy(PImage src,
- int sx1, int sy1, int sx2, int sy2,
- int dx1, int dy1, int dx2, int dy2) {
- beginPixels();
- super.copy(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
- endPixels();
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public void blend(PImage src, int sx, int sy, int dx, int dy, int mode) {
- beginPixels();
- super.blend(src, sx, sy, dx, dy, mode);
- endPixels();
- }
-
-
- public void blend(int sx, int sy, int dx, int dy, int mode) {
- beginPixels();
- super.blend(sx, sy, dx, dy, mode);
- endPixels();
- }
-
-
- public void blend(int sx1, int sy1, int sx2, int sy2,
- int dx1, int dy1, int dx2, int dy2, int mode) {
- beginPixels();
- super.blend(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode);
- endPixels();
- }
-
-
- public void blend(PImage src, int sx1, int sy1, int sx2, int sy2,
- int dx1, int dy1, int dx2, int dy2, int mode) {
- beginPixels();
- super.blend(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode);
- endPixels();
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public void save(String filename) {
- //System.out.println("start load");
- beginPixels();
- //System.out.println("end load, start save");
- super.save(filename);
- //System.out.println("done with save");
- }
-}
+/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2005-06 Ben Fry and Casey Reas
+
+ 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.awt.*;
+import java.awt.geom.*;
+import java.awt.image.*;
+
+
+/**
+ * Subclass for PGraphics that implements the graphics API
+ * in Java 1.3+ using Java 2D.
+ */
+public class PGraphicsJava extends PGraphics {
+
+ public Graphics2D g2;
+ GeneralPath gpath;
+
+ int transformCount;
+ AffineTransform transformStack[] =
+ new AffineTransform[MATRIX_STACK_DEPTH];
+ double transform[] = new double[6];
+
+ Line2D.Float line = new Line2D.Float();
+ Ellipse2D.Float ellipse = new Ellipse2D.Float();
+ Rectangle2D.Float rect = new Rectangle2D.Float();
+ Arc2D.Float arc = new Arc2D.Float();
+
+ protected Color tintColorObject;
+ protected Color fillColorObject;
+ protected Color strokeColorObject;
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // INTERNAL
+
+
+ /**
+ * Constructor for the PGraphicsJava object.
+ * This prototype only exists because of annoying
+ * java compilers, and should not be used.
+ */
+ public PGraphicsJava() { }
+
+
+ /**
+ * Constructor for the PGraphics object. Use this to ensure that
+ * the defaults get set properly. In a subclass, use this(w, h)
+ * as the first line of a subclass' constructor to properly set
+ * the internal fields and defaults.
+ *
+ * @param iwidth viewport width
+ * @param iheight viewport height
+ */
+ public PGraphicsJava(int iwidth, int iheight, PApplet parent) {
+ super(iwidth, iheight, parent);
+ //resize(iwidth, iheight);
+ }
+
+
+ /**
+ * Called in repsonse to a resize event, handles setting the
+ * new width and height internally, as well as re-allocating
+ * the pixel buffer for the new size.
+ *
+ * Note that this will nuke any cameraMode() settings.
+ */
+ public void resize(int iwidth, int iheight) { // ignore
+ //System.out.println("resize " + iwidth + " " + iheight);
+
+ width = iwidth;
+ height = iheight;
+ width1 = width - 1;
+ height1 = height - 1;
+
+ allocate();
+
+ // clear the screen with the old background color
+ //background(backgroundColor);
+ }
+
+
+ // broken out because of subclassing for opengl
+ protected void allocate() {
+ image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ g2 = (Graphics2D) image.getGraphics();
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // FRAME
+
+
+ // turn off mis.newPixels
+ public void endDraw() {
+ // moving this back here (post-68) because of macosx thread problem
+ //mis.newPixels(pixels, cm, 0, width);
+
+ // need to mark pixels as needing an update, without calling
+ // its own endPixels, since that's crazy slow
+ //endPixels();
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // SHAPES
+
+
+ public void vertex(float x, float y) {
+ splineVertexCount = 0;
+ //float vertex[];
+
+ if (vertexCount == vertices.length) {
+ float temp[][] = new float[vertexCount<<1][VERTEX_FIELD_COUNT];
+ System.arraycopy(vertices, 0, temp, 0, vertexCount);
+ vertices = temp;
+ //message(CHATTER, "allocating more vertices " + vertices.length);
+ }
+ // not everyone needs this, but just easier to store rather
+ // than adding another moving part to the code...
+ vertices[vertexCount][MX] = x;
+ vertices[vertexCount][MY] = y;
+ vertexCount++;
+
+ switch (shape) {
+
+ case POINTS:
+ point(x, y);
+ break;
+
+ case LINES:
+ if ((vertexCount % 2) == 0) {
+ line(vertices[vertexCount-2][MX],
+ vertices[vertexCount-2][MY], x, y);
+ }
+ break;
+
+ case LINE_STRIP:
+ case LINE_LOOP:
+ if (gpath == null) {
+ gpath = new GeneralPath();
+ gpath.moveTo(x, y);
+ } else {
+ gpath.lineTo(x, y);
+ }
+ break;
+
+ case TRIANGLES:
+ if ((vertexCount % 3) == 0) {
+ triangle(vertices[vertexCount - 3][MX],
+ vertices[vertexCount - 3][MY],
+ vertices[vertexCount - 2][MX],
+ vertices[vertexCount - 2][MY],
+ x, y);
+ }
+ break;
+
+ case TRIANGLE_STRIP:
+ if (vertexCount >= 3) {
+ triangle(vertices[vertexCount - 2][MX],
+ vertices[vertexCount - 2][MY],
+ vertices[vertexCount - 1][MX],
+ vertices[vertexCount - 1][MY],
+ vertices[vertexCount - 3][MX],
+ vertices[vertexCount - 3][MY]);
+ }
+ break;
+
+ case TRIANGLE_FAN:
+ if (vertexCount == 3) {
+ triangle(vertices[0][MX], vertices[0][MY],
+ vertices[1][MX], vertices[1][MY],
+ x, y);
+ } else if (vertexCount > 3) {
+ gpath = new GeneralPath();
+ // when vertexCount > 3, draw an un-closed triangle
+ // for indices 0 (center), previous, current
+ gpath.moveTo(vertices[0][MX],
+ vertices[0][MY]);
+ gpath.lineTo(vertices[vertexCount - 2][MX],
+ vertices[vertexCount - 2][MY]);
+ gpath.lineTo(x, y);
+ draw_shape(gpath);
+ }
+ break;
+
+ case QUADS:
+ if ((vertexCount % 4) == 0) {
+ quad(vertices[vertexCount - 4][MX],
+ vertices[vertexCount - 4][MY],
+ vertices[vertexCount - 3][MX],
+ vertices[vertexCount - 3][MY],
+ vertices[vertexCount - 2][MX],
+ vertices[vertexCount - 2][MY],
+ x, y);
+ }
+ break;
+
+ case QUAD_STRIP:
+ // 0---2---4
+ // | | |
+ // 1---3---5
+ if ((vertexCount >= 4) && ((vertexCount % 2) == 0)) {
+ quad(vertices[vertexCount - 4][MX],
+ vertices[vertexCount - 4][MY],
+ vertices[vertexCount - 2][MX],
+ vertices[vertexCount - 2][MY],
+ x, y,
+ vertices[vertexCount - 3][MX],
+ vertices[vertexCount - 3][MY]);
+ }
+ break;
+
+ case POLYGON:
+ if (gpath == null) {
+ gpath = new GeneralPath();
+ gpath.moveTo(x, y);
+ } else {
+ gpath.lineTo(x, y);
+ }
+ break;
+ }
+ }
+
+
+ public void bezierVertex(float x1, float y1,
+ float x2, float y2,
+ float x3, float y3) {
+ if (gpath == null) {
+ throw new RuntimeException("Must call vertex() at least once " +
+ "before using bezierVertex()");
+ }
+
+ switch (shape) {
+ case LINE_LOOP:
+ case LINE_STRIP:
+ case POLYGON:
+ gpath.curveTo(x1, y1, x2, y2, x3, y3);
+ break;
+
+ default:
+ throw new RuntimeException("bezierVertex() can only be used with " +
+ "LINE_STRIP, LINE_LOOP, or POLYGON");
+ }
+ }
+
+
+ float curveX[] = new float[4];
+ float curveY[] = new float[4];
+
+ public void curveVertex(float x, float y) {
+ if ((shape != LINE_LOOP) && (shape != LINE_STRIP) && (shape != POLYGON)) {
+ throw new RuntimeException("curveVertex() can only be used with " +
+ "LINE_LOOP, LINE_STRIP, and POLYGON shapes");
+ }
+
+ if (!curve_inited) curve_init();
+ vertexCount = 0;
+
+ if (splineVertices == null) {
+ splineVertices = new float[DEFAULT_SPLINE_VERTICES][VERTEX_FIELD_COUNT];
+ }
+
+ // if more than 128 points, shift everything back to the beginning
+ if (splineVertexCount == DEFAULT_SPLINE_VERTICES) {
+ System.arraycopy(splineVertices[DEFAULT_SPLINE_VERTICES - 3], 0,
+ splineVertices[0], 0, VERTEX_FIELD_COUNT);
+ System.arraycopy(splineVertices[DEFAULT_SPLINE_VERTICES - 2], 0,
+ splineVertices[1], 0, VERTEX_FIELD_COUNT);
+ System.arraycopy(splineVertices[DEFAULT_SPLINE_VERTICES - 1], 0,
+ splineVertices[2], 0, VERTEX_FIELD_COUNT);
+ splineVertexCount = 3;
+ }
+
+ // this new guy will be the fourth point (or higher),
+ // which means it's time to draw segments of the curve
+ if (splineVertexCount >= 3) {
+ curveX[0] = splineVertices[splineVertexCount-3][MX];
+ curveY[0] = splineVertices[splineVertexCount-3][MY];
+
+ curveX[1] = splineVertices[splineVertexCount-2][MX];
+ curveY[1] = splineVertices[splineVertexCount-2][MY];
+
+ curveX[2] = splineVertices[splineVertexCount-1][MX];
+ curveY[2] = splineVertices[splineVertexCount-1][MY];
+
+ curveX[3] = x;
+ curveY[3] = y;
+
+ curveToBezierMatrix.mult(curveX, curveX);
+ curveToBezierMatrix.mult(curveY, curveY);
+
+ // since the paths are continuous,
+ // only the first point needs the actual moveto
+ if (gpath == null) {
+ gpath = new GeneralPath();
+ gpath.moveTo(curveX[0], curveY[0]);
+ }
+
+ gpath.curveTo(curveX[1], curveY[1],
+ curveX[2], curveY[2],
+ curveX[3], curveY[3]);
+ }
+
+ // add the current point to the list
+ splineVertices[splineVertexCount][MX] = x;
+ splineVertices[splineVertexCount][MY] = y;
+ splineVertexCount++;
+ }
+
+
+ public void beginShape(int kind) {
+ super.beginShape(kind);
+
+ // set gpath to null, because when mixing curves and straight
+ // lines, vertexCount will be set back to zero, so vertexCount == 1
+ // is no longer a good indicator of whether the shape is new.
+ // this way, just check to see if gpath is null, and if it isn't
+ // then just use it to continue the shape.
+ gpath = null;
+ }
+
+
+ public void endShape() {
+ if (gpath != null) { // make sure something has been drawn
+ switch (shape) {
+ case LINE_STRIP:
+ stroke_shape(gpath);
+ break;
+
+ case LINE_LOOP:
+ gpath.closePath();
+ stroke_shape(gpath);
+ break;
+
+ case POLYGON:
+ gpath.closePath();
+ draw_shape(gpath);
+ break;
+ }
+ }
+ shape = 0;
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ protected void fill_shape(Shape s) {
+ if (fill) {
+ g2.setColor(fillColorObject);
+ g2.fill(s);
+ }
+ }
+
+ protected void stroke_shape(Shape s) {
+ if (stroke) {
+ g2.setColor(strokeColorObject);
+ g2.draw(s);
+ }
+ }
+
+ protected void draw_shape(Shape s) {
+ if (fill) {
+ g2.setColor(fillColorObject);
+ g2.fill(s);
+ }
+ if (stroke) {
+ g2.setColor(strokeColorObject);
+ g2.draw(s);
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public void point(float x, float y) {
+ line(x, y, x, y);
+ }
+
+
+ public void line(float x1, float y1, float x2, float y2) {
+ //graphics.setColor(strokeColorObject);
+ //graphics.drawLine(x1, y1, x2, y2);
+ line.setLine(x1, y1, x2, y2);
+ stroke_shape(line);
+ }
+
+
+ public void triangle(float x1, float y1, float x2, float y2,
+ float x3, float y3) {
+ gpath = new GeneralPath();
+ gpath.moveTo(x1, y1);
+ gpath.lineTo(x2, y2);
+ gpath.lineTo(x3, y3);
+ gpath.closePath();
+
+ draw_shape(gpath);
+ }
+
+
+ public void quad(float x1, float y1, float x2, float y2,
+ float x3, float y3, float x4, float y4) {
+ GeneralPath gp = new GeneralPath();
+ gp.moveTo(x1, y1);
+ gp.lineTo(x2, y2);
+ gp.lineTo(x3, y3);
+ gp.lineTo(x4, y4);
+ gp.closePath();
+
+ draw_shape(gp);
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ protected void rectImpl(float x1, float y1, float x2, float y2) {
+ rect.setFrame(x1, y1, x2-x1, y2-y1);
+ draw_shape(rect);
+ }
+
+
+ protected void ellipseImpl(float x, float y, float w, float h) {
+ ellipse.setFrame(x, y, w, h);
+ draw_shape(ellipse);
+ }
+
+
+ protected void arcImpl(float x, float y, float w, float h,
+ float start, float stop) {
+ // 0 to 90 in java would be 0 to -90 for p5 renderer
+ // but that won't work, so -90 to 0?
+
+ if (stop - start >= TWO_PI) {
+ start = 0;
+ stop = 360;
+
+ } else {
+ start = -start * RAD_TO_DEG;
+ stop = -stop * RAD_TO_DEG;
+
+ // ok to do this because already checked for NaN
+ while (start < 0) {
+ start += 360;
+ stop += 360;
+ }
+ if (start > stop) {
+ float temp = start;
+ start = stop;
+ stop = temp;
+ }
+ }
+ float span = stop - start;
+
+ // stroke as Arc2D.OPEN, fill as Arc2D.PIE
+ if (fill) {
+ //System.out.println("filla");
+ arc.setArc(x, y, w, h, start, span, Arc2D.PIE);
+ fill_shape(arc);
+ }
+ if (stroke) {
+ //System.out.println("strokey");
+ arc.setArc(x, y, w, h, start, span, Arc2D.OPEN);
+ stroke_shape(arc);
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ /** Ignored (not needed) in Java 2D. */
+ public void bezierDetail(int detail) {
+ }
+
+
+ /** Ignored (not needed) in Java 2D. */
+ public void curveDetail(int detail) {
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ /**
+ * Handle renderer-specific image drawing.
+ */
+ protected void imageImpl(PImage who,
+ float x1, float y1, float x2, float y2,
+ int u1, int v1, int u2, int v2) {
+ if (who.cache == null) {
+ who.cache = new ImageCache(who);
+ who.endPixels(); // mark the whole thing for update
+ }
+
+ ImageCache cash = (ImageCache) who.cache;
+ // if image previously was tinted, or the color changed
+ // or the image was tinted, and tint is now disabled
+ if ((tint && !cash.tinted) ||
+ (tint && (cash.tintedColor != tintColor)) ||
+ (!tint && cash.tinted)) {
+ // for tint change, mark all pixels as needing update
+ who.endPixels();
+ }
+
+ if (who.modified) {
+ cash.update();
+ who.modified = false;
+ //System.out.println("image modified");
+ }
+
+ /*
+ imageImplAWT(((ImageCache) who.cache).image,
+ x1, y1, x2, y2,
+ u1, v1, u2, v2);
+ }
+
+ // Second stage of image implementation. In this case, all that's
+ // done is the AWT version of the image is drawn. This is broken out
+ // separately because the PDF library needs to handle drawImage()
+ // differently at this stage.
+
+ protected void imageImplAWT(Image awtImage,
+ float x1, float y1, float x2, float y2,
+ int u1, int v1, int u2, int v2) {
+ */
+ g2.drawImage(((ImageCache) who.cache).image, //awtImage,
+ (int) x1, (int) y1, (int) x2, (int) y2,
+ u1, v1, u2, v2, null);
+ }
+
+
+ class ImageCache {
+ PImage source;
+ boolean tinted;
+ int tintedColor;
+ int tintedPixels[];
+ BufferedImage image;
+
+ public ImageCache(PImage source) {
+ this.source = source;
+ // even if RGB, set the image type to ARGB, because the
+ // image may have an alpha value for its tint().
+ int type = BufferedImage.TYPE_INT_ARGB;
+ image = new BufferedImage(source.width, source.height, type);
+ }
+
+ public void update() { //boolean t, int argb) {
+ if ((source.format == ARGB) || (source.format == RGB)) {
+ if (tint) {
+ // create tintedPixels[] if necessary
+ if (tintedPixels == null) {
+ tintedPixels = new int[source.width * source.height];
+ }
+
+ //int argb2 = tintColor;
+ int a2 = (tintColor >> 24) & 0xff;
+ int r2 = (tintColor >> 16) & 0xff;
+ int g2 = (tintColor >> 8) & 0xff;
+ int b2 = (tintColor) & 0xff;
+ //System.out.println("a2 is " + a2);
+
+ // multiply each of the color components into tintedPixels
+ // if straight RGB image, don't bother multiplying
+ // (also avoids problems if high bits not set)
+ if (source.format == RGB) {
+ int alpha = a2 << 24;
+
+ for (int i = 0; i < tintedPixels.length; i++) {
+ int argb1 = source.pixels[i];
+ int r1 = (argb1 >> 16) & 0xff;
+ int g1 = (argb1 >> 8) & 0xff;
+ int b1 = (argb1) & 0xff;
+
+ tintedPixels[i] = alpha |
+ (((r2 * r1) & 0xff00) << 8) |
+ ((g2 * g1) & 0xff00) |
+ (((b2 * b1) & 0xff00) >> 8);
+ }
+
+ } else {
+ for (int i = 0; i < tintedPixels.length; i++) {
+ int argb1 = source.pixels[i];
+ int a1 = (argb1 >> 24) & 0xff;
+ int r1 = (argb1 >> 16) & 0xff;
+ int g1 = (argb1 >> 8) & 0xff;
+ int b1 = (argb1) & 0xff;
+
+ tintedPixels[i] =
+ (((a2 * a1) & 0xff00) << 16) |
+ (((r2 * r1) & 0xff00) << 8) |
+ ((g2 * g1) & 0xff00) |
+ (((b2 * b1) & 0xff00) >> 8);
+ }
+ }
+
+ tinted = true;
+ tintedColor = tintColor;
+
+ // finally, do a setRGB based on tintedPixels
+ image.setRGB(0, 0, source.width, source.height,
+ tintedPixels, 0, source.width);
+
+ } else { // no tint
+ // just do a setRGB like before
+ // (and we'll just hope that the high bits are set)
+ image.setRGB(0, 0, source.width, source.height,
+ source.pixels, 0, source.width);
+ }
+
+ } else if (source.format == ALPHA) {
+ if (tintedPixels == null) {
+ tintedPixels = new int[source.width * source.height];
+ }
+
+ int lowbits = tintColor & 0x00ffffff;
+ if (((tintColor >> 24) & 0xff) >= 254) {
+ // no actual alpha to the tint, set the image's alpha
+ // as the high 8 bits, and use the color as the low 24 bits
+ for (int i = 0; i < tintedPixels.length; i++) {
+ // don't bother with the math if value is zero
+ tintedPixels[i] = (source.pixels[i] == 0) ?
+ 0 : (source.pixels[i] << 24) | lowbits;
+ }
+
+ } else {
+ // multiply each image alpha by the tint alpha
+ int alphabits = (tintColor >> 24) & 0xff;
+ for (int i = 0; i < tintedPixels.length; i++) {
+ tintedPixels[i] = (source.pixels[i] == 0) ?
+ 0 : (((alphabits * source.pixels[i]) & 0xFF00) << 16) | lowbits;
+ }
+ }
+
+ // mark the pixels for next time
+ tinted = true;
+ tintedColor = tintColor;
+
+ // finally, do a setRGB based on tintedPixels
+ image.setRGB(0, 0, source.width, source.height,
+ tintedPixels, 0, source.width);
+ }
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public float textAscent() {
+ if (textFontNative == null) {
+ return super.textAscent();
+ }
+ return textFontNativeMetrics.getAscent();
+ }
+
+
+ public float textDescent() {
+ if (textFontNative == null) {
+ return super.textDescent();
+ }
+ return textFontNativeMetrics.getDescent();
+ }
+
+
+ /**
+ * Same as parent, but override for native version of the font.
+ *
+ * Unlike in PImage, where endPixels() only asks that the
+ * update happens, in PGraphicsJava, this will happen immediately.
+ */
+ public void endPixels() {
+ //endPixels(0, 0, width, height);
+ WritableRaster raster = ((BufferedImage) image).getRaster();
+ raster.setDataElements(0, 0, width, height, pixels);
+ }
+
+
+ /**
+ * Update the pixels[] buffer to the PGraphics image.
+ *
+ * Unlike in PImage, where endPixels() only asks that the
+ * update happens, in PGraphicsJava, this will happen immediately.
+ */
+ public void endPixels(int x, int y, int c, int d) {
+ if ((x == 0) && (y == 0) && (c == width) && (d == height)) {
+ endPixels();
+ } else {
+ throw new RuntimeException("endPixels(x, y, c, d) not implemented");
+ }
+ /*
+ ((BufferedImage) image).setRGB(x, y,
+ (imageMode == CORNER) ? c : (c - x),
+ (imageMode == CORNER) ? d : (d - y),
+ pixels, 0, width);
+ WritableRaster raster = ((BufferedImage) image).getRaster();
+ raster.setDataElements(x, y,
+ (imageMode == CORNER) ? c : (c - x),
+ (imageMode == CORNER) ? d : (d - y),
+ pixels);
+ */
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ static int getset[] = new int[1];
+
+
+ public int get(int x, int y) {
+ if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return 0;
+ //return ((BufferedImage) image).getRGB(x, y);
+ WritableRaster raster = ((BufferedImage) image).getRaster();
+ raster.getDataElements(x, y, getset);
+ return getset[0];
+ }
+
+
+ public PImage get(int x, int y, int w, int h) {
+ if (imageMode == CORNERS) { // if CORNER, do nothing
+ // w/h are x2/y2 in this case, bring em down to size
+ w = (w - x);
+ h = (h - x);
+ }
+
+ if (x < 0) {
+ w += x; // clip off the left edge
+ x = 0;
+ }
+ if (y < 0) {
+ h += y; // clip off some of the height
+ y = 0;
+ }
+
+ if (x + w > width) w = width - x;
+ if (y + h > height) h = height - y;
+
+ PImage output = new PImage(w, h);
+ // oops, the last parameter is the scan size of the *target* buffer
+ //((BufferedImage) image).getRGB(x, y, w, h, output.pixels, 0, w);
+ WritableRaster raster = ((BufferedImage) image).getRaster();
+ raster.getDataElements(x, y, w, h, output.pixels);
+
+ return output;
+ }
+
+
+ /**
+ * Grab a copy of the current pixel buffer.
+ */
+ public PImage get() {
+ /*
+ PImage outgoing = new PImage(width, height);
+ ((BufferedImage) image).getRGB(0, 0, width, height,
+ outgoing.pixels, 0, width);
+ return outgoing;
+ */
+ return get(0, 0, width, height);
+ }
+
+
+ public void set(int x, int y, int argb) {
+ if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return;
+ //((BufferedImage) image).setRGB(x, y, argb);
+ getset[0] = argb;
+ WritableRaster raster = ((BufferedImage) image).getRaster();
+ raster.setDataElements(x, y, getset);
+ }
+
+
+ // fully debugged
+ /*
+ public void set(int dx, int dy, PImage src) {
+ int sx = 0;
+ int sy = 0;
+ int sw = src.width;
+ int sh = src.height;
+
+ if (dx < 0) { // off left edge
+ sx -= dx;
+ sw += dx;
+ dx = 0;
+ }
+ if (dy < 0) { // off top edge
+ sy -= dy;
+ sh += dy;
+ dy = 0;
+ }
+ if (dx + sw > width) { // off right edge
+ sw = width - dx;
+ }
+ if (dy + sh > height) { // off bottom edge
+ sh = height - dy;
+ }
+
+ //System.out.println(dx + " " + dy + " " +
+ // sx + " " + sy + " " + sw + " " + sh + " " +
+ // src.pixels + " " + 0 + " " + src.width);
+ BufferedImage bi = (BufferedImage) image;
+ bi.setRGB(dx, dy, sw, sh, src.pixels, sy*src.width + sx, src.width);
+ }
+ */
+
+
+ protected void setImpl(int dx, int dy, int sx, int sy, int sw, int sh,
+ PImage src) {
+ //BufferedImage bi = (BufferedImage) image;
+ //bi.setRGB(dx, dy, sw, sh, src.pixels, sy*src.width + sx, src.width);
+
+ WritableRaster raster = ((BufferedImage) image).getRaster();
+ if ((sx == 0) && (sy == 0) && (sw == src.width) && (sh == src.height)) {
+ raster.setDataElements(dx, dy, src.width, src.height, src.pixels);
+ } else {
+ int mode = src.imageMode;
+ src.imageMode = CORNERS;
+ PImage temp = src.get(sx, sy, sw, sh);
+ src.imageMode = mode;
+ raster.setPixels(dx, dy, temp.width, temp.height, temp.pixels);
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public void mask(int alpha[]) {
+ throw new RuntimeException("mask() cannot be used with JAVA2D");
+ }
+
+
+ public void mask(PImage alpha) {
+ throw new RuntimeException("mask() cannot be used with JAVA2D");
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public void filter(int kind) {
+ beginPixels();
+ super.filter(kind);
+ endPixels();
+ }
+
+
+ public void filter(int kind, float param) {
+ beginPixels();
+ super.filter(kind, param);
+ endPixels();
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public void copy(int sx, int sy, int sw, int sh,
+ int dx, int dy, int dw, int dh) {
+ if ((sw != dw) || (sh != dh)) {
+ // use slow version if changing size
+ copy(this, sx, sy, sw, sh, dx, dy, dw, dh);
+ }
+ if (imageMode == CORNERS) {
+ sw -= sx;
+ sh -= sy;
+ }
+ dx = dx - sx; // java2d's "dx" is the delta, not dest
+ dy = dy - sy;
+ g2.copyArea(sx, sy, sw, sh, dx, dy);
+ }
+
+
+ public void copy(PImage src,
+ int sx1, int sy1, int sx2, int sy2,
+ int dx1, int dy1, int dx2, int dy2) {
+ beginPixels();
+ super.copy(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
+ endPixels();
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public void blend(PImage src, int sx, int sy, int dx, int dy, int mode) {
+ beginPixels();
+ super.blend(src, sx, sy, dx, dy, mode);
+ endPixels();
+ }
+
+
+ public void blend(int sx, int sy, int dx, int dy, int mode) {
+ beginPixels();
+ super.blend(sx, sy, dx, dy, mode);
+ endPixels();
+ }
+
+
+ public void blend(int sx1, int sy1, int sx2, int sy2,
+ int dx1, int dy1, int dx2, int dy2, int mode) {
+ beginPixels();
+ super.blend(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode);
+ endPixels();
+ }
+
+
+ public void blend(PImage src, int sx1, int sy1, int sx2, int sy2,
+ int dx1, int dy1, int dx2, int dy2, int mode) {
+ beginPixels();
+ super.blend(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode);
+ endPixels();
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public void save(String filename) {
+ //System.out.println("start load");
+ beginPixels();
+ //System.out.println("end load, start save");
+ super.save(filename);
+ //System.out.println("done with save");
+ }
+}
diff --git a/core/PImage.java b/core/src/processing/core/PImage.java
similarity index 96%
rename from core/PImage.java
rename to core/src/processing/core/PImage.java
index e5cc5f8fc..d5b024224 100644
--- a/core/PImage.java
+++ b/core/src/processing/core/PImage.java
@@ -1,2151 +1,2151 @@
-/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
-
-/*
- Part of the Processing project - http://processing.org
-
- Copyright (c) 2004-06 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.awt.image.*;
-import java.io.*;
-import java.lang.reflect.*;
-
-import javax.imageio.*;
-
-
-/**
- * Storage class for pixel data.
- *
- * Code for copying, resizing, scaling, and blending contributed
- * by toxi
- *
- */
-public class PImage implements PConstants, Cloneable {
-
- /**
- * Format for this image, one of RGB, ARGB or ALPHA.
- * note that RGB images still require 0xff in the high byte
- * because of how they'll be manipulated by other functions
- */
- public int format;
-
- public int pixels[];
- public int width, height;
- // would scan line be useful? maybe for pow of 2 gl textures
-
- // note! inherited by PGraphics
- public int imageMode = CORNER;
- public boolean smooth = false;
-
- /** native storage for java 1.3 image object */
- //public Object image;
-
- /** for subclasses that need to store info about the image */
- public Object cache;
-
- /** modified portion of the image */
- public boolean modified;
- public int mx1, my1, mx2, my2;
-
- // private fields
- private int fracU, ifU, fracV, ifV, u1, u2, v1, v2, sX, sY, iw, iw1, ih1;
- private int ul, ll, ur, lr, cUL, cLL, cUR, cLR;
- private int srcXOffset, srcYOffset;
- private int r, g, b, a;
- private int[] srcBuffer;
-
- // fixed point precision is limited to 15 bits!!
- static final int PRECISIONB = 15;
- static final int PRECISIONF = 1 << PRECISIONB;
- static final int PREC_MAXVAL = PRECISIONF-1;
- static final int PREC_ALPHA_SHIFT = 24-PRECISIONB;
- static final int PREC_RED_SHIFT = 16-PRECISIONB;
-
- // internal kernel stuff for the gaussian blur filter
- int blurRadius;
- int blurKernelSize;
- int[] blurKernel;
- int[][] blurMult;
-
-
- //////////////////////////////////////////////////////////////
-
-
- /**
- * Create an empty image object, set its format to RGB.
- * The pixel array is not allocated.
- */
- public PImage() {
- format = RGB; // makes sure that this guy is useful
- cache = null;
- }
-
-
- /**
- * Create a new RGB (alpha ignored) image of a specific size.
- * All pixels are set to zero, meaning black, but since the
- * alpha is zero, it will be transparent.
- */
- public PImage(int width, int height) {
- init(width, height, RGB);
- //this(new int[width * height], width, height, ARGB);
- // toxi: is it maybe better to init the image with max alpha enabled?
- //for(int i=0; i
- * This is not currently used by any of the renderers, however the api
- * is structured this way in the hope of being able to use this to
- * speed things up in the future.
- *
- * Note that when using imageMode(CORNERS),
- * the x2 and y2 positions are non-inclusive.
- */
- public void endPixels(int x1, int y1, int x2, int y2) {
- //if (!modified) { // could just set directly, but..
- //}
-
- if (imageMode == CORNER) { // x2, y2 are w/h
- x2 += x1;
- y2 += y1;
- }
-
- if (!modified) {
- mx1 = x1;
- mx2 = x2;
- my1 = y1;
- my2 = y2;
- modified = true;
-
- } else {
- if (x1 < mx1) mx1 = x1;
- if (x1 > mx2) mx2 = x1;
- if (y1 < my1) my1 = y1;
- if (y1 > my2) my2 = y1;
-
- if (x2 < mx1) mx1 = x2;
- if (x2 > mx2) mx2 = x2;
- if (y2 < my1) my1 = y2;
- if (y2 > my2) my2 = y2;
- }
- }
-
-
- //public void pixelsUpdated() {
- //mx1 = Integer.MAX_VALUE;
- //my1 = Integer.MAX_VALUE;
- //mx2 = -Integer.MAX_VALUE;
- //my2 = -Integer.MAX_VALUE;
- //modified = false;
- //}
-
-
- //////////////////////////////////////////////////////////////
-
- // GET/SET PIXELS
-
-
- /**
- * Returns an ARGB "color" type (a packed 32 bit int with the color.
- * If the coordinate is outside the image, zero is returned
- * (black, but completely transparent).
- *
- * If the image is in RGB format (i.e. on a PVideo object),
- * the value will get its high bits set, just to avoid cases where
- * they haven't been set already.
- *
- * If the image is in ALPHA format, this returns a white color
- * that has its alpha value set.
- *
- * This function is included primarily for beginners. It is quite
- * slow because it has to check to see if the x, y that was provided
- * is inside the bounds, and then has to check to see what image
- * type it is. If you want things to be more efficient, access the
- * pixels[] array directly.
- */
- public int get(int x, int y) {
- if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return 0;
-
- switch (format) {
- case RGB:
- return pixels[y*width + x] | 0xff000000;
-
- case ARGB:
- return pixels[y*width + x];
-
- case ALPHA:
- return (pixels[y*width + x] << 24) | 0xffffff;
- }
- return 0;
- }
-
-
- /**
- * Grab a subsection of a PImage, and copy it into a fresh PImage.
- * This honors imageMode() for the coordinates.
- */
- public PImage get(int x, int y, int w, int h) {
- if (imageMode == CORNERS) { // if CORNER, do nothing
- //x2 += x1; y2 += y1;
- // w/h are x2/y2 in this case, bring em down to size
- w = (w - x);
- h = (h - x);
- }
-
- if (x < 0) {
- w += x; // clip off the left edge
- x = 0;
- }
- if (y < 0) {
- h += y; // clip off some of the height
- y = 0;
- }
-
- if (x + w > width) w = width - x;
- if (y + h > height) h = height - y;
-
- PImage newbie = new PImage(new int[w*h], w, h, format);
-
- int index = y*width + x;
- int index2 = 0;
- for (int row = y; row < y+h; row++) {
- System.arraycopy(pixels, index,
- newbie.pixels, index2, w);
- index+=width;
- index2+=w;
- }
- return newbie;
- }
-
-
- /**
- * Returns a copy of this PImage. Equivalent to get(0, 0, width, height).
- */
- public PImage get() {
- try {
- return (PImage) clone();
- } catch (CloneNotSupportedException e) {
- return null;
- }
- }
-
-
- /**
- * Silently ignores if the coordinate is outside the image.
- */
- public void set(int x, int y, int c) {
- if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return;
- pixels[y*width + x] = c;
- }
-
-
- public void set(int dx, int dy, PImage src) {
- int sx = 0;
- int sy = 0;
- int sw = src.width;
- int sh = src.height;
-
- if (dx < 0) { // off left edge
- sx -= dx;
- sw += dx;
- dx = 0;
- }
- if (dy < 0) { // off top edge
- sy -= dy;
- sh += dy;
- dy = 0;
- }
- if (dx + sw > width) { // off right edge
- sw = width - dx;
- }
- if (dy + sh > height) { // off bottom edge
- sh = height - dy;
- }
-
- // this could be nonexistant
- if ((sw <= 0) || (sh <= 0)) return;
-
- setImpl(dx, dy, sx, sy, sw, sh, src);
- }
-
-
- /**
- * Internal function to actually handle setting a block of pixels that
- * has already been properly cropped from the image to a valid region.
- */
- protected void setImpl(int dx, int dy, int sx, int sy, int sw, int sh,
- PImage src) {
- int srcOffset = sy * src.width + sx;
- int dstOffset = dy * width + dx;
-
- for (int y = sy; y < sy + sh; y++) {
- System.arraycopy(src.pixels, srcOffset, pixels, dstOffset, sw);
- srcOffset += src.width;
- dstOffset += width;
- }
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // ALPHA CHANNEL
-
-
- /**
- * Set alpha channel for an image. Black colors in the source
- * image will make the destination image completely transparent,
- * and white will make things fully opaque. Gray values will
- * be in-between steps.
- *
- * Strictly speaking the "blue" value from the source image is
- * used as the alpha color. For a fully grayscale image, this
- * is correct, but for a color image it's not 100% accurate.
- * For a more accurate conversion, first use filter(GRAY)
- * which will make the image into a "correct" grayscake by
- * performing a proper luminance-based conversion.
- */
- public void mask(int alpha[]) {
- // don't execute if mask image is different size
- if (alpha.length != pixels.length) {
- throw new RuntimeException("The PImage used with mask() must be " +
- "the same size as the applet.");
- }
- for (int i = 0; i < pixels.length; i++) {
- pixels[i] = ((alpha[i] & 0xff) << 24) | (pixels[i] & 0xffffff);
- }
- format = ARGB;
- }
-
-
- /**
- * Set alpha channel for an image using another image as the source.
- */
- public void mask(PImage alpha) {
- mask(alpha.pixels);
- }
-
-
- /**
- * Method to apply a variety of basic filters to this image.
- *
- *
- *
- *
- */
- public InputStream openStream(String filename) {
- InputStream stream = null;
-
- if (filename == null) return null;
-
- if (filename.length() == 0) {
- // an error will be called by the parent function
- //System.err.println("The filename passed to openStream() was empty.");
- return null;
- }
-
- // by default, data files are exported to the root path of the jar.
- // (not the data folder) so check there first.
- // using getClassLoader() prevents java from converting dots
- // to slashes or requiring a slash at the beginning.
- // (a slash as a prefix means that it'll load from the root of
- // the jar, rather than trying to dig into the package location)
- ClassLoader cl = getClass().getClassLoader();
- stream = cl.getResourceAsStream("data/" + filename);
- if (stream != null) {
- String cn = stream.getClass().getName();
- // this is an irritation of sun's java plug-in, which will return
- // a non-null stream for an object that doesn't exist. like all good
- // things, this is probably introduced in java 1.5. awesome!
- // http://dev.processing.org/bugs/show_bug.cgi?id=359
- if (!cn.equals("sun.plugin.cache.EmptyInputStream")) {
- return stream;
- }
- }
-
- try {
- URL url = new URL(filename);
- stream = url.openStream();
- return stream;
-
- } catch (MalformedURLException e) {
- // not a url, that's fine
-
- } catch (IOException e) {
- throw new RuntimeException("Error downloading from URL " + filename);
- }
-
- // handle case sensitivity check
- if (!online) {
- try {
- // first see if it's in a data folder
- File file = new File(dataPath(filename));
- if (!file.exists()) {
- // next see if it's just in this folder
- file = new File(sketchPath, filename);
- }
- if (file.exists()) {
- try {
- String filePath = file.getCanonicalPath();
- String filenameActual = new File(filePath).getName();
- // make sure there isn't a subfolder prepended to the name
- String filenameShort = new File(filename).getName();
- // if the actual filename is the same, but capitalized
- // differently, warn the user.
- //if (filenameActual.equalsIgnoreCase(filenameShort) &&
- //!filenameActual.equals(filenameShort)) {
- if (!filenameActual.equals(filenameShort)) {
- throw new RuntimeException("This file is named " +
- filenameActual + " not " +
- filename + ". Re-name it " +
- "or change your code.");
- }
- } catch (IOException e) { }
- }
-
- // if this file is ok, may as well just load it
- stream = new FileInputStream(file);
- if (stream != null) return stream;
-
- // have to break these out because a general Exception might
- // catch the RuntimeException being thrown above
- } catch (IOException ioe) {
- } catch (SecurityException se) { }
- }
-
- try {
- // attempt to load from a local file, used when running as
- // an application, or as a signed applet
- try { // first try to catch any security exceptions
- try {
- stream = new FileInputStream(dataPath(filename));
- if (stream != null) return stream;
- } catch (IOException e2) { }
-
- try {
- stream = new FileInputStream(sketchPath(filename));
- if (stream != null) return stream;
- } catch (Exception e) { } // ignored
-
- try {
- stream = new FileInputStream(filename);
- if (stream != null) return stream;
- } catch (IOException e1) { }
-
- } catch (SecurityException se) { } // online, whups
-
- } catch (Exception e) {
- //die(e.getMessage(), e);
- e.printStackTrace();
- }
- return null;
- }
-
-
- public byte[] loadBytes(String filename) {
- InputStream is = openStream(filename);
- if (is != null) return loadBytes(is);
-
- System.err.println("The file \"" + filename + "\" " +
- "is missing or inaccessible, make sure " +
- "it's been added to your sketch and is readable.");
- return null;
- }
-
-
- static public byte[] loadBytes(InputStream input) {
- try {
- BufferedInputStream bis = new BufferedInputStream(input);
- ByteArrayOutputStream out = new ByteArrayOutputStream();
-
- int c = bis.read();
- while (c != -1) {
- out.write(c);
- c = bis.read();
- }
- return out.toByteArray();
-
- } catch (IOException e) {
- e.printStackTrace();
- //throw new RuntimeException("Couldn't load bytes from stream");
- }
- return null;
- }
-
-
- static public String[] loadStrings(File file) {
- InputStream is = openStream(file);
- if (is != null) return loadStrings(is);
- return null;
- }
-
-
- public String[] loadStrings(String filename) {
- InputStream is = openStream(filename);
- if (is != null) return loadStrings(is);
-
- System.err.println("The file \"" + filename + "\" " +
- "is missing or inaccessible, make sure " +
- "it's been added to your sketch and is readable.");
- return null;
- }
-
-
- static public String[] loadStrings(InputStream input) {
- try {
- BufferedReader reader =
- new BufferedReader(new InputStreamReader(input));
-
- String lines[] = new String[100];
- int lineCount = 0;
- String line = null;
- while ((line = reader.readLine()) != null) {
- if (lineCount == lines.length) {
- String temp[] = new String[lineCount << 1];
- System.arraycopy(lines, 0, temp, 0, lineCount);
- lines = temp;
- }
- lines[lineCount++] = line;
- }
- reader.close();
-
- if (lineCount == lines.length) {
- return lines;
- }
-
- // resize array to appropriate amount for these lines
- String output[] = new String[lineCount];
- System.arraycopy(lines, 0, output, 0, lineCount);
- return output;
-
- } catch (IOException e) {
- e.printStackTrace();
- //throw new RuntimeException("Error inside loadStrings()");
- }
- return null;
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // FILE OUTPUT
-
-
- /**
- * Saves bytes to a file to inside the sketch folder.
- * The filename can be a relative path, i.e. "poo/bytefun.txt"
- * would save to a file named "bytefun.txt" to a subfolder
- * called 'poo' inside the sketch folder. If the in-between
- * subfolders don't exist, they'll be created.
- */
- public void saveBytes(String filename, byte buffer[]) {
- try {
- String location = savePath(filename);
- FileOutputStream fos = new FileOutputStream(location);
- saveBytes(fos, buffer);
- fos.close();
-
- } catch (IOException e) {
- System.err.println("error saving bytes to " + filename);
- e.printStackTrace();
- }
- }
-
- /**
- * Saves bytes to a specific File location specified by the user.
- */
- static public void saveBytes(File file, byte buffer[]) {
- try {
- String filename = file.getAbsolutePath();
- createPath(filename);
- FileOutputStream fos = new FileOutputStream(file);
- saveBytes(fos, buffer);
- fos.close();
-
- } catch (IOException e) {
- System.err.println("error saving bytes to " + file);
- e.printStackTrace();
- }
- }
-
-
- /**
- * Spews a buffer of bytes to an OutputStream.
- */
- static public void saveBytes(OutputStream output, byte buffer[]) {
- try {
- //BufferedOutputStream bos = new BufferedOutputStream(output);
- output.write(buffer);
- output.flush();
-
- } catch (IOException e) {
- e.printStackTrace();
- throw new RuntimeException("Couldn't save bytes");
- }
- }
-
- //
-
- public void saveStrings(String filename, String strings[]) {
- try {
- String location = savePath(filename);
- FileOutputStream fos = new FileOutputStream(location);
- saveStrings(fos, strings);
- fos.close();
-
- } catch (IOException e) {
- e.printStackTrace();
- throw new RuntimeException("saveStrings() failed: " + e.getMessage());
- }
- }
-
-
- static public void saveStrings(File file, String strings[]) {
- try {
- String location = file.getAbsolutePath();
- createPath(location);
- FileOutputStream fos = new FileOutputStream(location);
- saveStrings(fos, strings);
- fos.close();
-
- } catch (IOException e) {
- System.err.println("error while saving strings");
- e.printStackTrace();
- }
- }
-
-
- static public void saveStrings(OutputStream output, String strings[]) {
- PrintWriter writer =
- new PrintWriter(new OutputStreamWriter(output));
- for (int i = 0; i < strings.length; i++) {
- writer.println(strings[i]);
- }
- writer.flush();
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- /**
- * Prepend the path to the sketch folder to the filename or
- * path that is passed in. Can be used by applets or external
- * libraries to save to the sketch folder.
- */
- public String sketchPath(String where) {
- if (sketchPath == null) {
- throw new RuntimeException("Applet not inited properly, " +
- "the sketch path could not be determined.");
- }
- // isAbsolute() could throw an access exception, but so will writing
- // to the local disk using the sketch path, so this is safe here.
- if (new File(where).isAbsolute()) return where;
-
- return sketchPath + File.separator + where;
- }
-
-
- /**
- * Returns a path inside the applet folder to save to,
- * just like sketchPath(), but also creates any in-between
- * folders so that things save properly.
- *
- * All saveXxxx() functions use the path to the sketch folder, rather than
- * its data folder. Once exported, the data folder will be found inside the
- * jar file of the exported application or applet. In this case, it's not
- * possible to save data into the jar file, because it will often be running
- * from a server, or marked in-use if running from a local file system.
- * With this in mind, saving to the data path doesn't make sense anyway.
- * If you know you're running locally, and want to save to the data folder,
- * use saveXxxx("data/blah.dat").
- */
- public String savePath(String where) {
- String filename = sketchPath(where);
- createPath(filename);
- return filename;
- }
-
-
- /**
- * Return a full path to an item in the data folder.
- * arraycopy(src, 0, dst, 0, length);
- */
- static public void arraycopy(Object src, Object dst, int length) {
- System.arraycopy(src, 0, dst, 0, length);
- }
-
-
- /**
- * Shortcut to copy the entire contents of
- * the source into the destination array.
- * Identical to arraycopy(src, 0, dst, 0, src.length);
- */
- static public void arraycopy(Object src, Object dst) {
- System.arraycopy(src, 0, dst, 0, Array.getLength(src));
- }
-
-
- static public boolean[] expand(boolean list[]) {
- return expand(list, list.length << 1);
- }
-
- static public boolean[] expand(boolean list[], int newSize) {
- boolean temp[] = new boolean[newSize];
- System.arraycopy(list, 0, temp, 0, Math.min(newSize, list.length));
- return temp;
- }
-
-
- static public byte[] expand(byte list[]) {
- return expand(list, list.length << 1);
- }
-
- static public byte[] expand(byte list[], int newSize) {
- byte temp[] = new byte[newSize];
- System.arraycopy(list, 0, temp, 0, Math.min(newSize, list.length));
- return temp;
- }
-
-
- static public char[] expand(char list[]) {
- return expand(list, list.length << 1);
- }
-
- static public char[] expand(char list[], int newSize) {
- char temp[] = new char[newSize];
- System.arraycopy(list, 0, temp, 0, Math.min(newSize, list.length));
- return temp;
- }
-
-
- static public int[] expand(int list[]) {
- return expand(list, list.length << 1);
- }
-
- static public int[] expand(int list[], int newSize) {
- int temp[] = new int[newSize];
- System.arraycopy(list, 0, temp, 0, Math.min(newSize, list.length));
- return temp;
- }
-
-
- static public float[] expand(float list[]) {
- return expand(list, list.length << 1);
- }
-
- static public float[] expand(float list[], int newSize) {
- float temp[] = new float[newSize];
- System.arraycopy(list, 0, temp, 0, Math.min(newSize, list.length));
- return temp;
- }
-
-
- static public String[] expand(String list[]) {
- return expand(list, list.length << 1);
- }
-
- static public String[] expand(String list[], int newSize) {
- String temp[] = new String[newSize];
- // in case the new size is smaller than list.length
- System.arraycopy(list, 0, temp, 0, Math.min(newSize, list.length));
- return temp;
- }
-
-
- static public Object expand(Object array) {
- return expand(array, Array.getLength(array) << 1);
- }
-
- static public Object expand(Object list, int newSize) {
- Class type = list.getClass().getComponentType();
- Object temp = Array.newInstance(type, newSize);
- System.arraycopy(list, 0, temp, 0,
- Math.min(Array.getLength(list), newSize));
- return temp;
- }
-
- //
-
- static public boolean[] contract(boolean list[], int newSize) {
- return expand(list, newSize);
- }
-
- static public byte[] contract(byte list[], int newSize) {
- return expand(list, newSize);
- }
-
- static public char[] contract(char list[], int newSize) {
- return expand(list, newSize);
- }
-
- static public int[] contract(int list[], int newSize) {
- return expand(list, newSize);
- }
-
- static public float[] contract(float list[], int newSize) {
- return expand(list, newSize);
- }
-
- static public String[] contract(String list[], int newSize) {
- return expand(list, newSize);
- }
-
- static public Object contract(Object list, int newSize) {
- return expand(list, newSize);
- }
-
- //
-
- static public byte[] append(byte b[], byte value) {
- b = expand(b, b.length + 1);
- b[b.length-1] = value;
- return b;
- }
-
- static public char[] append(char b[], char value) {
- b = expand(b, b.length + 1);
- b[b.length-1] = value;
- return b;
- }
-
- static public int[] append(int b[], int value) {
- b = expand(b, b.length + 1);
- b[b.length-1] = value;
- return b;
- }
-
- static public float[] append(float b[], float value) {
- b = expand(b, b.length + 1);
- b[b.length-1] = value;
- return b;
- }
-
- static public String[] append(String b[], String value) {
- b = expand(b, b.length + 1);
- b[b.length-1] = value;
- return b;
- }
-
- static public Object append(Object b, Object value) {
- int length = Array.getLength(b);
- b = expand(b, length + 1);
- Array.set(b, length, value);
- return b;
- }
-
- //
-
- static public boolean[] shorten(boolean list[]) {
- return contract(list, list.length-1);
- }
-
- static public byte[] shorten(byte list[]) {
- return contract(list, list.length-1);
- }
-
- static public char[] shorten(char list[]) {
- return contract(list, list.length-1);
- }
-
- static public int[] shorten(int list[]) {
- return contract(list, list.length-1);
- }
-
- static public float[] shorten(float list[]) {
- return contract(list, list.length-1);
- }
-
- static public String[] shorten(String list[]) {
- return contract(list, list.length-1);
- }
-
- static public Object shorten(Object list) {
- int length = Array.getLength(list);
- //Object b = Array.get(list, length - 1);
- return contract(list, length - 1);
- }
-
- //
-
- static final public boolean[] splice(boolean list[],
- boolean v, int index) {
- boolean outgoing[] = new boolean[list.length + 1];
- System.arraycopy(list, 0, outgoing, 0, index);
- outgoing[index] = v;
- System.arraycopy(list, index, outgoing, index + 1,
- list.length - index);
- return outgoing;
- }
-
- static final public boolean[] splice(boolean list[],
- boolean v[], int index) {
- boolean outgoing[] = new boolean[list.length + v.length];
- System.arraycopy(list, 0, outgoing, 0, index);
- System.arraycopy(v, 0, outgoing, index, v.length);
- System.arraycopy(list, index, outgoing, index + v.length,
- list.length - index);
- return outgoing;
- }
-
-
- static final public byte[] splice(byte list[],
- byte v, int index) {
- byte outgoing[] = new byte[list.length + 1];
- System.arraycopy(list, 0, outgoing, 0, index);
- outgoing[index] = v;
- System.arraycopy(list, index, outgoing, index + 1,
- list.length - index);
- return outgoing;
- }
-
- static final public byte[] splice(byte list[],
- byte v[], int index) {
- byte outgoing[] = new byte[list.length + v.length];
- System.arraycopy(list, 0, outgoing, 0, index);
- System.arraycopy(v, 0, outgoing, index, v.length);
- System.arraycopy(list, index, outgoing, index + v.length,
- list.length - index);
- return outgoing;
- }
-
-
- static final public char[] splice(char list[],
- char v, int index) {
- char outgoing[] = new char[list.length + 1];
- System.arraycopy(list, 0, outgoing, 0, index);
- outgoing[index] = v;
- System.arraycopy(list, index, outgoing, index + 1,
- list.length - index);
- return outgoing;
- }
-
- static final public char[] splice(char list[],
- char v[], int index) {
- char outgoing[] = new char[list.length + v.length];
- System.arraycopy(list, 0, outgoing, 0, index);
- System.arraycopy(v, 0, outgoing, index, v.length);
- System.arraycopy(list, index, outgoing, index + v.length,
- list.length - index);
- return outgoing;
- }
-
-
- static final public int[] splice(int list[],
- int v, int index) {
- int outgoing[] = new int[list.length + 1];
- System.arraycopy(list, 0, outgoing, 0, index);
- outgoing[index] = v;
- System.arraycopy(list, index, outgoing, index + 1,
- list.length - index);
- return outgoing;
- }
-
- static final public int[] splice(int list[],
- int v[], int index) {
- int outgoing[] = new int[list.length + v.length];
- System.arraycopy(list, 0, outgoing, 0, index);
- System.arraycopy(v, 0, outgoing, index, v.length);
- System.arraycopy(list, index, outgoing, index + v.length,
- list.length - index);
- return outgoing;
- }
-
-
- static final public float[] splice(float list[],
- float v, int index) {
- float outgoing[] = new float[list.length + 1];
- System.arraycopy(list, 0, outgoing, 0, index);
- outgoing[index] = v;
- System.arraycopy(list, index, outgoing, index + 1,
- list.length - index);
- return outgoing;
- }
-
- static final public float[] splice(float list[],
- float v[], int index) {
- float outgoing[] = new float[list.length + v.length];
- System.arraycopy(list, 0, outgoing, 0, index);
- System.arraycopy(v, 0, outgoing, index, v.length);
- System.arraycopy(list, index, outgoing, index + v.length,
- list.length - index);
- return outgoing;
- }
-
-
- static final public String[] splice(String list[],
- String v, int index) {
- String outgoing[] = new String[list.length + 1];
- System.arraycopy(list, 0, outgoing, 0, index);
- outgoing[index] = v;
- System.arraycopy(list, index, outgoing, index + 1,
- list.length - index);
- return outgoing;
- }
-
- static final public String[] splice(String list[],
- String v[], int index) {
- String outgoing[] = new String[list.length + v.length];
- System.arraycopy(list, 0, outgoing, 0, index);
- System.arraycopy(v, 0, outgoing, index, v.length);
- System.arraycopy(list, index, outgoing, index + v.length,
- list.length - index);
- return outgoing;
- }
-
-
- static final public Object splice(Object list, Object v, int index) {
- Object[] outgoing = null;
- int length = Array.getLength(list);
-
- // check whether is an array or not, and if so, treat as such
- if (list.getClass().getName().charAt(0) == '[') {
- int vlength = Array.getLength(v);
- outgoing = new Object[length + vlength];
- System.arraycopy(list, 0, outgoing, 0, index);
- System.arraycopy(v, 0, outgoing, index, vlength);
- System.arraycopy(list, index, outgoing, index + vlength, length - index);
-
- } else {
- outgoing = new Object[length + 1];
- System.arraycopy(list, 0, outgoing, 0, index);
- Array.set(outgoing, index, v);
- System.arraycopy(list, index, outgoing, index + 1, length - index);
- }
- return outgoing;
- }
-
- //
-
- static public boolean[] subset(boolean list[], int start) {
- return subset(list, start, list.length - start);
- }
-
- static public boolean[] subset(boolean list[], int start, int count) {
- boolean output[] = new boolean[count];
- System.arraycopy(list, start, output, 0, count);
- return output;
- }
-
-
- static public byte[] subset(byte list[], int start) {
- return subset(list, start, list.length - start);
- }
-
- static public byte[] subset(byte list[], int start, int count) {
- byte output[] = new byte[count];
- System.arraycopy(list, start, output, 0, count);
- return output;
- }
-
-
- static public char[] subset(char list[], int start) {
- return subset(list, start, list.length - start);
- }
-
- static public char[] subset(char list[], int start, int count) {
- char output[] = new char[count];
- System.arraycopy(list, start, output, 0, count);
- return output;
- }
-
-
- static public int[] subset(int list[], int start) {
- return subset(list, start, list.length - start);
- }
-
- static public int[] subset(int list[], int start, int count) {
- int output[] = new int[count];
- System.arraycopy(list, start, output, 0, count);
- return output;
- }
-
-
- static public float[] subset(float list[], int start) {
- return subset(list, start, list.length - start);
- }
-
- static public float[] subset(float list[], int start, int count) {
- float output[] = new float[count];
- System.arraycopy(list, start, output, 0, count);
- return output;
- }
-
-
- static public String[] subset(String list[], int start) {
- return subset(list, start, list.length - start);
- }
-
- static public String[] subset(String list[], int start, int count) {
- String output[] = new String[count];
- System.arraycopy(list, start, output, 0, count);
- return output;
- }
-
-
- static public Object subset(Object list, int start) {
- int length = Array.getLength(list);
- int count = length - start;
- Class type = list.getClass().getComponentType();
- Object outgoing = Array.newInstance(type, count);
- System.arraycopy(list, 0, outgoing, 0, count);
- return outgoing;
- }
-
- static public Object subset(Object list, int start, int count) {
- int length = Array.getLength(list);
- Class type = list.getClass().getComponentType();
- Object outgoing = Array.newInstance(type, count);
- System.arraycopy(list, start, outgoing, 0, count);
- return outgoing;
- }
-
- //
-
- static public boolean[] concat(boolean a[], boolean b[]) {
- boolean c[] = new boolean[a.length + b.length];
- System.arraycopy(a, 0, c, 0, a.length);
- System.arraycopy(b, 0, c, a.length, b.length);
- return c;
- }
-
- static public byte[] concat(byte a[], byte b[]) {
- byte c[] = new byte[a.length + b.length];
- System.arraycopy(a, 0, c, 0, a.length);
- System.arraycopy(b, 0, c, a.length, b.length);
- return c;
- }
-
- static public char[] concat(char a[], char b[]) {
- char c[] = new char[a.length + b.length];
- System.arraycopy(a, 0, c, 0, a.length);
- System.arraycopy(b, 0, c, a.length, b.length);
- return c;
- }
-
- static public int[] concat(int a[], int b[]) {
- int c[] = new int[a.length + b.length];
- System.arraycopy(a, 0, c, 0, a.length);
- System.arraycopy(b, 0, c, a.length, b.length);
- return c;
- }
-
- static public float[] concat(float a[], float b[]) {
- float c[] = new float[a.length + b.length];
- System.arraycopy(a, 0, c, 0, a.length);
- System.arraycopy(b, 0, c, a.length, b.length);
- return c;
- }
-
- static public String[] concat(String a[], String b[]) {
- String c[] = new String[a.length + b.length];
- System.arraycopy(a, 0, c, 0, a.length);
- System.arraycopy(b, 0, c, a.length, b.length);
- return c;
- }
-
- static public Object concat(Object a, Object b) {
- Class type = a.getClass().getComponentType();
- int alength = Array.getLength(a);
- int blength = Array.getLength(b);
- Object outgoing = Array.newInstance(type, alength + blength);
- System.arraycopy(a, 0, outgoing, 0, alength);
- System.arraycopy(b, 0, outgoing, alength, blength);
- return outgoing;
- }
-
- //
-
- static public boolean[] reverse(boolean list[]) {
- boolean outgoing[] = new boolean[list.length];
- int length1 = list.length - 1;
- for (int i = 0; i < list.length; i++) {
- outgoing[i] = list[length1 - i];
- }
- return outgoing;
- }
-
- static public byte[] reverse(byte list[]) {
- byte outgoing[] = new byte[list.length];
- int length1 = list.length - 1;
- for (int i = 0; i < list.length; i++) {
- outgoing[i] = list[length1 - i];
- }
- return outgoing;
- }
-
- static public char[] reverse(char list[]) {
- char outgoing[] = new char[list.length];
- int length1 = list.length - 1;
- for (int i = 0; i < list.length; i++) {
- outgoing[i] = list[length1 - i];
- }
- return outgoing;
- }
-
- static public int[] reverse(int list[]) {
- int outgoing[] = new int[list.length];
- int length1 = list.length - 1;
- for (int i = 0; i < list.length; i++) {
- outgoing[i] = list[length1 - i];
- }
- return outgoing;
- }
-
- static public float[] reverse(float list[]) {
- float outgoing[] = new float[list.length];
- int length1 = list.length - 1;
- for (int i = 0; i < list.length; i++) {
- outgoing[i] = list[length1 - i];
- }
- return outgoing;
- }
-
- static public String[] reverse(String list[]) {
- String outgoing[] = new String[list.length];
- int length1 = list.length - 1;
- for (int i = 0; i < list.length; i++) {
- outgoing[i] = list[length1 - i];
- }
- return outgoing;
- }
-
- static public Object reverse(Object list) {
- Class type = list.getClass().getComponentType();
- int length = Array.getLength(list);
- Object outgoing = Array.newInstance(type, length);
- for (int i = 0; i < length; i++) {
- Array.set(outgoing, i, Array.get(list, (length - 1) - i));
- }
- return outgoing;
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // STRINGS
-
-
- /**
- * Remove whitespace characters from the beginning and ending
- * of a String. Works like String.trim() but includes the
- * unicode nbsp character as well.
- */
- static public String trim(String str) {
- return str.replace('\u00A0', ' ').trim();
-
- /*
- int left = 0;
- int right = str.length() - 1;
-
- while ((left <= right) &&
- (WHITESPACE.indexOf(str.charAt(left)) != -1)) left++;
- if (left == right) return "";
-
- while (WHITESPACE.indexOf(str.charAt(right)) != -1) --right;
-
- return str.substring(left, right-left+1);
- */
- }
-
- /**
- * Join an array of Strings together as a single String,
- * separated by the whatever's passed in for the separator.
- */
- static public String join(String str[], char separator) {
- return join(str, String.valueOf(separator));
- }
-
-
- /**
- * Join an array of Strings together as a single String,
- * separated by the whatever's passed in for the separator.
- *
- * 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 or later.
- *
- * --hide-stop use to hide the stop button in situations where
- * you don't want to allow users to exit. also
- * see the FAQ on information for capturing the ESC
- * key when running in presentation mode.
- *
- * --stop-color color of the 'stop' text used to quit an
- * sketch when it's in present mode.
- *
- * --bgcolor=#xxxxxx background color of the window.
- *
- * --sketch-path 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 via the PDE
- *
- * --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
- *
- */
- static public void main(String args[]) {
- if (args.length < 1) {
- System.err.println("Usage: PApplet
+ * 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.
+ *
+ * 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).
+ * open(new String[] { "firefox", url });
+ * or whatever you want as your browser, since Linux doesn't
+ * yet have a standard method for launching URLs.
+ */
+ public void link(String url, String frameTitle) {
+ if (online) {
+ try {
+ if (frameTitle == null) {
+ getAppletContext().showDocument(new URL(url));
+ } else {
+ getAppletContext().showDocument(new URL(url), frameTitle);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("Could not open " + url);
+ }
+ } else {
+ try {
+ if (platform == WINDOWS) {
+ // the following uses a shell execute to launch the .html file
+ // note that under cygwin, the .html files have to be chmodded +x
+ // after they're unpacked from the zip file. i don't know why,
+ // and don't understand what this does in terms of windows
+ // permissions. without the chmod, the command prompt says
+ // "Access is denied" in both cygwin and the "dos" prompt.
+ //Runtime.getRuntime().exec("cmd /c " + currentDir + "\\reference\\" +
+ // referenceFile + ".html");
+
+ // open dos prompt, give it 'start' command, which will
+ // open the url properly. start by itself won't work since
+ // it appears to need cmd
+ Runtime.getRuntime().exec("cmd /c start " + url);
+
+ } else if ((platform == MACOSX) || (platform == MACOS9)) {
+ //com.apple.mrj.MRJFileUtils.openURL(url);
+ try {
+ Class mrjFileUtils = Class.forName("com.apple.mrj.MRJFileUtils");
+ Method openMethod =
+ mrjFileUtils.getMethod("openURL", new Class[] { String.class });
+ openMethod.invoke(null, new Object[] { url });
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } else {
+ throw new RuntimeException("Can't open URLs for this platform");
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Could not open " + url);
+ }
+ }
+ }
+
+
+ /**
+ * Attempt to open a file using the platform's shell.
+ */
+ public void open(String filename) {
+ if (platform == WINDOWS) {
+ // just launching the .html file via the shell works
+ // but make sure to chmod +x the .html files first
+ // also place quotes around it in case there's a space
+ // in the user.dir part of the url
+ try {
+ Runtime.getRuntime().exec("cmd /c \"" + filename + "\"");
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Could not open " + filename);
+ }
+
+ } else if (platform == MACOSX) {
+ // osx fix contributed by chandler for rev 0113
+ try {
+ // Java on OS X doesn't like to exec commands inside quotes
+ // for some reason.. escape spaces with slashes just in case
+ if (filename.indexOf(' ') != -1) {
+ StringBuffer sb = new StringBuffer();
+ char c[] = filename.toCharArray();
+ for (int i = 0; i < c.length; i++) {
+ if (c[i] == ' ') {
+ sb.append("\\\\ ");
+ } else {
+ sb.append(c[i]);
+ }
+ }
+ filename = sb.toString();
+ }
+ Runtime.getRuntime().exec("open " + filename);
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Could not open " + filename);
+ }
+
+ } else if (platform == MACOS9) {
+ // prepend file:// on this guy since it's a file
+ String url = "file://" + filename;
+
+ // replace spaces with %20 for the file url
+ // otherwise the mac doesn't like to open it
+ // can't just use URLEncoder, since that makes slashes into
+ // %2F characters, which is no good. some might say "useless"
+ if (url.indexOf(' ') != -1) {
+ StringBuffer sb = new StringBuffer();
+ char c[] = url.toCharArray();
+ for (int i = 0; i < c.length; i++) {
+ if (c[i] == ' ') {
+ sb.append("%20");
+ } else {
+ sb.append(c[i]);
+ }
+ }
+ url = sb.toString();
+ }
+ link(url);
+
+ } else { // give up and just pass it to Runtime.exec()
+ open(new String[] { filename });
+ }
+ }
+
+
+ /**
+ * Launch a process using a platforms shell, and an array of
+ * args passed on the command line.
+ */
+ public Process open(String args[]) {
+ try {
+ return Runtime.getRuntime().exec(args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("Could not open " + join(args, ' '));
+ }
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ /**
+ * 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);
+ }
+
+
+ /**
+ * Call to safely exit the sketch when finished. For instance,
+ * to render a single frame, save it, and quit.
+ */
+ public void exit() {
+ if (thread == null) {
+ // exit immediately, stop() has already been called,
+ // meaning that the main thread has long since exited
+ if ((leechErr == null) && !online) {
+ // don't want to call System.exit() when an applet,
+ // or running inside the PDE (would kill the PDE)
+ System.exit(0);
+ }
+ } else {
+ finished = true; // stop() will be called as the thread exits
+ //stop();
+ exit = true;
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // SCREEN GRABASS
+
+
+ /**
+ * Intercepts any relative paths to make them absolute (relative
+ * to the sketch folder) before passing to save() in PImage.
+ * (Changed in 0100)
+ */
+ 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.
+ *
+ * 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;
+ }
+
+ g.save(savePath(insertFrame(what)));
+ /*
+ int first = what.indexOf('#');
+ int last = what.lastIndexOf('#');
+
+ if (first == -1) {
+ g.save(savePath(what));
+
+ } else {
+ String prefix = what.substring(0, first);
+ int count = last - first + 1;
+ String suffix = what.substring(last + 1);
+ g.save(savePath(prefix + nf(frameCount, count) + suffix));
+ }
+ */
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // CURSOR
+
+ //
+
+
+ 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) {
+ 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 InputStream openStream(String filename) {
+ InputStream stream = null;
+
+ if (filename == null) return null;
+
+ if (filename.length() == 0) {
+ // an error will be called by the parent function
+ //System.err.println("The filename passed to openStream() was empty.");
+ return null;
+ }
+
+ // by default, data files are exported to the root path of the jar.
+ // (not the data folder) so check there first.
+ // using getClassLoader() prevents java from converting dots
+ // to slashes or requiring a slash at the beginning.
+ // (a slash as a prefix means that it'll load from the root of
+ // the jar, rather than trying to dig into the package location)
+ ClassLoader cl = getClass().getClassLoader();
+ stream = cl.getResourceAsStream("data/" + filename);
+ if (stream != null) {
+ String cn = stream.getClass().getName();
+ // this is an irritation of sun's java plug-in, which will return
+ // a non-null stream for an object that doesn't exist. like all good
+ // things, this is probably introduced in java 1.5. awesome!
+ // http://dev.processing.org/bugs/show_bug.cgi?id=359
+ if (!cn.equals("sun.plugin.cache.EmptyInputStream")) {
+ return stream;
+ }
+ }
+
+ try {
+ URL url = new URL(filename);
+ stream = url.openStream();
+ return stream;
+
+ } catch (MalformedURLException e) {
+ // not a url, that's fine
+
+ } catch (IOException e) {
+ throw new RuntimeException("Error downloading from URL " + filename);
+ }
+
+ // handle case sensitivity check
+ if (!online) {
+ try {
+ // first see if it's in a data folder
+ File file = new File(dataPath(filename));
+ if (!file.exists()) {
+ // next see if it's just in this folder
+ file = new File(sketchPath, filename);
+ }
+ if (file.exists()) {
+ try {
+ String filePath = file.getCanonicalPath();
+ String filenameActual = new File(filePath).getName();
+ // make sure there isn't a subfolder prepended to the name
+ String filenameShort = new File(filename).getName();
+ // if the actual filename is the same, but capitalized
+ // differently, warn the user.
+ //if (filenameActual.equalsIgnoreCase(filenameShort) &&
+ //!filenameActual.equals(filenameShort)) {
+ if (!filenameActual.equals(filenameShort)) {
+ throw new RuntimeException("This file is named " +
+ filenameActual + " not " +
+ filename + ". Re-name it " +
+ "or change your code.");
+ }
+ } catch (IOException e) { }
+ }
+
+ // if this file is ok, may as well just load it
+ stream = new FileInputStream(file);
+ if (stream != null) return stream;
+
+ // have to break these out because a general Exception might
+ // catch the RuntimeException being thrown above
+ } catch (IOException ioe) {
+ } catch (SecurityException se) { }
+ }
+
+ try {
+ // attempt to load from a local file, used when running as
+ // an application, or as a signed applet
+ try { // first try to catch any security exceptions
+ try {
+ stream = new FileInputStream(dataPath(filename));
+ if (stream != null) return stream;
+ } catch (IOException e2) { }
+
+ try {
+ stream = new FileInputStream(sketchPath(filename));
+ if (stream != null) return stream;
+ } catch (Exception e) { } // ignored
+
+ try {
+ stream = new FileInputStream(filename);
+ if (stream != null) return stream;
+ } catch (IOException e1) { }
+
+ } catch (SecurityException se) { } // online, whups
+
+ } catch (Exception e) {
+ //die(e.getMessage(), e);
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ public byte[] loadBytes(String filename) {
+ InputStream is = openStream(filename);
+ if (is != null) return loadBytes(is);
+
+ System.err.println("The file \"" + filename + "\" " +
+ "is missing or inaccessible, make sure " +
+ "it's been added to your sketch and is readable.");
+ return null;
+ }
+
+
+ static public byte[] loadBytes(InputStream input) {
+ try {
+ BufferedInputStream bis = new BufferedInputStream(input);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ int c = bis.read();
+ while (c != -1) {
+ out.write(c);
+ c = bis.read();
+ }
+ return out.toByteArray();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ //throw new RuntimeException("Couldn't load bytes from stream");
+ }
+ return null;
+ }
+
+
+ static public String[] loadStrings(File file) {
+ InputStream is = openStream(file);
+ if (is != null) return loadStrings(is);
+ return null;
+ }
+
+
+ public String[] loadStrings(String filename) {
+ InputStream is = openStream(filename);
+ if (is != null) return loadStrings(is);
+
+ System.err.println("The file \"" + filename + "\" " +
+ "is missing or inaccessible, make sure " +
+ "it's been added to your sketch and is readable.");
+ return null;
+ }
+
+
+ static public String[] loadStrings(InputStream input) {
+ try {
+ BufferedReader reader =
+ new BufferedReader(new InputStreamReader(input));
+
+ String lines[] = new String[100];
+ int lineCount = 0;
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ if (lineCount == lines.length) {
+ String temp[] = new String[lineCount << 1];
+ System.arraycopy(lines, 0, temp, 0, lineCount);
+ lines = temp;
+ }
+ lines[lineCount++] = line;
+ }
+ reader.close();
+
+ if (lineCount == lines.length) {
+ return lines;
+ }
+
+ // resize array to appropriate amount for these lines
+ String output[] = new String[lineCount];
+ System.arraycopy(lines, 0, output, 0, lineCount);
+ return output;
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ //throw new RuntimeException("Error inside loadStrings()");
+ }
+ return null;
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // FILE OUTPUT
+
+
+ /**
+ * Saves bytes to a file to inside the sketch folder.
+ * The filename can be a relative path, i.e. "poo/bytefun.txt"
+ * would save to a file named "bytefun.txt" to a subfolder
+ * called 'poo' inside the sketch folder. If the in-between
+ * subfolders don't exist, they'll be created.
+ */
+ public void saveBytes(String filename, byte buffer[]) {
+ try {
+ String location = savePath(filename);
+ FileOutputStream fos = new FileOutputStream(location);
+ saveBytes(fos, buffer);
+ fos.close();
+
+ } catch (IOException e) {
+ System.err.println("error saving bytes to " + filename);
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Saves bytes to a specific File location specified by the user.
+ */
+ static public void saveBytes(File file, byte buffer[]) {
+ try {
+ String filename = file.getAbsolutePath();
+ createPath(filename);
+ FileOutputStream fos = new FileOutputStream(file);
+ saveBytes(fos, buffer);
+ fos.close();
+
+ } catch (IOException e) {
+ System.err.println("error saving bytes to " + file);
+ e.printStackTrace();
+ }
+ }
+
+
+ /**
+ * Spews a buffer of bytes to an OutputStream.
+ */
+ static public void saveBytes(OutputStream output, byte buffer[]) {
+ try {
+ //BufferedOutputStream bos = new BufferedOutputStream(output);
+ output.write(buffer);
+ output.flush();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Couldn't save bytes");
+ }
+ }
+
+ //
+
+ public void saveStrings(String filename, String strings[]) {
+ try {
+ String location = savePath(filename);
+ FileOutputStream fos = new FileOutputStream(location);
+ saveStrings(fos, strings);
+ fos.close();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException("saveStrings() failed: " + e.getMessage());
+ }
+ }
+
+
+ static public void saveStrings(File file, String strings[]) {
+ try {
+ String location = file.getAbsolutePath();
+ createPath(location);
+ FileOutputStream fos = new FileOutputStream(location);
+ saveStrings(fos, strings);
+ fos.close();
+
+ } catch (IOException e) {
+ System.err.println("error while saving strings");
+ e.printStackTrace();
+ }
+ }
+
+
+ static public void saveStrings(OutputStream output, String strings[]) {
+ PrintWriter writer =
+ new PrintWriter(new OutputStreamWriter(output));
+ for (int i = 0; i < strings.length; i++) {
+ writer.println(strings[i]);
+ }
+ writer.flush();
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ /**
+ * Prepend the path to the sketch folder to the filename or
+ * path that is passed in. Can be used by applets or external
+ * libraries to save to the sketch folder.
+ */
+ public String sketchPath(String where) {
+ if (sketchPath == null) {
+ throw new RuntimeException("Applet not inited properly, " +
+ "the sketch path could not be determined.");
+ }
+ // isAbsolute() could throw an access exception, but so will writing
+ // to the local disk using the sketch path, so this is safe here.
+ if (new File(where).isAbsolute()) return where;
+
+ return sketchPath + File.separator + where;
+ }
+
+
+ /**
+ * Returns a path inside the applet folder to save to,
+ * just like sketchPath(), but also creates any in-between
+ * folders so that things save properly.
+ *
+ * All saveXxxx() functions use the path to the sketch folder, rather than
+ * its data folder. Once exported, the data folder will be found inside the
+ * jar file of the exported application or applet. In this case, it's not
+ * possible to save data into the jar file, because it will often be running
+ * from a server, or marked in-use if running from a local file system.
+ * With this in mind, saving to the data path doesn't make sense anyway.
+ * If you know you're running locally, and want to save to the data folder,
+ * use saveXxxx("data/blah.dat").
+ */
+ public String savePath(String where) {
+ String filename = sketchPath(where);
+ createPath(filename);
+ return filename;
+ }
+
+
+ /**
+ * Return a full path to an item in the data folder.
+ * arraycopy(src, 0, dst, 0, length);
+ */
+ static public void arraycopy(Object src, Object dst, int length) {
+ System.arraycopy(src, 0, dst, 0, length);
+ }
+
+
+ /**
+ * Shortcut to copy the entire contents of
+ * the source into the destination array.
+ * Identical to arraycopy(src, 0, dst, 0, src.length);
+ */
+ static public void arraycopy(Object src, Object dst) {
+ System.arraycopy(src, 0, dst, 0, Array.getLength(src));
+ }
+
+
+ static public boolean[] expand(boolean list[]) {
+ return expand(list, list.length << 1);
+ }
+
+ static public boolean[] expand(boolean list[], int newSize) {
+ boolean temp[] = new boolean[newSize];
+ System.arraycopy(list, 0, temp, 0, Math.min(newSize, list.length));
+ return temp;
+ }
+
+
+ static public byte[] expand(byte list[]) {
+ return expand(list, list.length << 1);
+ }
+
+ static public byte[] expand(byte list[], int newSize) {
+ byte temp[] = new byte[newSize];
+ System.arraycopy(list, 0, temp, 0, Math.min(newSize, list.length));
+ return temp;
+ }
+
+
+ static public char[] expand(char list[]) {
+ return expand(list, list.length << 1);
+ }
+
+ static public char[] expand(char list[], int newSize) {
+ char temp[] = new char[newSize];
+ System.arraycopy(list, 0, temp, 0, Math.min(newSize, list.length));
+ return temp;
+ }
+
+
+ static public int[] expand(int list[]) {
+ return expand(list, list.length << 1);
+ }
+
+ static public int[] expand(int list[], int newSize) {
+ int temp[] = new int[newSize];
+ System.arraycopy(list, 0, temp, 0, Math.min(newSize, list.length));
+ return temp;
+ }
+
+
+ static public float[] expand(float list[]) {
+ return expand(list, list.length << 1);
+ }
+
+ static public float[] expand(float list[], int newSize) {
+ float temp[] = new float[newSize];
+ System.arraycopy(list, 0, temp, 0, Math.min(newSize, list.length));
+ return temp;
+ }
+
+
+ static public String[] expand(String list[]) {
+ return expand(list, list.length << 1);
+ }
+
+ static public String[] expand(String list[], int newSize) {
+ String temp[] = new String[newSize];
+ // in case the new size is smaller than list.length
+ System.arraycopy(list, 0, temp, 0, Math.min(newSize, list.length));
+ return temp;
+ }
+
+
+ static public Object expand(Object array) {
+ return expand(array, Array.getLength(array) << 1);
+ }
+
+ static public Object expand(Object list, int newSize) {
+ Class type = list.getClass().getComponentType();
+ Object temp = Array.newInstance(type, newSize);
+ System.arraycopy(list, 0, temp, 0,
+ Math.min(Array.getLength(list), newSize));
+ return temp;
+ }
+
+ //
+
+ static public boolean[] contract(boolean list[], int newSize) {
+ return expand(list, newSize);
+ }
+
+ static public byte[] contract(byte list[], int newSize) {
+ return expand(list, newSize);
+ }
+
+ static public char[] contract(char list[], int newSize) {
+ return expand(list, newSize);
+ }
+
+ static public int[] contract(int list[], int newSize) {
+ return expand(list, newSize);
+ }
+
+ static public float[] contract(float list[], int newSize) {
+ return expand(list, newSize);
+ }
+
+ static public String[] contract(String list[], int newSize) {
+ return expand(list, newSize);
+ }
+
+ static public Object contract(Object list, int newSize) {
+ return expand(list, newSize);
+ }
+
+ //
+
+ static public byte[] append(byte b[], byte value) {
+ b = expand(b, b.length + 1);
+ b[b.length-1] = value;
+ return b;
+ }
+
+ static public char[] append(char b[], char value) {
+ b = expand(b, b.length + 1);
+ b[b.length-1] = value;
+ return b;
+ }
+
+ static public int[] append(int b[], int value) {
+ b = expand(b, b.length + 1);
+ b[b.length-1] = value;
+ return b;
+ }
+
+ static public float[] append(float b[], float value) {
+ b = expand(b, b.length + 1);
+ b[b.length-1] = value;
+ return b;
+ }
+
+ static public String[] append(String b[], String value) {
+ b = expand(b, b.length + 1);
+ b[b.length-1] = value;
+ return b;
+ }
+
+ static public Object append(Object b, Object value) {
+ int length = Array.getLength(b);
+ b = expand(b, length + 1);
+ Array.set(b, length, value);
+ return b;
+ }
+
+ //
+
+ static public boolean[] shorten(boolean list[]) {
+ return contract(list, list.length-1);
+ }
+
+ static public byte[] shorten(byte list[]) {
+ return contract(list, list.length-1);
+ }
+
+ static public char[] shorten(char list[]) {
+ return contract(list, list.length-1);
+ }
+
+ static public int[] shorten(int list[]) {
+ return contract(list, list.length-1);
+ }
+
+ static public float[] shorten(float list[]) {
+ return contract(list, list.length-1);
+ }
+
+ static public String[] shorten(String list[]) {
+ return contract(list, list.length-1);
+ }
+
+ static public Object shorten(Object list) {
+ int length = Array.getLength(list);
+ //Object b = Array.get(list, length - 1);
+ return contract(list, length - 1);
+ }
+
+ //
+
+ static final public boolean[] splice(boolean list[],
+ boolean v, int index) {
+ boolean outgoing[] = new boolean[list.length + 1];
+ System.arraycopy(list, 0, outgoing, 0, index);
+ outgoing[index] = v;
+ System.arraycopy(list, index, outgoing, index + 1,
+ list.length - index);
+ return outgoing;
+ }
+
+ static final public boolean[] splice(boolean list[],
+ boolean v[], int index) {
+ boolean outgoing[] = new boolean[list.length + v.length];
+ System.arraycopy(list, 0, outgoing, 0, index);
+ System.arraycopy(v, 0, outgoing, index, v.length);
+ System.arraycopy(list, index, outgoing, index + v.length,
+ list.length - index);
+ return outgoing;
+ }
+
+
+ static final public byte[] splice(byte list[],
+ byte v, int index) {
+ byte outgoing[] = new byte[list.length + 1];
+ System.arraycopy(list, 0, outgoing, 0, index);
+ outgoing[index] = v;
+ System.arraycopy(list, index, outgoing, index + 1,
+ list.length - index);
+ return outgoing;
+ }
+
+ static final public byte[] splice(byte list[],
+ byte v[], int index) {
+ byte outgoing[] = new byte[list.length + v.length];
+ System.arraycopy(list, 0, outgoing, 0, index);
+ System.arraycopy(v, 0, outgoing, index, v.length);
+ System.arraycopy(list, index, outgoing, index + v.length,
+ list.length - index);
+ return outgoing;
+ }
+
+
+ static final public char[] splice(char list[],
+ char v, int index) {
+ char outgoing[] = new char[list.length + 1];
+ System.arraycopy(list, 0, outgoing, 0, index);
+ outgoing[index] = v;
+ System.arraycopy(list, index, outgoing, index + 1,
+ list.length - index);
+ return outgoing;
+ }
+
+ static final public char[] splice(char list[],
+ char v[], int index) {
+ char outgoing[] = new char[list.length + v.length];
+ System.arraycopy(list, 0, outgoing, 0, index);
+ System.arraycopy(v, 0, outgoing, index, v.length);
+ System.arraycopy(list, index, outgoing, index + v.length,
+ list.length - index);
+ return outgoing;
+ }
+
+
+ static final public int[] splice(int list[],
+ int v, int index) {
+ int outgoing[] = new int[list.length + 1];
+ System.arraycopy(list, 0, outgoing, 0, index);
+ outgoing[index] = v;
+ System.arraycopy(list, index, outgoing, index + 1,
+ list.length - index);
+ return outgoing;
+ }
+
+ static final public int[] splice(int list[],
+ int v[], int index) {
+ int outgoing[] = new int[list.length + v.length];
+ System.arraycopy(list, 0, outgoing, 0, index);
+ System.arraycopy(v, 0, outgoing, index, v.length);
+ System.arraycopy(list, index, outgoing, index + v.length,
+ list.length - index);
+ return outgoing;
+ }
+
+
+ static final public float[] splice(float list[],
+ float v, int index) {
+ float outgoing[] = new float[list.length + 1];
+ System.arraycopy(list, 0, outgoing, 0, index);
+ outgoing[index] = v;
+ System.arraycopy(list, index, outgoing, index + 1,
+ list.length - index);
+ return outgoing;
+ }
+
+ static final public float[] splice(float list[],
+ float v[], int index) {
+ float outgoing[] = new float[list.length + v.length];
+ System.arraycopy(list, 0, outgoing, 0, index);
+ System.arraycopy(v, 0, outgoing, index, v.length);
+ System.arraycopy(list, index, outgoing, index + v.length,
+ list.length - index);
+ return outgoing;
+ }
+
+
+ static final public String[] splice(String list[],
+ String v, int index) {
+ String outgoing[] = new String[list.length + 1];
+ System.arraycopy(list, 0, outgoing, 0, index);
+ outgoing[index] = v;
+ System.arraycopy(list, index, outgoing, index + 1,
+ list.length - index);
+ return outgoing;
+ }
+
+ static final public String[] splice(String list[],
+ String v[], int index) {
+ String outgoing[] = new String[list.length + v.length];
+ System.arraycopy(list, 0, outgoing, 0, index);
+ System.arraycopy(v, 0, outgoing, index, v.length);
+ System.arraycopy(list, index, outgoing, index + v.length,
+ list.length - index);
+ return outgoing;
+ }
+
+
+ static final public Object splice(Object list, Object v, int index) {
+ Object[] outgoing = null;
+ int length = Array.getLength(list);
+
+ // check whether is an array or not, and if so, treat as such
+ if (list.getClass().getName().charAt(0) == '[') {
+ int vlength = Array.getLength(v);
+ outgoing = new Object[length + vlength];
+ System.arraycopy(list, 0, outgoing, 0, index);
+ System.arraycopy(v, 0, outgoing, index, vlength);
+ System.arraycopy(list, index, outgoing, index + vlength, length - index);
+
+ } else {
+ outgoing = new Object[length + 1];
+ System.arraycopy(list, 0, outgoing, 0, index);
+ Array.set(outgoing, index, v);
+ System.arraycopy(list, index, outgoing, index + 1, length - index);
+ }
+ return outgoing;
+ }
+
+ //
+
+ static public boolean[] subset(boolean list[], int start) {
+ return subset(list, start, list.length - start);
+ }
+
+ static public boolean[] subset(boolean list[], int start, int count) {
+ boolean output[] = new boolean[count];
+ System.arraycopy(list, start, output, 0, count);
+ return output;
+ }
+
+
+ static public byte[] subset(byte list[], int start) {
+ return subset(list, start, list.length - start);
+ }
+
+ static public byte[] subset(byte list[], int start, int count) {
+ byte output[] = new byte[count];
+ System.arraycopy(list, start, output, 0, count);
+ return output;
+ }
+
+
+ static public char[] subset(char list[], int start) {
+ return subset(list, start, list.length - start);
+ }
+
+ static public char[] subset(char list[], int start, int count) {
+ char output[] = new char[count];
+ System.arraycopy(list, start, output, 0, count);
+ return output;
+ }
+
+
+ static public int[] subset(int list[], int start) {
+ return subset(list, start, list.length - start);
+ }
+
+ static public int[] subset(int list[], int start, int count) {
+ int output[] = new int[count];
+ System.arraycopy(list, start, output, 0, count);
+ return output;
+ }
+
+
+ static public float[] subset(float list[], int start) {
+ return subset(list, start, list.length - start);
+ }
+
+ static public float[] subset(float list[], int start, int count) {
+ float output[] = new float[count];
+ System.arraycopy(list, start, output, 0, count);
+ return output;
+ }
+
+
+ static public String[] subset(String list[], int start) {
+ return subset(list, start, list.length - start);
+ }
+
+ static public String[] subset(String list[], int start, int count) {
+ String output[] = new String[count];
+ System.arraycopy(list, start, output, 0, count);
+ return output;
+ }
+
+
+ static public Object subset(Object list, int start) {
+ int length = Array.getLength(list);
+ int count = length - start;
+ Class type = list.getClass().getComponentType();
+ Object outgoing = Array.newInstance(type, count);
+ System.arraycopy(list, 0, outgoing, 0, count);
+ return outgoing;
+ }
+
+ static public Object subset(Object list, int start, int count) {
+ int length = Array.getLength(list);
+ Class type = list.getClass().getComponentType();
+ Object outgoing = Array.newInstance(type, count);
+ System.arraycopy(list, start, outgoing, 0, count);
+ return outgoing;
+ }
+
+ //
+
+ static public boolean[] concat(boolean a[], boolean b[]) {
+ boolean c[] = new boolean[a.length + b.length];
+ System.arraycopy(a, 0, c, 0, a.length);
+ System.arraycopy(b, 0, c, a.length, b.length);
+ return c;
+ }
+
+ static public byte[] concat(byte a[], byte b[]) {
+ byte c[] = new byte[a.length + b.length];
+ System.arraycopy(a, 0, c, 0, a.length);
+ System.arraycopy(b, 0, c, a.length, b.length);
+ return c;
+ }
+
+ static public char[] concat(char a[], char b[]) {
+ char c[] = new char[a.length + b.length];
+ System.arraycopy(a, 0, c, 0, a.length);
+ System.arraycopy(b, 0, c, a.length, b.length);
+ return c;
+ }
+
+ static public int[] concat(int a[], int b[]) {
+ int c[] = new int[a.length + b.length];
+ System.arraycopy(a, 0, c, 0, a.length);
+ System.arraycopy(b, 0, c, a.length, b.length);
+ return c;
+ }
+
+ static public float[] concat(float a[], float b[]) {
+ float c[] = new float[a.length + b.length];
+ System.arraycopy(a, 0, c, 0, a.length);
+ System.arraycopy(b, 0, c, a.length, b.length);
+ return c;
+ }
+
+ static public String[] concat(String a[], String b[]) {
+ String c[] = new String[a.length + b.length];
+ System.arraycopy(a, 0, c, 0, a.length);
+ System.arraycopy(b, 0, c, a.length, b.length);
+ return c;
+ }
+
+ static public Object concat(Object a, Object b) {
+ Class type = a.getClass().getComponentType();
+ int alength = Array.getLength(a);
+ int blength = Array.getLength(b);
+ Object outgoing = Array.newInstance(type, alength + blength);
+ System.arraycopy(a, 0, outgoing, 0, alength);
+ System.arraycopy(b, 0, outgoing, alength, blength);
+ return outgoing;
+ }
+
+ //
+
+ static public boolean[] reverse(boolean list[]) {
+ boolean outgoing[] = new boolean[list.length];
+ int length1 = list.length - 1;
+ for (int i = 0; i < list.length; i++) {
+ outgoing[i] = list[length1 - i];
+ }
+ return outgoing;
+ }
+
+ static public byte[] reverse(byte list[]) {
+ byte outgoing[] = new byte[list.length];
+ int length1 = list.length - 1;
+ for (int i = 0; i < list.length; i++) {
+ outgoing[i] = list[length1 - i];
+ }
+ return outgoing;
+ }
+
+ static public char[] reverse(char list[]) {
+ char outgoing[] = new char[list.length];
+ int length1 = list.length - 1;
+ for (int i = 0; i < list.length; i++) {
+ outgoing[i] = list[length1 - i];
+ }
+ return outgoing;
+ }
+
+ static public int[] reverse(int list[]) {
+ int outgoing[] = new int[list.length];
+ int length1 = list.length - 1;
+ for (int i = 0; i < list.length; i++) {
+ outgoing[i] = list[length1 - i];
+ }
+ return outgoing;
+ }
+
+ static public float[] reverse(float list[]) {
+ float outgoing[] = new float[list.length];
+ int length1 = list.length - 1;
+ for (int i = 0; i < list.length; i++) {
+ outgoing[i] = list[length1 - i];
+ }
+ return outgoing;
+ }
+
+ static public String[] reverse(String list[]) {
+ String outgoing[] = new String[list.length];
+ int length1 = list.length - 1;
+ for (int i = 0; i < list.length; i++) {
+ outgoing[i] = list[length1 - i];
+ }
+ return outgoing;
+ }
+
+ static public Object reverse(Object list) {
+ Class type = list.getClass().getComponentType();
+ int length = Array.getLength(list);
+ Object outgoing = Array.newInstance(type, length);
+ for (int i = 0; i < length; i++) {
+ Array.set(outgoing, i, Array.get(list, (length - 1) - i));
+ }
+ return outgoing;
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // STRINGS
+
+
+ /**
+ * Remove whitespace characters from the beginning and ending
+ * of a String. Works like String.trim() but includes the
+ * unicode nbsp character as well.
+ */
+ static public String trim(String str) {
+ return str.replace('\u00A0', ' ').trim();
+
+ /*
+ int left = 0;
+ int right = str.length() - 1;
+
+ while ((left <= right) &&
+ (WHITESPACE.indexOf(str.charAt(left)) != -1)) left++;
+ if (left == right) return "";
+
+ while (WHITESPACE.indexOf(str.charAt(right)) != -1) --right;
+
+ return str.substring(left, right-left+1);
+ */
+ }
+
+ /**
+ * Join an array of Strings together as a single String,
+ * separated by the whatever's passed in for the separator.
+ */
+ static public String join(String str[], char separator) {
+ return join(str, String.valueOf(separator));
+ }
+
+
+ /**
+ * Join an array of Strings together as a single String,
+ * separated by the whatever's passed in for the separator.
+ *
+ * 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 or later.
+ *
+ * --hide-stop use to hide the stop button in situations where
+ * you don't want to allow users to exit. also
+ * see the FAQ on information for capturing the ESC
+ * key when running in presentation mode.
+ *
+ * --stop-color color of the 'stop' text used to quit an
+ * sketch when it's in present mode.
+ *
+ * --bgcolor=#xxxxxx background color of the window.
+ *
+ * --sketch-path 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 via the PDE
+ *
+ * --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
+ *
+ */
+ static public void main(String args[]) {
+ if (args.length < 1) {
+ System.err.println("Usage: PApplet
- * |
- * | height is the full used height of the image
- * |
- * | ..XX.. }
- * | ..XX.. }
- * | ...... }
- * | XXXX.. } topExtent (top y is baseline - topExtent)
- * | ..XX.. }
- * | ..XX.. } dotted areas are where the image data
- * | ..XX.. } is actually located for the character
- * +---XXXXXX---- } (it extends to the right and down
- * | for power of two texture sizes)
- * ^^^^ leftExtent (amount to move over before drawing the image
- *
- * ^^^^^^^^^^^^^^ setWidth (width displaced by char)
- *
- */
-public class PFont implements PConstants {
-
- public int charCount;
- public PImage images[];
-
- /**
- * Native Java version of the font. If possible, this allows the
- * PGraphics subclass to just use Java's font rendering stuff
- * in situations where that's faster.
- */
- public Font font;
-
- /**
- * Name of the font as seen by Java when it was created.
- * If the font is available, the native version will be used.
- */
- public String name;
-
- /**
- * Postscript name of the font that this bitmap was created from.
- */
- public String psname;
-
- /** "natural" size of the font (most often 48) */
- public int size;
-
- /** true if smoothing was enabled for this font, used for native impl */
- public boolean smooth;
-
- /** next power of 2 over the max image size (usually 64) */
- public int mbox2;
-
- /** floating point width (convenience) */
- protected float fwidth;
-
- /** floating point width (convenience) */
- protected float fheight;
-
- /** texture width, same as mbox2, but reserved for future use */
- public int twidth;
-
- /** texture height, same as mbox2, but reserved for future use */
- public int theight;
-
- public int value[]; // char code
- public int height[]; // height of the bitmap data
- public int width[]; // width of bitmap data
- public int setWidth[]; // width displaced by the char
- public int topExtent[]; // offset for the top
- public int leftExtent[]; // offset for the left
-
- public int ascent;
- public int descent;
-
- protected int ascii[]; // quick lookup for the ascii chars
-
- // shared by the text() functions to avoid incessant allocation of memory
- //protected char textBuffer[] = new char[8 * 1024];
- //protected char widthBuffer[] = new char[8 * 1024];
-
-
- public PFont() { } // for subclasses
-
-
- public PFont(InputStream input) throws IOException {
- DataInputStream is = new DataInputStream(input);
-
- // number of character images stored in this font
- charCount = is.readInt();
-
- // bit count is ignored since this is always 8
- //int numBits = is.readInt();
- // used to be the bitCount, but now used for version number.
- // version 8 is any font before 69, so 9 is anything from 83+
- // 9 was buggy so gonna increment to 10.
- int version = is.readInt();
-
- // this was formerly ignored, now it's the actual font size
- //mbox = is.readInt();
- size = is.readInt();
- // this was formerly mboxY, the one that was used
- // this will make new fonts downward compatible
- //mbox2 = is.readInt();
- mbox2 = is.readInt();
-
- fwidth = size; //mbox;
- fheight = size; //mbox;
-
- // size for image ("texture") is next power of 2
- // over the font size. for most vlw fonts, the size is 48
- // so the next power of 2 is 64.
- // double-check to make sure that mbox2 is a power of 2
- // there was a bug in the old font generator that broke this
- //mbox2 = (int) Math.pow(2, Math.ceil(Math.log(mbox2) / Math.log(2)));
- mbox2 = (int) Math.pow(2, Math.ceil(Math.log(mbox2) / Math.log(2)));
- // size for the texture is stored in the font
- twidth = theight = mbox2; //mbox2;
-
- ascent = is.readInt(); // formerly baseHt (zero/ignored)
- descent = is.readInt(); // formerly ignored struct padding
-
- // allocate enough space for the character info
- value = new int[charCount];
- height = new int[charCount];
- width = new int[charCount];
- setWidth = new int[charCount];
- topExtent = new int[charCount];
- leftExtent = new int[charCount];
-
- ascii = new int[128];
- for (int i = 0; i < 128; i++) ascii[i] = -1;
-
- // read the information about the individual characters
- for (int i = 0; i < charCount; i++) {
- value[i] = is.readInt();
- height[i] = is.readInt();
- width[i] = is.readInt();
- setWidth[i] = is.readInt();
- topExtent[i] = is.readInt();
- leftExtent[i] = is.readInt();
-
- // pointer in the c version, ignored
- is.readInt();
-
- // cache locations of the ascii charset
- if (value[i] < 128) ascii[value[i]] = i;
-
- // the values for getAscent() and getDescent() from FontMetrics
- // seem to be way too large.. perhaps they're the max?
- // as such, use a more traditional marker for ascent/descent
- if (value[i] == 'd') {
- if (ascent == 0) ascent = topExtent[i];
- }
- if (value[i] == 'p') {
- if (descent == 0) descent = -topExtent[i] + height[i];
- }
- }
-
- // not a roman font, so throw an error and ask to re-build.
- // that way can avoid a bunch of error checking hacks in here.
- if ((ascent == 0) && (descent == 0)) {
- throw new RuntimeException("Please use \"Create Font\" to " +
- "re-create this font.");
- }
-
- images = new PImage[charCount];
- for (int i = 0; i < charCount; i++) {
- int pixels[] = new int[twidth * theight];
- images[i] = new PImage(pixels, twidth, theight, ALPHA);
- int bitmapSize = height[i] * width[i];
-
- byte temp[] = new byte[bitmapSize];
- is.readFully(temp);
-
- // convert the bitmap to an alpha channel
- int w = width[i];
- int h = height[i];
- for (int y = 0; y < h; y++) {
- for (int x = 0; x < w; x++) {
- int valu = temp[y*w + x] & 0xff;
- images[i].pixels[y * twidth + x] = valu;
- //(valu << 24) | 0xFFFFFF; // windows
- //0xFFFFFF00 | valu; // macosx
-
- //System.out.print((images[i].pixels[y*64+x] > 128) ? "*" : ".");
- }
- //System.out.println();
- }
- //System.out.println();
- }
-
- if (version >= 10) { // includes the font name at the end of the file
- name = is.readUTF();
- psname = is.readUTF();
-
- // this font may or may not be installed
- font = new Font(name, Font.PLAIN, size);
- // if the ps name matches, then we're in fine shape
- if (!font.getPSName().equals(psname)) {
- // on osx java 1.4 (not 1.3.. ugh), you can specify the ps name
- // of the font, so try that in case this .vlw font was created on pc
- // and the name is different, but the ps name is found on the
- // java 1.4 mac that's currently running this sketch.
- font = new Font(psname, Font.PLAIN, size);
- }
- // check again, and if still bad, screw em
- if (!font.getPSName().equals(psname)) {
- font = null;
- }
- }
- if (version == 11) {
- smooth = is.readBoolean();
- }
- }
-
-
- /**
- * Write this PFont to an OutputStream.
- *
+ * |
+ * | height is the full used height of the image
+ * |
+ * | ..XX.. }
+ * | ..XX.. }
+ * | ...... }
+ * | XXXX.. } topExtent (top y is baseline - topExtent)
+ * | ..XX.. }
+ * | ..XX.. } dotted areas are where the image data
+ * | ..XX.. } is actually located for the character
+ * +---XXXXXX---- } (it extends to the right and down
+ * | for power of two texture sizes)
+ * ^^^^ leftExtent (amount to move over before drawing the image
+ *
+ * ^^^^^^^^^^^^^^ setWidth (width displaced by char)
+ *
+ */
+public class PFont implements PConstants {
+
+ public int charCount;
+ public PImage images[];
+
+ /**
+ * Native Java version of the font. If possible, this allows the
+ * PGraphics subclass to just use Java's font rendering stuff
+ * in situations where that's faster.
+ */
+ public Font font;
+
+ /**
+ * Name of the font as seen by Java when it was created.
+ * If the font is available, the native version will be used.
+ */
+ public String name;
+
+ /**
+ * Postscript name of the font that this bitmap was created from.
+ */
+ public String psname;
+
+ /** "natural" size of the font (most often 48) */
+ public int size;
+
+ /** true if smoothing was enabled for this font, used for native impl */
+ public boolean smooth;
+
+ /** next power of 2 over the max image size (usually 64) */
+ public int mbox2;
+
+ /** floating point width (convenience) */
+ protected float fwidth;
+
+ /** floating point width (convenience) */
+ protected float fheight;
+
+ /** texture width, same as mbox2, but reserved for future use */
+ public int twidth;
+
+ /** texture height, same as mbox2, but reserved for future use */
+ public int theight;
+
+ public int value[]; // char code
+ public int height[]; // height of the bitmap data
+ public int width[]; // width of bitmap data
+ public int setWidth[]; // width displaced by the char
+ public int topExtent[]; // offset for the top
+ public int leftExtent[]; // offset for the left
+
+ public int ascent;
+ public int descent;
+
+ protected int ascii[]; // quick lookup for the ascii chars
+
+ // shared by the text() functions to avoid incessant allocation of memory
+ //protected char textBuffer[] = new char[8 * 1024];
+ //protected char widthBuffer[] = new char[8 * 1024];
+
+
+ public PFont() { } // for subclasses
+
+
+ public PFont(InputStream input) throws IOException {
+ DataInputStream is = new DataInputStream(input);
+
+ // number of character images stored in this font
+ charCount = is.readInt();
+
+ // bit count is ignored since this is always 8
+ //int numBits = is.readInt();
+ // used to be the bitCount, but now used for version number.
+ // version 8 is any font before 69, so 9 is anything from 83+
+ // 9 was buggy so gonna increment to 10.
+ int version = is.readInt();
+
+ // this was formerly ignored, now it's the actual font size
+ //mbox = is.readInt();
+ size = is.readInt();
+ // this was formerly mboxY, the one that was used
+ // this will make new fonts downward compatible
+ //mbox2 = is.readInt();
+ mbox2 = is.readInt();
+
+ fwidth = size; //mbox;
+ fheight = size; //mbox;
+
+ // size for image ("texture") is next power of 2
+ // over the font size. for most vlw fonts, the size is 48
+ // so the next power of 2 is 64.
+ // double-check to make sure that mbox2 is a power of 2
+ // there was a bug in the old font generator that broke this
+ //mbox2 = (int) Math.pow(2, Math.ceil(Math.log(mbox2) / Math.log(2)));
+ mbox2 = (int) Math.pow(2, Math.ceil(Math.log(mbox2) / Math.log(2)));
+ // size for the texture is stored in the font
+ twidth = theight = mbox2; //mbox2;
+
+ ascent = is.readInt(); // formerly baseHt (zero/ignored)
+ descent = is.readInt(); // formerly ignored struct padding
+
+ // allocate enough space for the character info
+ value = new int[charCount];
+ height = new int[charCount];
+ width = new int[charCount];
+ setWidth = new int[charCount];
+ topExtent = new int[charCount];
+ leftExtent = new int[charCount];
+
+ ascii = new int[128];
+ for (int i = 0; i < 128; i++) ascii[i] = -1;
+
+ // read the information about the individual characters
+ for (int i = 0; i < charCount; i++) {
+ value[i] = is.readInt();
+ height[i] = is.readInt();
+ width[i] = is.readInt();
+ setWidth[i] = is.readInt();
+ topExtent[i] = is.readInt();
+ leftExtent[i] = is.readInt();
+
+ // pointer in the c version, ignored
+ is.readInt();
+
+ // cache locations of the ascii charset
+ if (value[i] < 128) ascii[value[i]] = i;
+
+ // the values for getAscent() and getDescent() from FontMetrics
+ // seem to be way too large.. perhaps they're the max?
+ // as such, use a more traditional marker for ascent/descent
+ if (value[i] == 'd') {
+ if (ascent == 0) ascent = topExtent[i];
+ }
+ if (value[i] == 'p') {
+ if (descent == 0) descent = -topExtent[i] + height[i];
+ }
+ }
+
+ // not a roman font, so throw an error and ask to re-build.
+ // that way can avoid a bunch of error checking hacks in here.
+ if ((ascent == 0) && (descent == 0)) {
+ throw new RuntimeException("Please use \"Create Font\" to " +
+ "re-create this font.");
+ }
+
+ images = new PImage[charCount];
+ for (int i = 0; i < charCount; i++) {
+ int pixels[] = new int[twidth * theight];
+ images[i] = new PImage(pixels, twidth, theight, ALPHA);
+ int bitmapSize = height[i] * width[i];
+
+ byte temp[] = new byte[bitmapSize];
+ is.readFully(temp);
+
+ // convert the bitmap to an alpha channel
+ int w = width[i];
+ int h = height[i];
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ int valu = temp[y*w + x] & 0xff;
+ images[i].pixels[y * twidth + x] = valu;
+ //(valu << 24) | 0xFFFFFF; // windows
+ //0xFFFFFF00 | valu; // macosx
+
+ //System.out.print((images[i].pixels[y*64+x] > 128) ? "*" : ".");
+ }
+ //System.out.println();
+ }
+ //System.out.println();
+ }
+
+ if (version >= 10) { // includes the font name at the end of the file
+ name = is.readUTF();
+ psname = is.readUTF();
+
+ // this font may or may not be installed
+ font = new Font(name, Font.PLAIN, size);
+ // if the ps name matches, then we're in fine shape
+ if (!font.getPSName().equals(psname)) {
+ // on osx java 1.4 (not 1.3.. ugh), you can specify the ps name
+ // of the font, so try that in case this .vlw font was created on pc
+ // and the name is different, but the ps name is found on the
+ // java 1.4 mac that's currently running this sketch.
+ font = new Font(psname, Font.PLAIN, size);
+ }
+ // check again, and if still bad, screw em
+ if (!font.getPSName().equals(psname)) {
+ font = null;
+ }
+ }
+ if (version == 11) {
+ smooth = is.readBoolean();
+ }
+ }
+
+
+ /**
+ * Write this PFont to an OutputStream.
+ *
- *
- */
- public void hint(int which) {
- hints[which] = true;
- }
-
- /**
- * Disable a hint.
- */
- public void noHint(int which) {
- hints[which] = false;
- }
-
-
- //////////////////////////////////////////////////////////////
-
- // SHAPES
-
- /**
- * Start a new shape of type POLYGON
- */
- public void beginShape() {
- beginShape(POLYGON);
- }
-
-
- /**
- * Start a new shape.
- *
- * beginShape();
- * beginPath();
- * // multiple calls to vertex() that draw the exterior shape
- * endPath();
- * beginPath();
- * // several calls to vertex() to draw the interior hole
- * endPath();
- * // more beginPath/endPath pairs can be used for additional holes
- * endShape();
- *
- *
- * This will probably be available only with the OpenGL renderer,
- * because it has a built-in tesselator from GLU.
- */
- //public void beginPath() {
- //throw new RuntimeException("beginPath() is not available");
- //}
-
-
- /**
- * End a path. Use this with beginPath() to close out a compound path.
- *
- * This will probably be available only with the OpenGL renderer,
- * because it has a built-in tesselator from GLU.
- */
- //public void endPath() {
- //throw new RuntimeException("endPath() is not available");
- //}
-
-
-
- //////////////////////////////////////////////////////////////
-
- // STROKE/FILL/DRAW
-
-
- //protected void fill_shape(Shape s) {
- protected void fill_shape(Path s) {
- if (fill) {
- //graphics.setColor(fillColorObject);
- //graphics.fill(s);
- }
- }
-
- //protected void stroke_shape(Shape s) {
- protected void stroke_shape(Path s) {
- if (stroke) {
- //graphics.setColor(strokeColorObject);
- //graphics.draw(s);
- }
- }
-
- //protected void draw_shape(Shape s) {
- protected void draw_shape(Path s) {
- if (fill) {
- //graphics.setColor(fillColorObject);
- //graphics.fill(s);
- }
- if (stroke) {
- //graphics.setColor(strokeColorObject);
- //graphics.draw(s);
- }
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // POINT
-
-
- public void point(float x, float y) {
- // TODO
- }
-
-
- public void point(float x, float y, float z) {
- depthErrorXYZ("point");
- }
-
-
- public void line(float x1, float y1, float x2, float y2) {
- // TODO
- }
-
-
- public void line(float x1, float y1, float z1,
- float x2, float y2, float z2) {
- depthErrorXYZ("line");
- }
-
-
- public void triangle(float x1, float y1, float x2, float y2,
- float x3, float y3) {
- // TODO
- }
-
-
- public void quad(float x1, float y1, float x2, float y2,
- float x3, float y3, float x4, float y4) {
- // TODO
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // RECT
-
-
- public void rectMode(int mode) {
- rectMode = mode;
- }
-
-
- public void rect(float x1, float y1, float x2, float y2) {
- float hradius, vradius;
- switch (rectMode) {
- case CORNERS:
- break;
- case CORNER:
- x2 += x1; y2 += y1;
- break;
- case CENTER_RADIUS:
- hradius = x2;
- vradius = y2;
- x2 = x1 + hradius;
- y2 = y1 + vradius;
- x1 -= hradius;
- y1 -= vradius;
- break;
- case CENTER:
- hradius = x2 / 2.0f;
- vradius = y2 / 2.0f;
- x2 = x1 + hradius;
- y2 = y1 + vradius;
- x1 -= hradius;
- y1 -= vradius;
- }
-
- if (x1 > x2) {
- float temp = x1; x1 = x2; x2 = temp;
- }
-
- if (y1 > y2) {
- float temp = y1; y1 = y2; y2 = temp;
- }
-
- rectImpl(x1, y1, x2, y2);
- }
-
-
- protected void rectImpl(float x1, float y1, float x2, float y2) {
- // TODO write rect drawing function
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // ELLIPSE AND ARC
-
-
- public void ellipseMode(int mode) {
- ellipseMode = mode;
- }
-
-
- public void ellipse(float a, float b, float c, float d) {
- float x = a;
- float y = b;
- float w = c;
- float h = d;
-
- if (ellipseMode == CORNERS) {
- w = c - a;
- h = d - b;
-
- } else if (ellipseMode == CENTER_RADIUS) {
- x = a - c;
- y = b - d;
- w = c * 2;
- h = d * 2;
-
- } else if (ellipseMode == CENTER) {
- x = a - c/2f;
- y = b - d/2f;
- }
-
- if (w < 0) { // undo negative width
- x += w;
- w = -w;
- }
-
- if (h < 0) { // undo negative height
- y += h;
- h = -h;
- }
-
- ellipseImpl(x, y, w, h);
- }
-
-
- protected void ellipseImpl(float x, float y, float w, float h) {
- // TODO draw an ellipse
- }
-
-
- /**
- * Identical parameters and placement to ellipse,
- * but draws only an arc of that ellipse.
- *
- * start and stop are always radians because angleMode() was goofy.
- * ellipseMode() sets the placement.
- *
- * also tries to be smart about start < stop.
- */
- public void arc(float a, float b, float c, float d,
- float start, float stop) {
- float x = a;
- float y = b;
- float w = c;
- float h = d;
-
- if (ellipseMode == CORNERS) {
- w = c - a;
- h = d - b;
-
- } else if (ellipseMode == CENTER_RADIUS) {
- x = a - c;
- y = b - d;
- w = c * 2;
- h = d * 2;
-
- } else if (ellipseMode == CENTER) {
- x = a - c/2f;
- y = b - d/2f;
- }
-
- //if (angleMode == DEGREES) {
- //start = start * DEG_TO_RAD;
- //stop = stop * DEG_TO_RAD;
- //}
- // before running a while loop like this,
- // make sure it will exit at some point.
- if (Float.isInfinite(start) || Float.isInfinite(stop)) return;
- while (stop < start) stop += TWO_PI;
-
- arcImpl(x, y, w, h, start, stop);
- }
-
-
- protected void arcImpl(float x, float y, float w, float h,
- float start, float stop) {
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // 3D SHAPES
-
-
- public void box(float size) {
- depthError("box");
- }
-
- public void box(float w, float h, float d) {
- depthError("box");
- }
-
- public void sphereDetail(int res) {
- depthError("sphereDetail");
- }
-
- public void sphere(float r) {
- depthError("sphere");
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // CURVES
-
-
- /**
- * Evalutes quadratic bezier at point t for points a, b, c, d.
- * t varies between 0 and 1, and a and d are the on curve points,
- * b and c are the control points. this can be done once with the
- * x coordinates and a second time with the y coordinates to get
- * the location of a bezier curve at t.
- *
- * stroke(255, 102, 0);
- * line(85, 20, 10, 10);
- * line(90, 90, 15, 80);
- * stroke(0, 0, 0);
- * bezier(85, 20, 10, 10, 90, 90, 15, 80);
- *
- * // draw it in gray, using 10 steps instead of the default 20
- * // this is a slower way to do it, but useful if you need
- * // to do things with the coordinates at each step
- * stroke(128);
- * beginShape(LINE_STRIP);
- * for (int i = 0; i <= 10; i++) {
- * float t = i / 10.0f;
- * float x = bezierPoint(85, 10, 90, 15, t);
- * float y = bezierPoint(20, 10, 90, 80, t);
- * vertex(x, y);
- * }
- * endShape();
- */
- public float bezierPoint(float a, float b, float c, float d, float t) {
- float t1 = 1.0f - t;
- return a*t1*t1*t1 + 3*b*t*t1*t1 + 3*c*t*t*t1 + d*t*t*t;
- }
-
-
- /**
- * Provide the tangent at the given point on the bezier curve.
- * Based on code from v3ga's wordstree sketch.
- */
- public float bezierTangent(float a, float b, float c, float d, float t) {
- float t1 = 1.0f - t;
-
- return (a * 3 * t*t +
- b * 3 * t * (2 - 3*t) +
- c * 3 * (3*t*t - 4*t + 1) +
- d * -3 * t1*t1);
- }
-
-
- protected void bezier_init() {
- bezierDetail(bezierDetail);
- }
-
-
- public void bezierDetail(int detail) {
- if (bezier_forward == null) {
- bezier_forward = new float[4][4];
- bezier_draw = new float[4][4];
- }
- bezierDetail = detail;
- bezierInited = true;
-
- // setup matrix for forward differencing to speed up drawing
- setup_spline_forward(detail, bezier_forward);
-
- // multiply the basis and forward diff matrices together
- // saves much time since this needn't be done for each curve
- mult_spline_matrix(bezier_forward, bezier_basis, bezier_draw, 4);
- }
-
-
- /**
- * Draw a quadratic bezier curve. The first and last points are
- * the on-curve points. The middle two are the 'control' points,
- * or 'handles' in an application like Illustrator.
- * beginShape();
- * vertex(x1, y1);
- * bezierVertex(x2, y2, x3, y3, x4, y4);
- * endShape();
- *
- * In Postscript-speak, this would be:
- * moveto(x1, y1);
- * curveto(x2, y2, x3, y3, x4, y4);
- * If you were to try and continue that curve like so:
- * curveto(x5, y5, x6, y6, x7, y7);
- * This would be done in processing by adding these statements:
- * bezierVertex(x5, y5, x6, y6, x7, y7)
- *
- * To draw a cubic (instead of quadratic) curve,
- * use the control point twice by doubling it:
- * bezier(x1, y1, cx, cy, cx, cy, x2, y2);
- */
- public void bezier(float x1, float y1,
- float x2, float y2,
- float x3, float y3,
- float x4, float y4) {
- beginShape(LINE_STRIP);
- vertex(x1, y1);
- bezierVertex(x2, y2, x3, y3, x4, y4);
- endShape();
- }
-
-
- public void bezier(float x1, float y1, float z1,
- float x2, float y2, float z2,
- float x3, float y3, float z3,
- float x4, float y4, float z4) {
- depthErrorXYZ("bezier");
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- /**
- * Get a location along a catmull-rom curve segment.
- *
- * @param t Value between zero and one for how far along the segment
- */
- public float curvePoint(float a, float b, float c, float d, float t) {
- if (!curve_inited) curve_init();
-
- float tt = t * t;
- float ttt = t * tt;
- float m[][] = curve_basis;
-
- // not optimized (and probably need not be)
- return (a * (ttt*m[0][0] + tt*m[1][0] + t*m[2][0] + m[3][0]) +
- b * (ttt*m[0][1] + tt*m[1][1] + t*m[2][1] + m[3][1]) +
- c * (ttt*m[0][2] + tt*m[1][2] + t*m[2][2] + m[3][2]) +
- d * (ttt*m[0][3] + tt*m[1][3] + t*m[2][3] + m[3][3]));
- }
-
-
- public float curveTangent(float a, float b, float c, float d,
- float t) {
- System.err.println("curveTangent not yet implemented");
- return 0;
- }
-
-
- public void curveDetail(int detail) {
- curve_mode(detail, curveTightness);
- }
-
-
- public void curveTightness(float tightness) {
- curve_mode(curveDetail, tightness);
- }
-
-
- protected void curve_init() {
- curve_mode(curveDetail, curveTightness);
- }
-
-
- /**
- * Set the number of segments to use when drawing a Catmull-Rom
- * curve, and setting the s parameter, which defines how tightly
- * the curve fits to each vertex. Catmull-Rom curves are actually
- * a subset of this curve type where the s is set to zero.
- *
- * beginShape();
- * curveVertex(x1, y1);
- * curveVertex(x2, y2);
- * curveVertex(x3, y3);
- * curveVertex(x4, y4);
- * endShape();
- *
- */
- public void curve(float x1, float y1,
- float x2, float y2,
- float x3, float y3,
- float x4, float y4) {
- beginShape(LINE_STRIP);
- curveVertex(x1, y1);
- curveVertex(x2, y2);
- curveVertex(x3, y3);
- curveVertex(x4, y4);
- endShape();
- }
-
-
- public void curve(float x1, float y1, float z1,
- float x2, float y2, float z2,
- float x3, float y3, float z3,
- float x4, float y4, float z4) {
- depthErrorXYZ("curve");
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- /**
- * Setup forward-differencing matrix to be used for speedy
- * curve rendering. It's based on using a specific number
- * of curve segments and just doing incremental adds for each
- * vertex of the segment, rather than running the mathematically
- * expensive cubic equation.
- * @param segments number of curve segments to use when drawing
- */
- protected void setup_spline_forward(int segments, float fwd[][]) {
- float f = 1.0f / segments;
- float ff = f * f;
- float fff = ff * f;
-
- fwd[0][0] = 0; fwd[0][1] = 0; fwd[0][2] = 0; fwd[0][3] = 1;
- fwd[1][0] = fff; fwd[1][1] = ff; fwd[1][2] = f; fwd[1][3] = 0;
- fwd[2][0] = 6*fff; fwd[2][1] = 2*ff; fwd[2][2] = 0; fwd[2][3] = 0;
- fwd[3][0] = 6*fff; fwd[3][1] = 0; fwd[3][2] = 0; fwd[3][3] = 0;
- }
-
-
- // internal matrix multiplication routine used by the spline code
- // should these go to 4 instead of 3?
- //void mult_curve_matrix(float m[4][4], float g[4][3], float mg[4][3]);
- protected void mult_spline_matrix(float m[][], float g[][],
- float mg[][], int dimensions) {
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < dimensions; j++) {
- mg[i][j] = 0;
- }
- }
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < dimensions; j++) {
- for (int k = 0; k < 4; k++) {
- mg[i][j] = mg[i][j] + (m[i][k] * g[k][j]);
- }
- }
- }
- }
-
-
- /**
- * Draw a segment of spline (bezier or catmull-rom curve)
- * using the matrix m, which is the basis matrix already
- * multiplied with the forward differencing matrix.
- * colorMode(HSB, 360, 100, 100);
- * because the alpha values were still between 0 and 255.
- */
- public void colorMode(int mode,
- float maxX, float maxY, float maxZ) {
- colorMode(mode, maxX, maxY, maxZ, colorModeA);
- }
-
-
- public void colorMode(int mode,
- float maxX, float maxY, float maxZ, float maxA) {
- colorMode = mode;
-
- colorModeX = maxX; // still needs to be set for hsb
- colorModeY = maxY;
- colorModeZ = maxZ;
- colorModeA = maxA;
-
- // if color max values are all 1, then no need to scale
- colorScale = ((maxA != ONE) || (maxX != maxY) ||
- (maxY != maxZ) || (maxZ != maxA));
-
- // if color is rgb/0..255 this will make it easier for the
- // red() green() etc functions
- colorRgb255 = (colorMode == RGB) &&
- (colorModeA == 255) && (colorModeX == 255) &&
- (colorModeY == 255) && (colorModeZ == 255);
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- protected void colorCalc(float gray) {
- colorCalc(gray, colorModeA);
- }
-
-
- protected void colorCalc(float gray, float alpha) {
- if (gray > colorModeX) gray = colorModeX;
- if (alpha > colorModeA) alpha = colorModeA;
-
- if (gray < 0) gray = 0;
- if (alpha < 0) alpha = 0;
-
- calcR = colorScale ? (gray / colorModeX) : gray;
- calcG = calcR;
- calcB = calcR;
- calcA = colorScale ? (alpha / colorModeA) : alpha;
-
- calcRi = (int)(calcR*255); calcGi = (int)(calcG*255);
- calcBi = (int)(calcB*255); calcAi = (int)(calcA*255);
- calcColor = (calcAi << 24) | (calcRi << 16) | (calcGi << 8) | calcBi;
- calcAlpha = (calcAi != 255);
- }
-
-
- protected void colorCalc(float x, float y, float z) {
- colorCalc(x, y, z, colorModeA);
- }
-
-
- protected void colorCalc(float x, float y, float z, float a) {
- if (x > colorModeX) x = colorModeX;
- if (y > colorModeY) y = colorModeY;
- if (z > colorModeZ) z = colorModeZ;
- if (a > colorModeA) a = colorModeA;
-
- if (x < 0) x = 0;
- if (y < 0) y = 0;
- if (z < 0) z = 0;
- if (a < 0) a = 0;
-
- switch (colorMode) {
- case RGB:
- if (colorScale) {
- calcR = x / colorModeX;
- calcG = y / colorModeY;
- calcB = z / colorModeZ;
- calcA = a / colorModeA;
- } else {
- calcR = x; calcG = y; calcB = z; calcA = a;
- }
- break;
-
- case HSB:
- x /= colorModeX; // h
- y /= colorModeY; // s
- z /= colorModeZ; // b
-
- calcA = colorScale ? (a/colorModeA) : a;
-
- if (y == 0) { // saturation == 0
- calcR = calcG = calcB = z;
-
- } else {
- float which = (x - (int)x) * 6.0f;
- float f = which - (int)which;
- float p = z * (1.0f - y);
- float q = z * (1.0f - y * f);
- float t = z * (1.0f - (y * (1.0f - f)));
-
- switch ((int)which) {
- case 0: calcR = z; calcG = t; calcB = p; break;
- case 1: calcR = q; calcG = z; calcB = p; break;
- case 2: calcR = p; calcG = z; calcB = t; break;
- case 3: calcR = p; calcG = q; calcB = z; break;
- case 4: calcR = t; calcG = p; calcB = z; break;
- case 5: calcR = z; calcG = p; calcB = q; break;
- }
- }
- break;
- }
- calcRi = (int)(255*calcR); calcGi = (int)(255*calcG);
- calcBi = (int)(255*calcB); calcAi = (int)(255*calcA);
- calcColor = (calcAi << 24) | (calcRi << 16) | (calcGi << 8) | calcBi;
- calcAlpha = (calcAi != 255);
- }
-
-
- /**
- * Unpacks AARRGGBB color for direct use with colorCalc.
- * size(256, 256);
- * for (int i = 0; i < 256; i++) {
- * color c = color(0, 0, 0, i);
- * stroke(c);
- * line(i, 0, i, 256);
- * }
- * ...on the first time through the loop, where (i == 0),
- * since the color itself is zero (black) then it would appear
- * indistinguishable from someone having written fill(0).
- */
- public void fill(int rgb) {
- if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
- fill((float) rgb);
-
- } else {
- colorCalcARGB(rgb, colorModeA);
- fillFromCalc();
- }
- }
-
-
- public void fill(int rgb, float alpha) {
- if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
- fill((float) rgb, alpha);
-
- } else {
- colorCalcARGB(rgb, alpha);
- fillFromCalc();
- }
- }
-
-
- public void fill(float gray) {
- colorCalc(gray);
- fillFromCalc();
- }
-
-
- public void fill(float gray, float alpha) {
- colorCalc(gray, alpha);
- fillFromCalc();
- }
-
-
- public void fill(float x, float y, float z) {
- colorCalc(x, y, z);
- fillFromCalc();
- }
-
-
- public void fill(float x, float y, float z, float a) {
- colorCalc(x, y, z, a);
- fillFromCalc();
- }
-
-
- protected void fillFromCalc() {
- fill = true;
- fillR = calcR;
- fillG = calcG;
- fillB = calcB;
- fillA = calcA;
- fillRi = calcRi;
- fillGi = calcGi;
- fillBi = calcBi;
- fillAi = calcAi;
- fillColor = calcColor;
- fillAlpha = calcAlpha;
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public void ambient(int rgb) {
- depthError("ambient");
- }
-
- public void ambient(float gray) {
- depthError("ambient");
- }
-
- public void ambient(float x, float y, float z) {
- depthError("ambient");
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public void specular(int rgb) {
- depthError("specular");
- }
-
- public void specular(float gray) {
- depthError("specular");
- }
-
- public void specular(float gray, float alpha) {
- depthError("specular");
- }
-
- public void specular(float x, float y, float z) {
- depthError("specular");
- }
-
- public void specular(float x, float y, float z, float a) {
- depthError("specular");
- }
-
- public void shininess(float shine) {
- depthError("shininess");
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- public void emissive(int rgb) {
- depthError("emissive");
- }
-
- public void emissive(float gray) {
- depthError("emissive");
- }
-
- public void emissive(float x, float y, float z ) {
- depthError("emissive");
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // LIGHTS
-
-
- public void lights() {
- depthError("lights");
- }
-
- public void ambientLight(float red, float green, float blue) {
- depthError("ambientLight");
- }
-
- public void ambientLight(float red, float green, float blue,
- float x, float y, float z) {
- depthError("ambientLight");
- }
-
- public void directionalLight(float red, float green, float blue,
- float nx, float ny, float nz) {
- depthError("directionalLight");
- }
-
- public void pointLight(float red, float green, float blue,
- float x, float y, float z) {
- depthError("pointLight");
- }
-
- public void spotLight(float red, float green, float blue,
- float x, float y, float z,
- float nx, float ny, float nz,
- float angle, float concentration) {
- depthError("spotLight");
- }
-
- public void lightFalloff(float constant, float linear, float quadratic) {
- depthError("lightFalloff");
- }
-
- public void lightSpecular(float x, float y, float z) {
- depthError("lightSpecular");
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
-
- /**
- * Set the background to a gray or ARGB color.
- *
+ *
+ */
+ public void hint(int which) {
+ hints[which] = true;
+ }
+
+ /**
+ * Disable a hint.
+ */
+ public void noHint(int which) {
+ hints[which] = false;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+ // SHAPES
+
+ /**
+ * Start a new shape of type POLYGON
+ */
+ public void beginShape() {
+ beginShape(POLYGON);
+ }
+
+
+ /**
+ * Start a new shape.
+ *
+ * beginShape();
+ * beginPath();
+ * // multiple calls to vertex() that draw the exterior shape
+ * endPath();
+ * beginPath();
+ * // several calls to vertex() to draw the interior hole
+ * endPath();
+ * // more beginPath/endPath pairs can be used for additional holes
+ * endShape();
+ *
+ *
+ * This will probably be available only with the OpenGL renderer,
+ * because it has a built-in tesselator from GLU.
+ */
+ //public void beginPath() {
+ //throw new RuntimeException("beginPath() is not available");
+ //}
+
+
+ /**
+ * End a path. Use this with beginPath() to close out a compound path.
+ *
+ * This will probably be available only with the OpenGL renderer,
+ * because it has a built-in tesselator from GLU.
+ */
+ //public void endPath() {
+ //throw new RuntimeException("endPath() is not available");
+ //}
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // STROKE/FILL/DRAW
+
+
+ //protected void fill_shape(Shape s) {
+ protected void fill_shape(Path s) {
+ if (fill) {
+ //graphics.setColor(fillColorObject);
+ //graphics.fill(s);
+ }
+ }
+
+ //protected void stroke_shape(Shape s) {
+ protected void stroke_shape(Path s) {
+ if (stroke) {
+ //graphics.setColor(strokeColorObject);
+ //graphics.draw(s);
+ }
+ }
+
+ //protected void draw_shape(Shape s) {
+ protected void draw_shape(Path s) {
+ if (fill) {
+ //graphics.setColor(fillColorObject);
+ //graphics.fill(s);
+ }
+ if (stroke) {
+ //graphics.setColor(strokeColorObject);
+ //graphics.draw(s);
+ }
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // POINT
+
+
+ public void point(float x, float y) {
+ // TODO
+ }
+
+
+ public void point(float x, float y, float z) {
+ depthErrorXYZ("point");
+ }
+
+
+ public void line(float x1, float y1, float x2, float y2) {
+ // TODO
+ }
+
+
+ public void line(float x1, float y1, float z1,
+ float x2, float y2, float z2) {
+ depthErrorXYZ("line");
+ }
+
+
+ public void triangle(float x1, float y1, float x2, float y2,
+ float x3, float y3) {
+ // TODO
+ }
+
+
+ public void quad(float x1, float y1, float x2, float y2,
+ float x3, float y3, float x4, float y4) {
+ // TODO
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // RECT
+
+
+ public void rectMode(int mode) {
+ rectMode = mode;
+ }
+
+
+ public void rect(float x1, float y1, float x2, float y2) {
+ float hradius, vradius;
+ switch (rectMode) {
+ case CORNERS:
+ break;
+ case CORNER:
+ x2 += x1; y2 += y1;
+ break;
+ case CENTER_RADIUS:
+ hradius = x2;
+ vradius = y2;
+ x2 = x1 + hradius;
+ y2 = y1 + vradius;
+ x1 -= hradius;
+ y1 -= vradius;
+ break;
+ case CENTER:
+ hradius = x2 / 2.0f;
+ vradius = y2 / 2.0f;
+ x2 = x1 + hradius;
+ y2 = y1 + vradius;
+ x1 -= hradius;
+ y1 -= vradius;
+ }
+
+ if (x1 > x2) {
+ float temp = x1; x1 = x2; x2 = temp;
+ }
+
+ if (y1 > y2) {
+ float temp = y1; y1 = y2; y2 = temp;
+ }
+
+ rectImpl(x1, y1, x2, y2);
+ }
+
+
+ protected void rectImpl(float x1, float y1, float x2, float y2) {
+ // TODO write rect drawing function
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // ELLIPSE AND ARC
+
+
+ public void ellipseMode(int mode) {
+ ellipseMode = mode;
+ }
+
+
+ public void ellipse(float a, float b, float c, float d) {
+ float x = a;
+ float y = b;
+ float w = c;
+ float h = d;
+
+ if (ellipseMode == CORNERS) {
+ w = c - a;
+ h = d - b;
+
+ } else if (ellipseMode == CENTER_RADIUS) {
+ x = a - c;
+ y = b - d;
+ w = c * 2;
+ h = d * 2;
+
+ } else if (ellipseMode == CENTER) {
+ x = a - c/2f;
+ y = b - d/2f;
+ }
+
+ if (w < 0) { // undo negative width
+ x += w;
+ w = -w;
+ }
+
+ if (h < 0) { // undo negative height
+ y += h;
+ h = -h;
+ }
+
+ ellipseImpl(x, y, w, h);
+ }
+
+
+ protected void ellipseImpl(float x, float y, float w, float h) {
+ // TODO draw an ellipse
+ }
+
+
+ /**
+ * Identical parameters and placement to ellipse,
+ * but draws only an arc of that ellipse.
+ *
+ * start and stop are always radians because angleMode() was goofy.
+ * ellipseMode() sets the placement.
+ *
+ * also tries to be smart about start < stop.
+ */
+ public void arc(float a, float b, float c, float d,
+ float start, float stop) {
+ float x = a;
+ float y = b;
+ float w = c;
+ float h = d;
+
+ if (ellipseMode == CORNERS) {
+ w = c - a;
+ h = d - b;
+
+ } else if (ellipseMode == CENTER_RADIUS) {
+ x = a - c;
+ y = b - d;
+ w = c * 2;
+ h = d * 2;
+
+ } else if (ellipseMode == CENTER) {
+ x = a - c/2f;
+ y = b - d/2f;
+ }
+
+ //if (angleMode == DEGREES) {
+ //start = start * DEG_TO_RAD;
+ //stop = stop * DEG_TO_RAD;
+ //}
+ // before running a while loop like this,
+ // make sure it will exit at some point.
+ if (Float.isInfinite(start) || Float.isInfinite(stop)) return;
+ while (stop < start) stop += TWO_PI;
+
+ arcImpl(x, y, w, h, start, stop);
+ }
+
+
+ protected void arcImpl(float x, float y, float w, float h,
+ float start, float stop) {
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // 3D SHAPES
+
+
+ public void box(float size) {
+ depthError("box");
+ }
+
+ public void box(float w, float h, float d) {
+ depthError("box");
+ }
+
+ public void sphereDetail(int res) {
+ depthError("sphereDetail");
+ }
+
+ public void sphere(float r) {
+ depthError("sphere");
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // CURVES
+
+
+ /**
+ * Evalutes quadratic bezier at point t for points a, b, c, d.
+ * t varies between 0 and 1, and a and d are the on curve points,
+ * b and c are the control points. this can be done once with the
+ * x coordinates and a second time with the y coordinates to get
+ * the location of a bezier curve at t.
+ *
+ * stroke(255, 102, 0);
+ * line(85, 20, 10, 10);
+ * line(90, 90, 15, 80);
+ * stroke(0, 0, 0);
+ * bezier(85, 20, 10, 10, 90, 90, 15, 80);
+ *
+ * // draw it in gray, using 10 steps instead of the default 20
+ * // this is a slower way to do it, but useful if you need
+ * // to do things with the coordinates at each step
+ * stroke(128);
+ * beginShape(LINE_STRIP);
+ * for (int i = 0; i <= 10; i++) {
+ * float t = i / 10.0f;
+ * float x = bezierPoint(85, 10, 90, 15, t);
+ * float y = bezierPoint(20, 10, 90, 80, t);
+ * vertex(x, y);
+ * }
+ * endShape();
+ */
+ public float bezierPoint(float a, float b, float c, float d, float t) {
+ float t1 = 1.0f - t;
+ return a*t1*t1*t1 + 3*b*t*t1*t1 + 3*c*t*t*t1 + d*t*t*t;
+ }
+
+
+ /**
+ * Provide the tangent at the given point on the bezier curve.
+ * Based on code from v3ga's wordstree sketch.
+ */
+ public float bezierTangent(float a, float b, float c, float d, float t) {
+ float t1 = 1.0f - t;
+
+ return (a * 3 * t*t +
+ b * 3 * t * (2 - 3*t) +
+ c * 3 * (3*t*t - 4*t + 1) +
+ d * -3 * t1*t1);
+ }
+
+
+ protected void bezier_init() {
+ bezierDetail(bezierDetail);
+ }
+
+
+ public void bezierDetail(int detail) {
+ if (bezier_forward == null) {
+ bezier_forward = new float[4][4];
+ bezier_draw = new float[4][4];
+ }
+ bezierDetail = detail;
+ bezierInited = true;
+
+ // setup matrix for forward differencing to speed up drawing
+ setup_spline_forward(detail, bezier_forward);
+
+ // multiply the basis and forward diff matrices together
+ // saves much time since this needn't be done for each curve
+ mult_spline_matrix(bezier_forward, bezier_basis, bezier_draw, 4);
+ }
+
+
+ /**
+ * Draw a quadratic bezier curve. The first and last points are
+ * the on-curve points. The middle two are the 'control' points,
+ * or 'handles' in an application like Illustrator.
+ * beginShape();
+ * vertex(x1, y1);
+ * bezierVertex(x2, y2, x3, y3, x4, y4);
+ * endShape();
+ *
+ * In Postscript-speak, this would be:
+ * moveto(x1, y1);
+ * curveto(x2, y2, x3, y3, x4, y4);
+ * If you were to try and continue that curve like so:
+ * curveto(x5, y5, x6, y6, x7, y7);
+ * This would be done in processing by adding these statements:
+ * bezierVertex(x5, y5, x6, y6, x7, y7)
+ *
+ * To draw a cubic (instead of quadratic) curve,
+ * use the control point twice by doubling it:
+ * bezier(x1, y1, cx, cy, cx, cy, x2, y2);
+ */
+ public void bezier(float x1, float y1,
+ float x2, float y2,
+ float x3, float y3,
+ float x4, float y4) {
+ beginShape(LINE_STRIP);
+ vertex(x1, y1);
+ bezierVertex(x2, y2, x3, y3, x4, y4);
+ endShape();
+ }
+
+
+ public void bezier(float x1, float y1, float z1,
+ float x2, float y2, float z2,
+ float x3, float y3, float z3,
+ float x4, float y4, float z4) {
+ depthErrorXYZ("bezier");
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ /**
+ * Get a location along a catmull-rom curve segment.
+ *
+ * @param t Value between zero and one for how far along the segment
+ */
+ public float curvePoint(float a, float b, float c, float d, float t) {
+ if (!curve_inited) curve_init();
+
+ float tt = t * t;
+ float ttt = t * tt;
+ float m[][] = curve_basis;
+
+ // not optimized (and probably need not be)
+ return (a * (ttt*m[0][0] + tt*m[1][0] + t*m[2][0] + m[3][0]) +
+ b * (ttt*m[0][1] + tt*m[1][1] + t*m[2][1] + m[3][1]) +
+ c * (ttt*m[0][2] + tt*m[1][2] + t*m[2][2] + m[3][2]) +
+ d * (ttt*m[0][3] + tt*m[1][3] + t*m[2][3] + m[3][3]));
+ }
+
+
+ public float curveTangent(float a, float b, float c, float d,
+ float t) {
+ System.err.println("curveTangent not yet implemented");
+ return 0;
+ }
+
+
+ public void curveDetail(int detail) {
+ curve_mode(detail, curveTightness);
+ }
+
+
+ public void curveTightness(float tightness) {
+ curve_mode(curveDetail, tightness);
+ }
+
+
+ protected void curve_init() {
+ curve_mode(curveDetail, curveTightness);
+ }
+
+
+ /**
+ * Set the number of segments to use when drawing a Catmull-Rom
+ * curve, and setting the s parameter, which defines how tightly
+ * the curve fits to each vertex. Catmull-Rom curves are actually
+ * a subset of this curve type where the s is set to zero.
+ *
+ * beginShape();
+ * curveVertex(x1, y1);
+ * curveVertex(x2, y2);
+ * curveVertex(x3, y3);
+ * curveVertex(x4, y4);
+ * endShape();
+ *
+ */
+ public void curve(float x1, float y1,
+ float x2, float y2,
+ float x3, float y3,
+ float x4, float y4) {
+ beginShape(LINE_STRIP);
+ curveVertex(x1, y1);
+ curveVertex(x2, y2);
+ curveVertex(x3, y3);
+ curveVertex(x4, y4);
+ endShape();
+ }
+
+
+ public void curve(float x1, float y1, float z1,
+ float x2, float y2, float z2,
+ float x3, float y3, float z3,
+ float x4, float y4, float z4) {
+ depthErrorXYZ("curve");
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ /**
+ * Setup forward-differencing matrix to be used for speedy
+ * curve rendering. It's based on using a specific number
+ * of curve segments and just doing incremental adds for each
+ * vertex of the segment, rather than running the mathematically
+ * expensive cubic equation.
+ * @param segments number of curve segments to use when drawing
+ */
+ protected void setup_spline_forward(int segments, float fwd[][]) {
+ float f = 1.0f / segments;
+ float ff = f * f;
+ float fff = ff * f;
+
+ fwd[0][0] = 0; fwd[0][1] = 0; fwd[0][2] = 0; fwd[0][3] = 1;
+ fwd[1][0] = fff; fwd[1][1] = ff; fwd[1][2] = f; fwd[1][3] = 0;
+ fwd[2][0] = 6*fff; fwd[2][1] = 2*ff; fwd[2][2] = 0; fwd[2][3] = 0;
+ fwd[3][0] = 6*fff; fwd[3][1] = 0; fwd[3][2] = 0; fwd[3][3] = 0;
+ }
+
+
+ // internal matrix multiplication routine used by the spline code
+ // should these go to 4 instead of 3?
+ //void mult_curve_matrix(float m[4][4], float g[4][3], float mg[4][3]);
+ protected void mult_spline_matrix(float m[][], float g[][],
+ float mg[][], int dimensions) {
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < dimensions; j++) {
+ mg[i][j] = 0;
+ }
+ }
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < dimensions; j++) {
+ for (int k = 0; k < 4; k++) {
+ mg[i][j] = mg[i][j] + (m[i][k] * g[k][j]);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Draw a segment of spline (bezier or catmull-rom curve)
+ * using the matrix m, which is the basis matrix already
+ * multiplied with the forward differencing matrix.
+ * colorMode(HSB, 360, 100, 100);
+ * because the alpha values were still between 0 and 255.
+ */
+ public void colorMode(int mode,
+ float maxX, float maxY, float maxZ) {
+ colorMode(mode, maxX, maxY, maxZ, colorModeA);
+ }
+
+
+ public void colorMode(int mode,
+ float maxX, float maxY, float maxZ, float maxA) {
+ colorMode = mode;
+
+ colorModeX = maxX; // still needs to be set for hsb
+ colorModeY = maxY;
+ colorModeZ = maxZ;
+ colorModeA = maxA;
+
+ // if color max values are all 1, then no need to scale
+ colorScale = ((maxA != ONE) || (maxX != maxY) ||
+ (maxY != maxZ) || (maxZ != maxA));
+
+ // if color is rgb/0..255 this will make it easier for the
+ // red() green() etc functions
+ colorRgb255 = (colorMode == RGB) &&
+ (colorModeA == 255) && (colorModeX == 255) &&
+ (colorModeY == 255) && (colorModeZ == 255);
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ protected void colorCalc(float gray) {
+ colorCalc(gray, colorModeA);
+ }
+
+
+ protected void colorCalc(float gray, float alpha) {
+ if (gray > colorModeX) gray = colorModeX;
+ if (alpha > colorModeA) alpha = colorModeA;
+
+ if (gray < 0) gray = 0;
+ if (alpha < 0) alpha = 0;
+
+ calcR = colorScale ? (gray / colorModeX) : gray;
+ calcG = calcR;
+ calcB = calcR;
+ calcA = colorScale ? (alpha / colorModeA) : alpha;
+
+ calcRi = (int)(calcR*255); calcGi = (int)(calcG*255);
+ calcBi = (int)(calcB*255); calcAi = (int)(calcA*255);
+ calcColor = (calcAi << 24) | (calcRi << 16) | (calcGi << 8) | calcBi;
+ calcAlpha = (calcAi != 255);
+ }
+
+
+ protected void colorCalc(float x, float y, float z) {
+ colorCalc(x, y, z, colorModeA);
+ }
+
+
+ protected void colorCalc(float x, float y, float z, float a) {
+ if (x > colorModeX) x = colorModeX;
+ if (y > colorModeY) y = colorModeY;
+ if (z > colorModeZ) z = colorModeZ;
+ if (a > colorModeA) a = colorModeA;
+
+ if (x < 0) x = 0;
+ if (y < 0) y = 0;
+ if (z < 0) z = 0;
+ if (a < 0) a = 0;
+
+ switch (colorMode) {
+ case RGB:
+ if (colorScale) {
+ calcR = x / colorModeX;
+ calcG = y / colorModeY;
+ calcB = z / colorModeZ;
+ calcA = a / colorModeA;
+ } else {
+ calcR = x; calcG = y; calcB = z; calcA = a;
+ }
+ break;
+
+ case HSB:
+ x /= colorModeX; // h
+ y /= colorModeY; // s
+ z /= colorModeZ; // b
+
+ calcA = colorScale ? (a/colorModeA) : a;
+
+ if (y == 0) { // saturation == 0
+ calcR = calcG = calcB = z;
+
+ } else {
+ float which = (x - (int)x) * 6.0f;
+ float f = which - (int)which;
+ float p = z * (1.0f - y);
+ float q = z * (1.0f - y * f);
+ float t = z * (1.0f - (y * (1.0f - f)));
+
+ switch ((int)which) {
+ case 0: calcR = z; calcG = t; calcB = p; break;
+ case 1: calcR = q; calcG = z; calcB = p; break;
+ case 2: calcR = p; calcG = z; calcB = t; break;
+ case 3: calcR = p; calcG = q; calcB = z; break;
+ case 4: calcR = t; calcG = p; calcB = z; break;
+ case 5: calcR = z; calcG = p; calcB = q; break;
+ }
+ }
+ break;
+ }
+ calcRi = (int)(255*calcR); calcGi = (int)(255*calcG);
+ calcBi = (int)(255*calcB); calcAi = (int)(255*calcA);
+ calcColor = (calcAi << 24) | (calcRi << 16) | (calcGi << 8) | calcBi;
+ calcAlpha = (calcAi != 255);
+ }
+
+
+ /**
+ * Unpacks AARRGGBB color for direct use with colorCalc.
+ * size(256, 256);
+ * for (int i = 0; i < 256; i++) {
+ * color c = color(0, 0, 0, i);
+ * stroke(c);
+ * line(i, 0, i, 256);
+ * }
+ * ...on the first time through the loop, where (i == 0),
+ * since the color itself is zero (black) then it would appear
+ * indistinguishable from someone having written fill(0).
+ */
+ public void fill(int rgb) {
+ if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
+ fill((float) rgb);
+
+ } else {
+ colorCalcARGB(rgb, colorModeA);
+ fillFromCalc();
+ }
+ }
+
+
+ public void fill(int rgb, float alpha) {
+ if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
+ fill((float) rgb, alpha);
+
+ } else {
+ colorCalcARGB(rgb, alpha);
+ fillFromCalc();
+ }
+ }
+
+
+ public void fill(float gray) {
+ colorCalc(gray);
+ fillFromCalc();
+ }
+
+
+ public void fill(float gray, float alpha) {
+ colorCalc(gray, alpha);
+ fillFromCalc();
+ }
+
+
+ public void fill(float x, float y, float z) {
+ colorCalc(x, y, z);
+ fillFromCalc();
+ }
+
+
+ public void fill(float x, float y, float z, float a) {
+ colorCalc(x, y, z, a);
+ fillFromCalc();
+ }
+
+
+ protected void fillFromCalc() {
+ fill = true;
+ fillR = calcR;
+ fillG = calcG;
+ fillB = calcB;
+ fillA = calcA;
+ fillRi = calcRi;
+ fillGi = calcGi;
+ fillBi = calcBi;
+ fillAi = calcAi;
+ fillColor = calcColor;
+ fillAlpha = calcAlpha;
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public void ambient(int rgb) {
+ depthError("ambient");
+ }
+
+ public void ambient(float gray) {
+ depthError("ambient");
+ }
+
+ public void ambient(float x, float y, float z) {
+ depthError("ambient");
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public void specular(int rgb) {
+ depthError("specular");
+ }
+
+ public void specular(float gray) {
+ depthError("specular");
+ }
+
+ public void specular(float gray, float alpha) {
+ depthError("specular");
+ }
+
+ public void specular(float x, float y, float z) {
+ depthError("specular");
+ }
+
+ public void specular(float x, float y, float z, float a) {
+ depthError("specular");
+ }
+
+ public void shininess(float shine) {
+ depthError("shininess");
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ public void emissive(int rgb) {
+ depthError("emissive");
+ }
+
+ public void emissive(float gray) {
+ depthError("emissive");
+ }
+
+ public void emissive(float x, float y, float z ) {
+ depthError("emissive");
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // LIGHTS
+
+
+ public void lights() {
+ depthError("lights");
+ }
+
+ public void ambientLight(float red, float green, float blue) {
+ depthError("ambientLight");
+ }
+
+ public void ambientLight(float red, float green, float blue,
+ float x, float y, float z) {
+ depthError("ambientLight");
+ }
+
+ public void directionalLight(float red, float green, float blue,
+ float nx, float ny, float nz) {
+ depthError("directionalLight");
+ }
+
+ public void pointLight(float red, float green, float blue,
+ float x, float y, float z) {
+ depthError("pointLight");
+ }
+
+ public void spotLight(float red, float green, float blue,
+ float x, float y, float z,
+ float nx, float ny, float nz,
+ float angle, float concentration) {
+ depthError("spotLight");
+ }
+
+ public void lightFalloff(float constant, float linear, float quadratic) {
+ depthError("lightFalloff");
+ }
+
+ public void lightSpecular(float x, float y, float z) {
+ depthError("lightSpecular");
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ /**
+ * Set the background to a gray or ARGB color.
+ *
- * Simple ear clipping polygon triangulation adapted from code by
- * John W. Ratcliff (jratcliff at verant.com). Presumably
- * this
- * bit of code from the web.
- */
- private void triangulate_polygon() {
- // first we check if the polygon goes clockwise or counterclockwise
- float area = 0.0f;
- for (int p = vertex_end - 1, q = vertex_start; q < vertex_end; p = q++) {
- area += (vertices[q][MX] * vertices[p][MY] -
- vertices[p][MX] * vertices[q][MY]);
- //area += (vertices[q][X] * vertices[p][Y] -
- // vertices[p][X] * vertices[q][Y]);
- }
-
- // then sort the vertices so they are always in a counterclockwise order
- int j = 0;
- //if (0.0f < area) { // def <
- if (area > 0) {
- for (int i = vertex_start; i < vertex_end; i++) {
- j = i - vertex_start;
- vertex_order[j] = i;
- }
- } else {
- for (int i = vertex_start; i < vertex_end; i++) {
- j = i - vertex_start;
- vertex_order[j] = (vertex_end - 1) - j;
- }
- }
-
- // remove vc-2 Vertices, creating 1 triangle every time
- int vc = vertex_end - vertex_start;
- int count = 2*vc; // complex polygon detection
-
- for (int m = 0, v = vc - 1; vc > 2; ) {
- boolean snip = true;
-
- // if we start over again, is a complex polygon
- if (0 >= (count--)) {
- break; // triangulation failed
- }
-
- // get 3 consecutive vertices
- int u = v ; if (vc <= u) u = 0; // previous
- v = u + 1; if (vc <= v) v = 0; // current
- int w = v + 1; if (vc <= w) w = 0; // next
-
- // triangle A B C
- //float Ax, Ay, Bx, By, Cx, Cy, Px, Py;
-
- float Ax = -vertices[vertex_order[u]][MX];
- float Ay = vertices[vertex_order[u]][MY];
- float Bx = -vertices[vertex_order[v]][MX];
- float By = vertices[vertex_order[v]][MY];
- float Cx = -vertices[vertex_order[w]][MX];
- float Cy = vertices[vertex_order[w]][MY];
-
- // first we check if continues going ccw
- if (EPSILON > (((Bx-Ax) * (Cy-Ay)) - ((By-Ay) * (Cx-Ax)))) {
- continue;
- }
-
- for (int p = 0; p < vc; p++) {
- //float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
- //float cCROSSap, bCROSScp, aCROSSbp;
-
- if( (p == u) || (p == v) || (p == w) ) {
- continue;
- }
-
- //float Px = -vertices[vertex_order[p]][X];
- //float Py = vertices[vertex_order[p]][Y];
- float Px = -vertices[vertex_order[p]][MX];
- float Py = vertices[vertex_order[p]][MY];
-
- float ax = Cx - Bx; float ay = Cy - By;
- float bx = Ax - Cx; float by = Ay - Cy;
- float cx = Bx - Ax; float cy = By - Ay;
- float apx = Px - Ax; float apy = Py - Ay;
- float bpx = Px - Bx; float bpy = Py - By;
- float cpx = Px - Cx; float cpy = Py - Cy;
-
- float aCROSSbp = ax * bpy - ay * bpx;
- float cCROSSap = cx * apy - cy * apx;
- float bCROSScp = bx * cpy - by * cpx;
-
- if ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)) {
- snip = false;
- }
- }
-
- if (snip) {
- add_triangle(vertex_order[u], vertex_order[v], vertex_order[w]);
-
- m++;
-
- // remove v from remaining polygon
- for (int s = v, t = v + 1; t < vc; s++, t++) {
- vertex_order[s] = vertex_order[t];
- }
- vc--;
-
- // reset error detection counter
- count = 2 * vc;
- }
- }
- }
-
-
- private void toWorldNormal(float nx, float ny, float nz, float[] out) {
- out[0] =
- modelviewInv.m00*nx + modelviewInv.m10*ny +
- modelviewInv.m20*nz + modelviewInv.m30;
- out[1] =
- modelviewInv.m01*nx + modelviewInv.m11*ny +
- modelviewInv.m21*nz + modelviewInv.m31;
- out[2] =
- modelviewInv.m02*nx + modelviewInv.m12*ny +
- modelviewInv.m22*nz + modelviewInv.m32;
- out[3] =
- modelviewInv.m03*nx + modelviewInv.m13*ny +
- modelviewInv.m23*nz + modelviewInv.m33;
-
- if (out[3] != 0 && out[3] != ONE) {
- // divide by perspective coordinate
- out[0] /= out[3]; out[1] /= out[3]; out[2] /= out[3];
- }
- out[3] = 1;
-
- float nlen = mag(out[0], out[1], out[2]); // normalize
- if (nlen != 0 && nlen != ONE) {
- out[0] /= nlen; out[1] /= nlen; out[2] /= nlen;
- }
- }
-
-
- private void calc_lighting_contribution(int vIndex,
- float[] contribution) {
- calc_lighting_contribution(vIndex, contribution, false);
- }
-
- private void calc_lighting_contribution(int vIndex,
- float[] contribution,
- boolean normalIsWorld) {
- float[] v = vertices[vIndex];
-
- float sr = v[SPR];
- float sg = v[SPG];
- float sb = v[SPB];
-
- float wx = v[VX];
- float wy = v[VY];
- float wz = v[VZ];
- float shine = v[SHINE];
-
- float nx;
- float ny;
- float nz;
- if (!normalIsWorld) {
- toWorldNormal(v[NX], v[NY], v[NZ], worldNormal);
- nx = worldNormal[X];
- ny = worldNormal[Y];
- nz = worldNormal[Z];
- }
- else {
- nx = v[NX];
- ny = v[NY];
- nz = v[NZ];
- }
-
-
- // Since the camera space == world space,
- // we can test for visibility by the dot product of
- // the normal with the direction from pt. to eye.
- float dir = dot(nx, ny, nz, -wx, -wy, -wz);
- // If normal is away from camera, choose its opposite.
- // If we add backface culling, this will be backfacing
- // (but since this is per vertex, it's more complicated)
- if (dir < 0) {
- nx = -nx;
- ny = -ny;
- nz = -nz;
- }
-
- // These two terms will sum the contributions from the various lights
- contribution[LIGHT_AMBIENT_R] = 0;
- contribution[LIGHT_AMBIENT_G] = 0;
- contribution[LIGHT_AMBIENT_B] = 0;
-
- contribution[LIGHT_DIFFUSE_R] = 0;
- contribution[LIGHT_DIFFUSE_G] = 0;
- contribution[LIGHT_DIFFUSE_B] = 0;
-
- contribution[LIGHT_SPECULAR_R] = 0;
- contribution[LIGHT_SPECULAR_G] = 0;
- contribution[LIGHT_SPECULAR_B] = 0;
-
- // for (int i = 0; i < MAX_LIGHTS; i++) {
- // if (!light[i]) continue;
- for (int i = 0; i < lightCount; i++) {
-
- float denom = lightFalloffConstant[i];
- float spotTerm = 1;
-
- if (lightType[i] == AMBIENT) {
- if (lightFalloffQuadratic[i] != 0 || lightFalloffLinear[i] != 0) {
- // Falloff depends on distance
- float distSq = mag(lightPosition[i][0] - wx,
- lightPosition[i][1] - wy,
- lightPosition[i][2] - wz);
- denom +=
- lightFalloffQuadratic[i] * distSq +
- lightFalloffLinear[i] * (float) sqrt(distSq);
- }
- if (denom == 0) denom = 1;
-
- contribution[LIGHT_AMBIENT_R] += lightDiffuse[i][0] / denom;
- contribution[LIGHT_AMBIENT_G] += lightDiffuse[i][1] / denom;
- contribution[LIGHT_AMBIENT_B] += lightDiffuse[i][2] / denom;
-
- } else {
- // If not ambient, we must deal with direction
-
- // li is the vector from the vertex to the light
- float lix, liy, liz;
- float lightDir_dot_li = 0;
- float n_dot_li = 0;
-
- if (lightType[i] == DIRECTIONAL) {
- lix = -lightNormal[i][0];
- liy = -lightNormal[i][1];
- liz = -lightNormal[i][2];
- denom = 1;
- n_dot_li = (nx * lix + ny * liy + nz * liz);
- // If light is lighting the face away from the camera, ditch
- if (n_dot_li <= 0) {
- continue;
- }
- } else { // Point or spot light (must deal also with light location)
- lix = lightPosition[i][0] - wx;
- liy = lightPosition[i][1] - wy;
- liz = lightPosition[i][2] - wz;
- // normalize
- float distSq = mag(lix, liy, liz);
- if (distSq != 0) {
- lix /= distSq;
- liy /= distSq;
- liz /= distSq;
- }
- n_dot_li = (nx * lix + ny * liy + nz * liz);
- // If light is lighting the face away from the camera, ditch
- if (n_dot_li <= 0) {
- continue;
- }
-
- if (lightType[i] == SPOT) { // Must deal with spot cone
- lightDir_dot_li =
- -(lightNormal[i][0] * lix +
- lightNormal[i][1] * liy +
- lightNormal[i][2] * liz);
- // Outside of spot cone
- if (lightDir_dot_li <= lightSpotAngleCos[i]) {
- continue;
- }
- spotTerm = pow(lightDir_dot_li, lightSpotConcentration[i]);
- }
-
- if (lightFalloffQuadratic[i] != 0 || lightFalloffLinear[i] != 0) {
- // Falloff depends on distance
- denom +=
- lightFalloffQuadratic[i] * distSq +
- lightFalloffLinear[i] * (float) sqrt(distSq);
- }
- }
- // Directional, point, or spot light:
-
- // We know n_dot_li > 0 from above "continues"
-
- if (denom == 0)
- denom = 1;
- float mul = n_dot_li * spotTerm / denom;
- contribution[LIGHT_DIFFUSE_R] += lightDiffuse[i][0] * mul;
- contribution[LIGHT_DIFFUSE_G] += lightDiffuse[i][1] * mul;
- contribution[LIGHT_DIFFUSE_B] += lightDiffuse[i][2] * mul;
-
- // SPECULAR
-
- // If the material and light have a specular component.
- if ((sr > 0 || sg > 0 || sb > 0) &&
- (lightSpecular[i][0] > 0 ||
- lightSpecular[i][1] > 0 ||
- lightSpecular[i][2] > 0)) {
-
- float vmag = mag(wx, wy, wz);
- if (vmag != 0) {
- wx /= vmag;
- wy /= vmag;
- wz /= vmag;
- }
- float sx = lix - wx;
- float sy = liy - wy;
- float sz = liz - wz;
- vmag = mag(sx, sy, sz);
- if (vmag != 0) {
- sx /= vmag;
- sy /= vmag;
- sz /= vmag;
- }
- float s_dot_n = (sx * nx + sy * ny + sz * nz);
-
- if (s_dot_n > 0) {
- s_dot_n = pow(s_dot_n, shine);
- mul = s_dot_n * spotTerm / denom;
- contribution[LIGHT_SPECULAR_R] += lightSpecular[i][0] * mul;
- contribution[LIGHT_SPECULAR_G] += lightSpecular[i][1] * mul;
- contribution[LIGHT_SPECULAR_B] += lightSpecular[i][2] * mul;
- }
-
- }
- }
- }
- /*target[toffset + 0] = min(1, er + dr * diffuse_r);
- target[toffset + 1] = min(1, eg + dg * diffuse_g);
- target[toffset + 2] = min(1, eb + db * diffuse_b);
-
- target[SPR] = min(1, sr * specular_r);
- target[SPG] = min(1, sg * specular_g);
- target[SPB] = min(1, sb * specular_b);*/
- return;
- }
-
-
- // Multiply the lighting contribution into the vertex's colors.
- // Only do this when there is ONE lighting per vertex
- // (MANUAL_VERTEX_NORMAL or SHAPE_NORMAL mode).
- private void apply_lighting_contribution(int vIndex, float[] contribution) {
- float[] v = vertices[vIndex];
-
- v[R] = min(1, v[ER] + v[AR] * contribution[LIGHT_AMBIENT_R] +
- v[DR] * contribution[LIGHT_DIFFUSE_R]);
- v[G] = min(1, v[EG] + v[AG] * contribution[LIGHT_AMBIENT_G] +
- v[DG] * contribution[LIGHT_DIFFUSE_G]);
- v[B] = min(1, v[EB] + v[AB] * contribution[LIGHT_AMBIENT_R] +
- v[DB] * contribution[LIGHT_DIFFUSE_B]);
- v[A] = min(1, v[DA]);
-
- v[SPR] = min(1, v[SPR] * contribution[LIGHT_SPECULAR_R]);
- v[SPG] = min(1, v[SPG] * contribution[LIGHT_SPECULAR_G]);
- v[SPB] = min(1, v[SPB] * contribution[LIGHT_SPECULAR_B]);
- v[SPA] = min(1, v[SPA]);
-
- v[BEEN_LIT] = 1;
- }
-
-
- private void light_vertex_always(int vIndex, float[] contribution) {
- calc_lighting_contribution(vIndex, contribution);
- apply_lighting_contribution(vIndex, contribution);
- }
-
-
- private void light_vertex_if_not_already_lit(int vIndex,
- float[] contribution) {
- if (vertices[vIndex][BEEN_LIT] == 0) {
- light_vertex_always(vIndex, contribution);
- }
- }
-
-
- private void copy_prelit_vertex_color_to_triangle(int triIndex, int vIndex,
- int colorIndex) {
- float[] triColor = triangleColors[triIndex][colorIndex];
- float[] v = vertices[vIndex];
-
- triColor[TRI_DIFFUSE_R] = v[R];
- triColor[TRI_DIFFUSE_G] = v[G];
- triColor[TRI_DIFFUSE_B] = v[B];
- triColor[TRI_DIFFUSE_A] = v[A];
- triColor[TRI_SPECULAR_R] = v[SPR];
- triColor[TRI_SPECULAR_G] = v[SPG];
- triColor[TRI_SPECULAR_B] = v[SPB];
- triColor[TRI_SPECULAR_A] = v[SPA];
- }
-
-
- private void copy_vertex_color_to_triangle(int triIndex,
- int vIndex, int colorIndex,
- float[] lightContribution) {
- float[] triColor = triangleColors[triIndex][colorIndex];
- float[] v = vertices[vIndex];
-
- triColor[TRI_DIFFUSE_R] =
- min(1, v[ER] + v[AR] * lightContribution[LIGHT_AMBIENT_R] +
- v[DR] * lightContribution[LIGHT_DIFFUSE_R]);
- triColor[TRI_DIFFUSE_G] =
- min(1, v[EG] + v[AG] * lightContribution[LIGHT_AMBIENT_G] +
- v[DG] * lightContribution[LIGHT_DIFFUSE_G]);
- triColor[TRI_DIFFUSE_B] =
- min(1, v[EB] + v[AB] * lightContribution[LIGHT_AMBIENT_R] +
- v[DB] * lightContribution[LIGHT_DIFFUSE_B]);
- triColor[TRI_DIFFUSE_A] = min(1, v[DA]);
-
- triColor[TRI_SPECULAR_R] =
- min(1, v[SPR] * lightContribution[LIGHT_SPECULAR_R]);
- triColor[TRI_SPECULAR_G] =
- min(1, v[SPG] * lightContribution[LIGHT_SPECULAR_G]);
- triColor[TRI_SPECULAR_B] =
- min(1, v[SPB] * lightContribution[LIGHT_SPECULAR_B]);
- triColor[TRI_SPECULAR_A] = min(1, v[SPA]);
- }
-
-
- private void light_triangle(int triIndex, float[] lightContribution) {
- int vIndex = triangles[triIndex][VERTEX1];
- copy_vertex_color_to_triangle(triIndex, vIndex, 0, lightContribution);
- vIndex = triangles[triIndex][VERTEX2];
- copy_vertex_color_to_triangle(triIndex, vIndex, 1, lightContribution);
- vIndex = triangles[triIndex][VERTEX3];
- copy_vertex_color_to_triangle(triIndex, vIndex, 2, lightContribution);
- }
-
-
- private void crossProduct(float[] u, float[] v, float[] out) {
- out[0] = u[1]*v[2] - u[2]*v[1];
- out[1] = u[2]*v[0] - u[0]*v[2];
- out[2] = u[0]*v[1] - u[1]*v[0];
- }
-
-
- private void light_triangle(int triIndex) {
- int vIndex;
-
- // Handle lighting on, but no lights (in this case, just use emissive)
- // This wont be used currently because lightCount == 0 is don't use
- // lighting at all... So. OK. If that ever changes, use the below:
- /*
- if (lightCount == 0) {
- vIndex = triangles[triIndex][VERTEX1];
- copy_emissive_vertex_color_to_triangle(triIndex, vIndex, 0);
- vIndex = triangles[triIndex][VERTEX2];
- copy_emissive_vertex_color_to_triangle(triIndex, vIndex, 1);
- vIndex = triangles[triIndex][VERTEX3];
- copy_emissive_vertex_color_to_triangle(triIndex, vIndex, 2);
- return;
- }
- */
-
- // In MANUAL_VERTEX_NORMAL mode, we have a specific normal
- // for each vertex. In that case, we light any verts that
- // haven't already been lit and copy their colors straight
- // into the triangle.
- if (normalMode == MANUAL_VERTEX_NORMAL) {
- vIndex = triangles[triIndex][VERTEX1];
- light_vertex_if_not_already_lit(vIndex, tempLightingContribution);
- copy_prelit_vertex_color_to_triangle(triIndex, vIndex, 0);
-
- vIndex = triangles[triIndex][VERTEX2];
- light_vertex_if_not_already_lit(vIndex, tempLightingContribution);
- copy_prelit_vertex_color_to_triangle(triIndex, vIndex, 1);
-
- vIndex = triangles[triIndex][VERTEX3];
- light_vertex_if_not_already_lit(vIndex, tempLightingContribution);
- copy_prelit_vertex_color_to_triangle(triIndex, vIndex, 2);
-
- }
-
- // If the lighting doesn't depend on the vertex position, do the
- // following: We've already dealt with MANUAL_SHAPE_NORMAL mode before
- // we got into this function, so here we only have to deal with
- // AUTO_NORMAL mode. So we calculate the normal for this triangle,
- // and use that for the lighting.
- else if (!lightingDependsOnVertexPosition) {
- vIndex = triangles[triIndex][VERTEX1];
- int vIndex2 = triangles[triIndex][VERTEX2];
- int vIndex3 = triangles[triIndex][VERTEX3];
-
- /*
- float[] dv1 = new float[] {vertices[vIndex2][VX] - vertices[vIndex][VX],
- vertices[vIndex2][VY] - vertices[vIndex][VY],
- vertices[vIndex2][VZ] - vertices[vIndex][VZ]};
- float[] dv2 = new float[] {vertices[vIndex3][VX] - vertices[vIndex][VX],
- vertices[vIndex3][VY] - vertices[vIndex][VY],
- vertices[vIndex3][VZ] - vertices[vIndex][VZ]};
- */
- dv1[0] = vertices[vIndex2][VX] - vertices[vIndex][VX];
- dv1[1] = vertices[vIndex2][VY] - vertices[vIndex][VY];
- dv1[2] = vertices[vIndex2][VZ] - vertices[vIndex][VZ];
-
- dv2[0] = vertices[vIndex3][VX] - vertices[vIndex][VX];
- dv2[1] = vertices[vIndex3][VY] - vertices[vIndex][VY];
- dv2[2] = vertices[vIndex3][VZ] - vertices[vIndex][VZ];
-
- //float[] norm = new float[3];
- crossProduct(dv1, dv2, norm);
- float nMag = mag(norm[X], norm[Y], norm[Z]);
- if (nMag != 0 && nMag != ONE) {
- norm[X] /= nMag; norm[Y] /= nMag; norm[Z] /= nMag;
- }
- vertices[vIndex][NX] = norm[X];
- vertices[vIndex][NY] = norm[Y];
- vertices[vIndex][NZ] = norm[Z];
-
- // The true at the end says the normal is already in world coordinates
- calc_lighting_contribution(vIndex, tempLightingContribution, true);
- copy_vertex_color_to_triangle(triIndex, vIndex, 0,
- tempLightingContribution);
- copy_vertex_color_to_triangle(triIndex, vIndex2, 1,
- tempLightingContribution);
- copy_vertex_color_to_triangle(triIndex, vIndex3, 2,
- tempLightingContribution);
- }
-
- // If lighting is position-dependent
- else {
- if (normalMode == MANUAL_SHAPE_NORMAL) {
- vIndex = triangles[triIndex][VERTEX1];
- vertices[vIndex][NX] = vertices[vertex_start][NX];
- vertices[vIndex][NY] = vertices[vertex_start][NY];
- vertices[vIndex][NZ] = vertices[vertex_start][NZ];
- calc_lighting_contribution(vIndex, tempLightingContribution);
- copy_vertex_color_to_triangle(triIndex, vIndex, 0,
- tempLightingContribution);
-
- vIndex = triangles[triIndex][VERTEX2];
- vertices[vIndex][NX] = vertices[vertex_start][NX];
- vertices[vIndex][NY] = vertices[vertex_start][NY];
- vertices[vIndex][NZ] = vertices[vertex_start][NZ];
- calc_lighting_contribution(vIndex, tempLightingContribution);
- copy_vertex_color_to_triangle(triIndex, vIndex, 1,
- tempLightingContribution);
-
- vIndex = triangles[triIndex][VERTEX3];
- vertices[vIndex][NX] = vertices[vertex_start][NX];
- vertices[vIndex][NY] = vertices[vertex_start][NY];
- vertices[vIndex][NZ] = vertices[vertex_start][NZ];
- calc_lighting_contribution(vIndex, tempLightingContribution);
- copy_vertex_color_to_triangle(triIndex, vIndex, 2,
- tempLightingContribution);
- }
-
- // lighting mode is AUTO_NORMAL
- else {
- vIndex = triangles[triIndex][VERTEX1];
- int vIndex2 = triangles[triIndex][VERTEX2];
- int vIndex3 = triangles[triIndex][VERTEX3];
- /*
- float[] dv1 = new float[] {vertices[vIndex2][VX] - vertices[vIndex][VX],
- vertices[vIndex2][VY] - vertices[vIndex][VY],
- vertices[vIndex2][VZ] - vertices[vIndex][VZ]};
- float[] dv2 = new float[] {vertices[vIndex3][VX] - vertices[vIndex][VX],
- vertices[vIndex3][VY] - vertices[vIndex][VY],
- vertices[vIndex3][VZ] - vertices[vIndex][VZ]};
- */
- dv1[0] = vertices[vIndex2][VX] - vertices[vIndex][VX];
- dv1[1] = vertices[vIndex2][VY] - vertices[vIndex][VY];
- dv1[2] = vertices[vIndex2][VZ] - vertices[vIndex][VZ];
- dv2[0] = vertices[vIndex3][VX] - vertices[vIndex][VX];
- dv2[1] = vertices[vIndex3][VY] - vertices[vIndex][VY];
- dv2[2] = vertices[vIndex3][VZ] - vertices[vIndex][VZ];
-
- //float[] norm = new float[3];
- crossProduct(dv1, dv2, norm);
- float nMag = mag(norm[X], norm[Y], norm[Z]);
- if (nMag != 0 && nMag != ONE) {
- norm[X] /= nMag; norm[Y] /= nMag; norm[Z] /= nMag;
- }
- vertices[vIndex][NX] = norm[X];
- vertices[vIndex][NY] = norm[Y];
- vertices[vIndex][NZ] = norm[Z];
- // The true at the end says the normal is already in world coordinates
- calc_lighting_contribution(vIndex, tempLightingContribution, true);
- copy_vertex_color_to_triangle(triIndex, vIndex, 0,
- tempLightingContribution);
-
- vertices[vIndex2][NX] = norm[X];
- vertices[vIndex2][NY] = norm[Y];
- vertices[vIndex2][NZ] = norm[Z];
- // The true at the end says the normal is already in world coordinates
- calc_lighting_contribution(vIndex2, tempLightingContribution, true);
- copy_vertex_color_to_triangle(triIndex, vIndex2, 1,
- tempLightingContribution);
-
- vertices[vIndex3][NX] = norm[X];
- vertices[vIndex3][NY] = norm[Y];
- vertices[vIndex3][NZ] = norm[Z];
- // The true at the end says the normal is already in world coordinates
- calc_lighting_contribution(vIndex3, tempLightingContribution, true);
- copy_vertex_color_to_triangle(triIndex, vIndex3, 2,
- tempLightingContribution);
- }
- }
- }
-
-
- protected void handle_lighting() {
-
- // If the lighting does not depend on vertex position and there is a single
- // normal specified for this shape, go ahead and apply the same lighting
- // contribution to every vertex in this shape (one lighting calc!)
- if (!lightingDependsOnVertexPosition && normalMode == MANUAL_SHAPE_NORMAL) {
- calc_lighting_contribution(vertex_start, tempLightingContribution);
- for (int tri = 0; tri < triangleCount; tri++) {
- light_triangle(tri, tempLightingContribution);
- }
- }
- // Otherwise light each triangle individually...
- else {
- for (int tri = 0; tri < triangleCount; tri++) {
- light_triangle(tri);
- }
- }
- }
-
-
- protected void handle_no_lighting() {
- int vIndex;
- for (int tri = 0; tri < triangleCount; tri++) {
- vIndex = triangles[tri][VERTEX1];
- copy_prelit_vertex_color_to_triangle(tri, vIndex, 0);
- vIndex = triangles[tri][VERTEX2];
- copy_prelit_vertex_color_to_triangle(tri, vIndex, 1);
- vIndex = triangles[tri][VERTEX3];
- copy_prelit_vertex_color_to_triangle(tri, vIndex, 2);
- }
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // BASIC SHAPES
-
-
- public void point(float x, float y) {
- point(x, y, 0);
- }
-
-
- public void point(float x, float y, float z) {
- /*
- beginShape(POINTS);
- vertex(x, y, z);
- endShape();
- */
-
- // hacked workaround for carlos line bug
- beginShape(LINES);
- vertex(x, y, z);
- vertex(x + EPSILON, y + EPSILON, z);
- endShape();
- }
-
- /*
- private void point3(float x, float y, float z, int color) {
- // need to get scaled version of the stroke
- float x1 = screenX(x - 0.5f, y - 0.5f, z);
- float y1 = screenY(x - 0.5f, y - 0.5f, z);
- float x2 = screenX(x + 0.5f, y + 0.5f, z);
- float y2 = screenY(x + 0.5f, y + 0.5f, z);
-
- float weight = (abs(x2 - x1) + abs(y2 - y1)) / 2f;
- if (weight < 1.5f) {
- int xx = (int) ((x1 + x2) / 2f);
- int yy = (int) ((y1 + y2) / 2f);
- //point0(xx, yy, z, color);
- zbuffer[yy*width + xx] = screenZ(x, y, z);
- //stencil?
-
- } else {
- // actually has some weight, need to draw shapes instead
- // these will be
- }
- }
- */
-
-
- public void line(float x1, float y1, float x2, float y2) {
- line(x1, y1, 0, x2, y2, 0);
- }
-
-
- public void line(float x1, float y1, float z1,
- float x2, float y2, float z2) {
- beginShape(LINES);
- vertex(x1, y1, z1);
- vertex(x2, y2, z2);
- endShape();
- }
-
-
- public void triangle(float x1, float y1, float x2, float y2,
- float x3, float y3) {
- beginShape(TRIANGLES);
- normal(0, 0, 1);
- vertex(x1, y1);
- vertex(x2, y2);
- vertex(x3, y3);
- endShape();
- }
-
-
- public void quad(float x1, float y1, float x2, float y2,
- float x3, float y3, float x4, float y4) {
- beginShape(QUADS);
- normal(0, 0, 1);
- vertex(x1, y1);
- vertex(x2, y2);
- vertex(x3, y3);
- vertex(x4, y4);
- endShape();
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // PLACED SHAPES
-
-
- protected void rectImpl(float x1, float y1, float x2, float y2) {
- quad(x1, y1, x2, y1, x2, y2, x1, y2);
- }
-
-
- protected void ellipseImpl(float x1, float y1, float w, float h) {
- float hradius = w / 2f;
- float vradius = h / 2f;
-
- float centerX = x1 + hradius;
- float centerY = y1 + vradius;
-
- // adapt accuracy to radii used w/ a minimum of 4 segments [toxi]
- // now uses current scale factors to determine "real" transformed radius
-
- //int cAccuracy = (int)(4+Math.sqrt(hradius*abs(m00)+vradius*abs(m11))*2);
- //int cAccuracy = (int)(4+Math.sqrt(hradius+vradius)*2);
-
- // notched this up to *3 instead of *2 because things were
- // looking a little rough, i.e. the calculate->arctangent example [fry]
-
- // also removed the m00 and m11 because those were causing weirdness
- // need an actual measure of magnitude in there [fry]
-
- int cAccuracy = (int)(4+Math.sqrt(hradius+vradius)*3);
-
- // [toxi031031] adapted to use new lookup tables
- float inc = (float)SINCOS_LENGTH / cAccuracy;
-
- float val = 0;
- /*
- beginShape(POLYGON);
- for (int i = 0; i < cAccuracy; i++) {
- vertex(centerX + cosLUT[(int) val] * hradius,
- centerY + sinLUT[(int) val] * vradius);
- val += inc;
- }
- endShape();
- */
-
- if (fill) {
- boolean savedStroke = stroke;
- stroke = false;
-
- beginShape(TRIANGLE_FAN);
- normal(0, 0, 1);
- vertex(centerX, centerY);
- for (int i = 0; i < cAccuracy; i++) {
- vertex(centerX + cosLUT[(int) val] * hradius,
- centerY + sinLUT[(int) val] * vradius);
- val += inc;
- }
- // back to the beginning
- vertex(centerX + cosLUT[0] * hradius,
- centerY + sinLUT[0] * vradius);
- endShape();
-
- stroke = savedStroke;
- }
-
- if (stroke) {
- boolean savedFill = fill;
- fill = false;
-
- val = 0;
- beginShape(LINE_LOOP);
- for (int i = 0; i < cAccuracy; i++) {
- vertex(centerX + cosLUT[(int) val] * hradius,
- centerY + sinLUT[(int) val] * vradius);
- val += inc;
- }
- endShape();
-
- fill = savedFill;
- }
- }
-
-
- /**
- * Start and stop are in radians, converted by the parent function.
- * Note that the radians can be greater (or less) than TWO_PI.
- * This is so that an arc can be drawn that crosses zero mark,
- * and the user will still collect $200.
- */
- protected void arcImpl(float x1, float y1, float w, float h,
- float start, float stop) {
- float hr = w / 2f;
- float vr = h / 2f;
-
- float centerX = x1 + hr;
- float centerY = y1 + vr;
-
- if (fill) {
- // shut off stroke for a minute
- boolean savedStroke = stroke;
- stroke = false;
-
- int startLUT = (int) (0.5f + (start / TWO_PI) * SINCOS_LENGTH);
- int stopLUT = (int) (0.5f + (stop / TWO_PI) * SINCOS_LENGTH);
-
- beginShape(TRIANGLE_FAN);
- vertex(centerX, centerY);
- int increment = 1; // what's a good algorithm? stopLUT - startLUT;
- for (int i = startLUT; i < stopLUT; i += increment) {
- int ii = i % SINCOS_LENGTH;
- vertex(centerX + cosLUT[ii] * hr,
- centerY + sinLUT[ii] * vr);
- }
- // draw last point explicitly for accuracy
- vertex(centerX + cosLUT[stopLUT % SINCOS_LENGTH] * hr,
- centerY + sinLUT[stopLUT % SINCOS_LENGTH] * vr);
- endShape();
-
- stroke = savedStroke;
- }
-
- if (stroke) {
- // Almost identical to above, but this uses a LINE_STRIP
- // and doesn't include the first (center) vertex.
-
- boolean savedFill = fill;
- fill = false;
-
- int startLUT = (int) (0.5f + (start / TWO_PI) * SINCOS_LENGTH);
- int stopLUT = (int) (0.5f + (stop / TWO_PI) * SINCOS_LENGTH);
-
- beginShape(LINE_STRIP);
- int increment = 1; // what's a good algorithm? stopLUT - startLUT;
- for (int i = startLUT; i < stopLUT; i += increment) {
- int ii = i % SINCOS_LENGTH;
- vertex(centerX + cosLUT[ii] * hr,
- centerY + sinLUT[ii] * vr);
- }
- // draw last point explicitly for accuracy
- vertex(centerX + cosLUT[stopLUT % SINCOS_LENGTH] * hr,
- centerY + sinLUT[stopLUT % SINCOS_LENGTH] * vr);
- endShape();
-
- fill = savedFill;
- }
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // 3D BOX
-
-
- public void box(float size) {
- box(size, size, size);
- }
-
-
- // OPT this isn't the least bit efficient
- // because it redraws lines along the vertices
- // ugly ugly ugly!
- public void box(float w, float h, float d) {
- float x1 = -w/2f; float x2 = w/2f;
- float y1 = -h/2f; float y2 = h/2f;
- float z1 = -d/2f; float z2 = d/2f;
-
- if (triangle != null) { // triangle is null in gl
- triangle.setCulling(true);
- }
-
- beginShape(QUADS);
-
- // front
- normal(0, 0, 1);
- vertex(x1, y1, z1);
- vertex(x2, y1, z1);
- vertex(x2, y2, z1);
- vertex(x1, y2, z1);
-
- // right
- normal(1, 0, 0);
- vertex(x2, y1, z1);
- vertex(x2, y1, z2);
- vertex(x2, y2, z2);
- vertex(x2, y2, z1);
-
- // back
- normal(0, 0, -1);
- vertex(x2, y1, z2);
- vertex(x1, y1, z2);
- vertex(x1, y2, z2);
- vertex(x2, y2, z2);
-
- // left
- normal(-1, 0, 0);
- vertex(x1, y1, z2);
- vertex(x1, y1, z1);
- vertex(x1, y2, z1);
- vertex(x1, y2, z2);
-
- // top
- normal(0, 1, 0);
- vertex(x1, y1, z2);
- vertex(x2, y1, z2);
- vertex(x2, y1, z1);
- vertex(x1, y1, z1);
-
- // bottom
- normal(0, -1, 0);
- vertex(x1, y2, z1);
- vertex(x2, y2, z1);
- vertex(x2, y2, z2);
- vertex(x1, y2, z2);
-
- endShape();
-
- if (triangle != null) { // triangle is null in gl
- triangle.setCulling(false);
- }
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // 3D SPHERE
-
-
- // [toxi031031] used by the new sphere code below
- // precompute vertices along unit sphere with new detail setting
-
- public void sphereDetail(int res) {
- if (res < 3) res = 3; // force a minimum res
- if (res == sphereDetail) return;
-
- float delta = (float)SINCOS_LENGTH/res;
- float[] cx = new float[res];
- float[] cz = new float[res];
- // calc unit circle in XZ plane
- for (int i = 0; i < res; i++) {
- cx[i] = cosLUT[(int) (i*delta) % SINCOS_LENGTH];
- cz[i] = sinLUT[(int) (i*delta) % SINCOS_LENGTH];
- }
- // computing vertexlist
- // vertexlist starts at south pole
- int vertCount = res * (res-1) + 2;
- int currVert = 0;
-
- // re-init arrays to store vertices
- sphereX = new float[vertCount];
- sphereY = new float[vertCount];
- sphereZ = new float[vertCount];
-
- float angle_step = (SINCOS_LENGTH*0.5f)/res;
- float angle = angle_step;
-
- // step along Y axis
- for (int i = 1; i < res; i++) {
- float curradius = sinLUT[(int) angle % SINCOS_LENGTH];
- float currY = -cosLUT[(int) angle % SINCOS_LENGTH];
- for (int j = 0; j < res; j++) {
- sphereX[currVert] = cx[j] * curradius;
- sphereY[currVert] = currY;
- sphereZ[currVert++] = cz[j] * curradius;
- }
- angle += angle_step;
- }
- sphereDetail = res;
- }
-
-
- /**
- * Draw a sphere with radius r centered at coordinate 0, 0, 0.
- *
- * [toxi031031] new sphere code. removed all multiplies with
- * radius, as scale() will take care of that anyway
- *
- * [toxi031223] updated sphere code (removed modulos)
- * and introduced sphereAt(x,y,z,r)
- * to avoid additional translate()'s on the user/sketch side
- *
- */
- public void sphere(float r) {
- float x = 0; // TODO clean this back up again
- float y = 0;
- float z = 0;
-
- if (sphereDetail == 0) {
- sphereDetail(30);
- }
-
- int v1,v11,v2;
- pushMatrix();
- if (x!=0f && y!=0f && z!=0f) translate(x,y,z);
- scale(r);
-
- if (triangle != null) { // triangle is null in gl
- triangle.setCulling(true);
- }
-
- // 1st ring from south pole
- beginShape(TRIANGLE_STRIP);
- for (int i = 0; i < sphereDetail; i++) {
- normal(0, -1, 0);
- vertex(0, -1, 0);
- normal(sphereX[i], sphereY[i], sphereZ[i]);
- vertex(sphereX[i], sphereY[i], sphereZ[i]);
- }
- //normal(0, -1, 0);
- vertex(0, -1, 0);
- normal(sphereX[0], sphereY[0], sphereZ[0]);
- vertex(sphereX[0], sphereY[0], sphereZ[0]);
- endShape();
-
- // middle rings
- int voff = 0;
- for(int i = 2; i < sphereDetail; i++) {
- v1=v11=voff;
- voff += sphereDetail;
- v2=voff;
- beginShape(TRIANGLE_STRIP);
- for (int j = 0; j < sphereDetail; j++) {
- normal(sphereX[v1], sphereY[v1], sphereZ[v1]);
- vertex(sphereX[v1], sphereY[v1], sphereZ[v1++]);
- normal(sphereX[v2], sphereY[v2], sphereZ[v2]);
- vertex(sphereX[v2], sphereY[v2], sphereZ[v2++]);
- }
- // close each ring
- v1=v11;
- v2=voff;
- normal(sphereX[v1], sphereY[v1], sphereZ[v1]);
- vertex(sphereX[v1], sphereY[v1], sphereZ[v1]);
- normal(sphereX[v2], sphereY[v2], sphereZ[v2]);
- vertex(sphereX[v2], sphereY[v2], sphereZ[v2]);
- endShape();
- }
-
- // add the northern cap
- beginShape(TRIANGLE_STRIP);
- for (int i = 0; i < sphereDetail; i++) {
- v2 = voff + i;
- normal(sphereX[v2], sphereY[v2], sphereZ[v2]);
- vertex(sphereX[v2], sphereY[v2], sphereZ[v2]);
- normal(0, 1, 0);
- vertex(0, 1, 0);
- }
- normal(sphereX[voff], sphereY[voff], sphereZ[voff]);
- vertex(sphereX[voff], sphereY[voff], sphereZ[voff]);
- normal(0, 1, 0);
- vertex(0, 1, 0);
- endShape();
- popMatrix();
-
- if (triangle != null) { // triangle is null in gl
- triangle.setCulling(false);
- }
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // CURVES
-
-
- public void bezier(float x1, float y1,
- float x2, float y2,
- float x3, float y3,
- float x4, float y4) {
- bezier(x1, y1, 0,
- x2, y2, 0,
- x3, y3, 0,
- x4, y4, 0);
- }
-
-
- public void bezier(float x1, float y1, float z1,
- float x2, float y2, float z2,
- float x3, float y3, float z3,
- float x4, float y4, float z4) {
- beginShape(LINE_STRIP);
- vertex(x1, y1, z1);
- bezierVertex(x2, y2, z2,
- x3, y3, z3,
- x4, y4, z4);
- endShape();
- }
-
-
- public void curve(float x1, float y1,
- float x2, float y2,
- float x3, float y3,
- float x4, float y4) {
- curve(x1, y1, 0,
- x2, y2, 0,
- x3, y3, 0,
- x4, y4, 0);
- }
-
-
- public void curve(float x1, float y1, float z1,
- float x2, float y2, float z2,
- float x3, float y3, float z3,
- float x4, float y4, float z4) {
- beginShape(LINE_STRIP);
- curveVertex(x1, y1, z1);
- curveVertex(x2, y2, z2);
- curveVertex(x3, y3, z3);
- curveVertex(x4, y4, z4);
- endShape();
- }
-
-
- //////////////////////////////////////////////////////////////
-
-
- protected void imageImpl(PImage image,
- float x1, float y1, float x2, float y2,
- int u1, int v1, int u2, int v2) {
-
- //float x2 = x1 + w;
- //float y2 = y1 + h;
-
- boolean savedStroke = stroke;
- boolean savedFill = fill;
- int savedTextureMode = textureMode;
-
- stroke = false;
- fill = true;
- textureMode = IMAGE;
-
- float savedFillR = fillR;
- float savedFillG = fillG;
- float savedFillB = fillB;
- float savedFillA = fillA;
-
- if (tint) {
- fillR = tintR;
- fillG = tintG;
- fillB = tintB;
- fillA = tintA;
-
- } else {
- fillR = 1;
- fillG = 1;
- fillB = 1;
- fillA = 1;
- }
-
- //System.out.println(fill + " " + fillR + " " + fillG + " " + fillB);
-
- beginShape(QUADS);
- texture(image);
- vertex(x1, y1, u1, v1);
- vertex(x1, y2, u1, v2);
- vertex(x2, y2, u2, v2);
- vertex(x2, y1, u2, v1);
- endShape();
-
- stroke = savedStroke;
- fill = savedFill;
- textureMode = savedTextureMode;
-
- fillR = savedFillR;
- fillG = savedFillG;
- fillB = savedFillB;
- fillA = savedFillA;
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // MATRIX TRANSFORMATIONS
-
-
- public void translate(float tx, float ty) {
- translate(tx, ty, 0);
- }
-
-
- public void translate(float tx, float ty, float tz) {
- forwardTransform.translate(tx, ty, tz);
- reverseTransform.invTranslate(tx, ty, tz);
- }
-
-
- /**
- * Two dimensional rotation. Same as rotateZ (this is identical
- * to a 3D rotation along the z-axis) but included for clarity --
- * it'd be weird for people drawing 2D graphics to be using rotateZ.
- * And they might kick our a-- for the confusion.
- */
- public void rotate(float angle) {
- rotateZ(angle);
- }
-
-
- // OPT could save several multiplies for the 0s and 1s by just
- // putting the multMatrix code here and removing uneccessary terms
-
- public void rotateX(float angle) {
- forwardTransform.rotateX(angle);
- reverseTransform.invRotateX(angle);
- }
-
-
- public void rotateY(float angle) {
- forwardTransform.rotateY(angle);
- reverseTransform.invRotateY(angle);
- }
-
-
- /**
- * Rotate in the XY plane by an angle.
- *
- * Note that this doesn't internally set the number of
- * dimensions to three, since rotateZ() is the same as a
- * 2D rotate in the XY plane.
- */
- public void rotateZ(float angle) {
- forwardTransform.rotateZ(angle);
- reverseTransform.invRotateZ(angle);
- }
-
-
- /**
- * Rotate around an arbitrary vector, similar to glRotate(),
- * except that it takes radians (instead of degrees).
- */
- public void rotate(float angle, float v0, float v1, float v2) {
- forwardTransform.rotate(angle, v0, v1, v2);
- reverseTransform.invRotate(angle, v0, v1, v2);
- }
-
-
- /**
- * Same as scale(s, s, s);
- */
- public void scale(float s) {
- scale(s, s, s);
- }
-
-
- /**
- * Not recommended for use in 3D, because the z-dimension is just
- * scaled by 1, since there's no way to know what else to scale it by.
- * Equivalent to scale(sx, sy, 1);
- */
- public void scale(float sx, float sy) {
- scale(sx, sy, 1);
- }
-
-
- /**
- * Scale in three dimensions.
- */
- public void scale(float x, float y, float z) {
- forwardTransform.scale(x, y, z);
- reverseTransform.invScale(x, y, z);
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // TRANSFORMATION MATRIX
-
-
- public void pushMatrix() {
- if (!modelview.push()) {
- throw new RuntimeException("Too many calls to pushMatrix()");
- }
- // Do this to the inverse regardless of the lights
- // to keep stack pointers in sync
- modelviewInv.push();
- }
-
-
- public void popMatrix() {
- if (!modelview.pop()) {
- throw new RuntimeException("Too many calls to popMatrix() " +
- "(and not enough to pushMatrix)");
- }
- // Do this to the inverse regardless of the lights
- // to keep stack pointers in sync
- modelviewInv.pop();
- }
-
-
- /**
- * Load identity as the transform/model matrix.
- * Same as glLoadIdentity().
- */
- public void resetMatrix() {
- forwardTransform.reset();
- reverseTransform.reset();
- }
-
-
- public void applyMatrix(float n00, float n01, float n02,
- float n10, float n11, float n12) {
- throw new RuntimeException("Use applyMatrix() with a 4x4 matrix " +
- "when using OPENGL or P3D");
- }
-
- /**
- * Apply a 4x4 transformation matrix. Same as glMultMatrix().
- * This call will be slow because it will try to calculate the
- * inverse of the transform. So avoid it whenever possible.
- */
- public void applyMatrix(float n00, float n01, float n02, float n03,
- float n10, float n11, float n12, float n13,
- float n20, float n21, float n22, float n23,
- float n30, float n31, float n32, float n33) {
-
- forwardTransform.apply(n00, n01, n02, n03,
- n10, n11, n12, n13,
- n20, n21, n22, n23,
- n30, n31, n32, n33);
-
- reverseTransform.invApply(n00, n01, n02, n03,
- n10, n11, n12, n13,
- n20, n21, n22, n23,
- n30, n31, n32, n33);
- }
-
-
- /**
- * Load the modelview into m00, m01, et al so that it can be used.
- */
- public void loadMatrix() {
- m00 = modelview.m00;
- m01 = modelview.m01;
- m02 = modelview.m02;
- m03 = modelview.m03;
-
- m10 = modelview.m10;
- m11 = modelview.m11;
- m12 = modelview.m12;
- m13 = modelview.m13;
-
- m20 = modelview.m20;
- m21 = modelview.m21;
- m22 = modelview.m22;
- m23 = modelview.m23;
-
- m30 = modelview.m30;
- m31 = modelview.m31;
- m32 = modelview.m32;
- m33 = modelview.m33;
- }
-
-
- /**
- * Print the current model (or "transformation") matrix.
- */
- public void printMatrix() {
- modelview.print();
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // CAMERA and PERSPECTIVE
-
-
- /**
- * Set matrix mode to the camera matrix (instead of the current
- * transformation matrix). This means applyMatrix, resetMatrix, etc.
- * will affect the camera.
- *
- * beginCamera();
- * translate(0, 0, 10);
- * endCamera();
- *
- * at the top of draw(), will result in a runaway camera that shoots
- * infinitely out of the screen over time. In order to prevent this,
- * it is necessary to call some function that does a hard reset of the
- * camera transform matrix inside of begin/endCamera. Two options are
- *
- * camera(); // sets up the nice default camera transform
- * resetMatrix(); // sets up the identity camera transform
- *
- * So to rotate a camera a constant amount, you might try
- *
- * beginCamera();
- * camera();
- * rotateY(PI/8);
- * endCamera();
- *
- */
- public void beginCamera() {
- if (manipulatingCamera) {
- throw new RuntimeException("beginCamera() cannot be called again " +
- "before endCamera()");
- } else {
- manipulatingCamera = true;
- forwardTransform = cameraInv;
- reverseTransform = camera;
- }
- }
-
-
- /**
- * Record the current settings into the camera matrix, and set
- * the matrix mode back to the current transformation matrix.
- *
- * camera(); or
- * camera(ex, ey, ez, cx, cy, cz, ux, uy, uz);
- *
- * do not need to be called from with beginCamera();/endCamera();
- * That's because they always apply to the camera transformation,
- * and they always totally replace it. That means that any coordinate
- * transforms done before camera(); in draw() will be wiped out.
- * It also means that camera() always operates in untransformed world
- * coordinates. Therefore it is always redundant to call resetMatrix();
- * before camera(); This isn't technically true of gluLookat, but it's
- * pretty much how it's used.
- *
- * beginCamera();
- * rotateY(PI/80);
- * endCamera();
- *
- * will result in a camera that spins without stopping. If you want to
- * just rotate a small constant amount, try this:
- *
- * beginCamera();
- * camera(); // sets up the default view
- * rotateY(PI/80);
- * endCamera();
- *
- * That will rotate a little off of the default view. Note that this
- * is entirely equivalent to
- *
- * camera(); // sets up the default view
- * beginCamera();
- * rotateY(PI/80);
- * endCamera();
- *
- * because camera() doesn't care whether or not it's inside a
- * begin/end clause. Basically it's safe to use camera() or
- * camera(ex, ey, ez, cx, cy, cz, ux, uy, uz) as naked calls because
- * they do all the matrix resetting automatically.
- */
- public void camera(float eyeX, float eyeY, float eyeZ,
- float centerX, float centerY, float centerZ,
- float upX, float upY, float upZ) {
- float z0 = eyeX - centerX;
- float z1 = eyeY - centerY;
- float z2 = eyeZ - centerZ;
- float mag = sqrt(z0*z0 + z1*z1 + z2*z2);
-
- if (mag != 0) {
- z0 /= mag;
- z1 /= mag;
- z2 /= mag;
- }
-
- float y0 = upX;
- float y1 = upY;
- float y2 = upZ;
-
- float x0 = y1*z2 - y2*z1;
- float x1 = -y0*z2 + y2*z0;
- float x2 = y0*z1 - y1*z0;
-
- y0 = z1*x2 - z2*x1;
- y1 = -z0*x2 + z2*x0;
- y2 = z0*x1 - z1*x0;
-
- mag = sqrt(x0*x0 + x1*x1 + x2*x2);
- if (mag != 0) {
- x0 /= mag;
- x1 /= mag;
- x2 /= mag;
- }
-
- mag = sqrt(y0*y0 + y1*y1 + y2*y2);
- if (mag != 0) {
- y0 /= mag;
- y1 /= mag;
- y2 /= mag;
- }
-
- // just does an apply to the main matrix,
- // since that'll be copied out on endCamera
- camera.set(x0, x1, x2, 0,
- y0, y1, y2, 0,
- z0, z1, z2, 0,
- 0, 0, 0, 1);
- camera.translate(-eyeX, -eyeY, -eyeZ);
-
- cameraInv.reset();
- cameraInv.invApply(x0, x1, x2, 0,
- y0, y1, y2, 0,
- z0, z1, z2, 0,
- 0, 0, 0, 1);
- cameraInv.invTranslate(-eyeX, -eyeY, -eyeZ);
-
- modelview.set(camera);
- modelviewInv.set(cameraInv);
- }
-
-
- /**
- * Print the current camera matrix.
- */
- public void printCamera() {
- camera.print();
- }
-
-
- /**
- * Calls ortho() with the proper parameters for Processing's
- * standard orthographic projection.
- */
- public void ortho() {
- ortho(0, width, 0, height, -10, 10);
- }
-
-
- /**
- * Similar to gluOrtho(), but wipes out the current projection matrix.
- *
- *
- * Each of these three functions completely replaces the projection
- * matrix with a new one. They can be called inside setup(), and their
- * effects will be felt inside draw(). At the top of draw(), the projection
- * matrix is not reset. Therefore the last projection function to be
- * called always dominates. On resize, the default projection is always
- * established, which has perspective.
- *
- * The Lighting Skinny:
- *
- * The way lighting works is complicated enough that it's worth
- * producing a document to describe it. Lighting calculations proceed
- * pretty much exactly as described in the OpenGL red book.
- *
- * Light-affecting material properties:
- *
- * AMBIENT COLOR
- * - multiplies by light's ambient component
- * - for believability this should match diffuse color
- *
- * DIFFUSE COLOR
- * - multiplies by light's diffuse component
- *
- * SPECULAR COLOR
- * - multiplies by light's specular component
- * - usually less colored than diffuse/ambient
- *
- * SHININESS
- * - the concentration of specular effect
- * - this should be set pretty high (20-50) to see really
- * noticeable specularity
- *
- * EMISSIVE COLOR
- * - constant additive color effect
- *
- * Light types:
- *
- * AMBIENT
- * - one color
- * - no specular color
- * - no direction
- * - may have falloff (constant, linear, and quadratic)
- * - may have position (which matters in non-constant falloff case)
- * - multiplies by a material's ambient reflection
- *
- * DIRECTIONAL
- * - has diffuse color
- * - has specular color
- * - has direction
- * - no position
- * - no falloff
- * - multiplies by a material's diffuse and specular reflections
- *
- * POINT
- * - has diffuse color
- * - has specular color
- * - has position
- * - no direction
- * - may have falloff (constant, linear, and quadratic)
- * - multiplies by a material's diffuse and specular reflections
- *
- * SPOT
- * - has diffuse color
- * - has specular color
- * - has position
- * - has direction
- * - has cone angle (set to half the total cone angle)
- * - has concentration value
- * - may have falloff (constant, linear, and quadratic)
- * - multiplies by a material's diffuse and specular reflections
- *
- * Normal modes:
- *
- * All of the primitives (rect, box, sphere, etc.) have their normals
- * set nicely. During beginShape/endShape normals can be set by the user.
- *
- * AUTO-NORMAL
- * - if no normal is set during the shape, we are in auto-normal mode
- * - auto-normal calculates one normal per triangle (face-normal mode)
- *
- * SHAPE-NORMAL
- * - if one normal is set during the shape, it will be used for
- * all vertices
- *
- * VERTEX-NORMAL
- * - if multiple normals are set, each normal applies to
- * subsequent vertices
- * - (except for the first one, which applies to previous
- * and subsequent vertices)
- *
- * Efficiency consequences:
- *
- * There is a major efficiency consequence of position-dependent
- * lighting calculations per vertex. (See below for determining
- * whether lighting is vertex position-dependent.) If there is no
- * position dependency then the only factors that affect the lighting
- * contribution per vertex are its colors and its normal.
- * There is a major efficiency win if
- *
- * 1) lighting is not position dependent
- * 2) we are in AUTO-NORMAL or SHAPE-NORMAL mode
- *
- * because then we can calculate one lighting contribution per shape
- * (SHAPE-NORMAL) or per triangle (AUTO-NORMAL) and simply multiply it
- * into the vertex colors. The converse is our worst-case performance when
- *
- * 1) lighting is position dependent
- * 2) we are in AUTO-NORMAL mode
- *
- * because then we must calculate lighting per-face * per-vertex.
- * Each vertex has a different lighting contribution per face in
- * which it appears. Yuck.
- *
- * Determining vertex position dependency:
- *
- * If any of the following factors are TRUE then lighting is
- * vertex position dependent:
- *
- * 1) Any lights uses non-constant falloff
- * 2) There are any point or spot lights
- * 3) There is a light with specular color AND there is a
- * material with specular color
- *
- * So worth noting is that default lighting (a no-falloff ambient
- * and a directional without specularity) is not position-dependent.
- * We should capitalize.
- *
- * Simon Greenwold, April 2005
- *
- */
- public void lights() {
- // need to make sure colorMode is RGB 255 here
- int colorModeSaved = colorMode;
- colorMode = RGB;
-
- lightFalloff(1, 0, 0);
- lightSpecular(0, 0, 0);
-
- ambientLight(colorModeX * 0.5f,
- colorModeY * 0.5f,
- colorModeZ * 0.5f);
- directionalLight(colorModeX * 0.5f,
- colorModeY * 0.5f,
- colorModeZ * 0.5f,
- 0, 0, -1);
-
- colorMode = colorModeSaved;
-
- lightingDependsOnVertexPosition = false;
- }
-
-
- /**
- * Add an ambient light based on the current color mode.
- */
- public void ambientLight(float r, float g, float b) {
- ambientLight(r, g, b, 0, 0, 0);
- }
-
-
- /**
- * Add an ambient light based on the current color mode.
- * This version includes an (x, y, z) position for situations
- * where the falloff distance is used.
- */
- public void ambientLight(float r, float g, float b,
- float x, float y, float z) {
- if (lightCount == MAX_LIGHTS) {
- throw new RuntimeException("can only create " + MAX_LIGHTS + " lights");
- }
- colorCalc(r, g, b);
- lightDiffuse[lightCount][0] = calcR;
- lightDiffuse[lightCount][1] = calcG;
- lightDiffuse[lightCount][2] = calcB;
-
- lightType[lightCount] = AMBIENT;
- lightFalloffConstant[lightCount] = currentLightFalloffConstant;
- lightFalloffLinear[lightCount] = currentLightFalloffLinear;
- lightFalloffQuadratic[lightCount] = currentLightFalloffQuadratic;
- lightPosition(lightCount, x, y, z);
- lightCount++;
- //return lightCount-1;
- }
-
-
- public void directionalLight(float r, float g, float b,
- float nx, float ny, float nz) {
- if (lightCount == MAX_LIGHTS) {
- throw new RuntimeException("can only create " + MAX_LIGHTS + " lights");
- }
- colorCalc(r, g, b);
- lightDiffuse[lightCount][0] = calcR;
- lightDiffuse[lightCount][1] = calcG;
- lightDiffuse[lightCount][2] = calcB;
-
- lightType[lightCount] = DIRECTIONAL;
- lightFalloffConstant[lightCount] = currentLightFalloffConstant;
- lightFalloffLinear[lightCount] = currentLightFalloffLinear;
- lightFalloffQuadratic[lightCount] = currentLightFalloffQuadratic;
- lightSpecular[lightCount][0] = currentLightSpecular[0];
- lightSpecular[lightCount][1] = currentLightSpecular[1];
- lightSpecular[lightCount][2] = currentLightSpecular[2];
- lightDirection(lightCount, nx, ny, nz);
- lightCount++;
- }
-
-
- public void pointLight(float r, float g, float b,
- float x, float y, float z) {
- if (lightCount == MAX_LIGHTS) {
- throw new RuntimeException("can only create " + MAX_LIGHTS + " lights");
- }
- colorCalc(r, g, b);
- lightDiffuse[lightCount][0] = calcR;
- lightDiffuse[lightCount][1] = calcG;
- lightDiffuse[lightCount][2] = calcB;
-
- lightType[lightCount] = POINT;
- lightFalloffConstant[lightCount] = currentLightFalloffConstant;
- lightFalloffLinear[lightCount] = currentLightFalloffLinear;
- lightFalloffQuadratic[lightCount] = currentLightFalloffQuadratic;
- lightSpecular[lightCount][0] = currentLightSpecular[0];
- lightSpecular[lightCount][1] = currentLightSpecular[1];
- lightSpecular[lightCount][2] = currentLightSpecular[2];
- lightPosition(lightCount, x, y, z);
- lightCount++;
-
- lightingDependsOnVertexPosition = true;
- }
-
-
- public void spotLight(float r, float g, float b,
- float x, float y, float z,
- float nx, float ny, float nz,
- float angle, float concentration) {
- if (lightCount == MAX_LIGHTS) {
- throw new RuntimeException("can only create " + MAX_LIGHTS + " lights");
- }
- colorCalc(r, g, b);
- lightDiffuse[lightCount][0] = calcR;
- lightDiffuse[lightCount][1] = calcG;
- lightDiffuse[lightCount][2] = calcB;
-
- lightType[lightCount] = SPOT;
- lightFalloffConstant[lightCount] = currentLightFalloffConstant;
- lightFalloffLinear[lightCount] = currentLightFalloffLinear;
- lightFalloffQuadratic[lightCount] = currentLightFalloffQuadratic;
- lightSpecular[lightCount][0] = currentLightSpecular[0];
- lightSpecular[lightCount][1] = currentLightSpecular[1];
- lightSpecular[lightCount][2] = currentLightSpecular[2];
- lightPosition(lightCount, x, y, z);
- lightDirection(lightCount, nx, ny, nz);
- lightSpotAngle[lightCount] = angle;
- lightSpotAngleCos[lightCount] = max(0, cos(angle));
- lightSpotConcentration[lightCount] = concentration;
- lightCount++;
-
- lightingDependsOnVertexPosition = true;
- }
-
-
- /**
- * Set the light falloff rates for the last light that was created.
- * Default is lightFalloff(1, 0, 0).
- */
- public void lightFalloff(float constant, float linear, float quadratic) {
- currentLightFalloffConstant = constant;
- currentLightFalloffLinear = linear;
- currentLightFalloffQuadratic = quadratic;
-
- lightingDependsOnVertexPosition = true;
- }
-
-
- /**
- * Set the specular color of the last light created.
- */
- public void lightSpecular(float x, float y, float z) {
- colorCalc(x, y, z);
- currentLightSpecular[0] = calcR;
- currentLightSpecular[1] = calcG;
- currentLightSpecular[2] = calcB;
-
- lightingDependsOnVertexPosition = true;
- }
-
-
- /**
- * internal function to set the light position
- * based on the current modelview matrix.
- */
- protected void lightPosition(int num, float x, float y, float z) {
- lightPosition[num][0] =
- modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
- lightPosition[num][1] =
- modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
- lightPosition[num][2] =
- modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
- }
-
-
- /**
- * internal function to set the light direction
- * based on the current modelview matrix.
- */
- protected void lightDirection(int num, float x, float y, float z) {
- // Multiply by inverse transpose.
- lightNormal[num][0] =
- modelviewInv.m00*x + modelviewInv.m10*y +
- modelviewInv.m20*z + modelviewInv.m30;
- lightNormal[num][1] =
- modelviewInv.m01*x + modelviewInv.m11*y +
- modelviewInv.m21*z + modelviewInv.m31;
- lightNormal[num][2] =
- modelviewInv.m02*x + modelviewInv.m12*y +
- modelviewInv.m22*z + modelviewInv.m32;
-
- float n = mag(lightNormal[num]);
- if (n == 0 || n == 1) return;
-
- lightNormal[num][0] /= n;
- lightNormal[num][1] /= n;
- lightNormal[num][2] /= n;
- }
-
-
-
- //////////////////////////////////////////////////////////////
-
- // BACKGROUND
-
-
- /**
- * Takes an RGB or RGBA image and sets it as the background.
- *
+ * Simple ear clipping polygon triangulation adapted from code by
+ * John W. Ratcliff (jratcliff at verant.com). Presumably
+ * this
+ * bit of code from the web.
+ */
+ private void triangulate_polygon() {
+ // first we check if the polygon goes clockwise or counterclockwise
+ float area = 0.0f;
+ for (int p = vertex_end - 1, q = vertex_start; q < vertex_end; p = q++) {
+ area += (vertices[q][MX] * vertices[p][MY] -
+ vertices[p][MX] * vertices[q][MY]);
+ //area += (vertices[q][X] * vertices[p][Y] -
+ // vertices[p][X] * vertices[q][Y]);
+ }
+
+ // then sort the vertices so they are always in a counterclockwise order
+ int j = 0;
+ //if (0.0f < area) { // def <
+ if (area > 0) {
+ for (int i = vertex_start; i < vertex_end; i++) {
+ j = i - vertex_start;
+ vertex_order[j] = i;
+ }
+ } else {
+ for (int i = vertex_start; i < vertex_end; i++) {
+ j = i - vertex_start;
+ vertex_order[j] = (vertex_end - 1) - j;
+ }
+ }
+
+ // remove vc-2 Vertices, creating 1 triangle every time
+ int vc = vertex_end - vertex_start;
+ int count = 2*vc; // complex polygon detection
+
+ for (int m = 0, v = vc - 1; vc > 2; ) {
+ boolean snip = true;
+
+ // if we start over again, is a complex polygon
+ if (0 >= (count--)) {
+ break; // triangulation failed
+ }
+
+ // get 3 consecutive vertices
+ int u = v ; if (vc <= u) u = 0; // previous
+ v = u + 1; if (vc <= v) v = 0; // current
+ int w = v + 1; if (vc <= w) w = 0; // next
+
+ // triangle A B C
+ //float Ax, Ay, Bx, By, Cx, Cy, Px, Py;
+
+ float Ax = -vertices[vertex_order[u]][MX];
+ float Ay = vertices[vertex_order[u]][MY];
+ float Bx = -vertices[vertex_order[v]][MX];
+ float By = vertices[vertex_order[v]][MY];
+ float Cx = -vertices[vertex_order[w]][MX];
+ float Cy = vertices[vertex_order[w]][MY];
+
+ // first we check if continues going ccw
+ if (EPSILON > (((Bx-Ax) * (Cy-Ay)) - ((By-Ay) * (Cx-Ax)))) {
+ continue;
+ }
+
+ for (int p = 0; p < vc; p++) {
+ //float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
+ //float cCROSSap, bCROSScp, aCROSSbp;
+
+ if( (p == u) || (p == v) || (p == w) ) {
+ continue;
+ }
+
+ //float Px = -vertices[vertex_order[p]][X];
+ //float Py = vertices[vertex_order[p]][Y];
+ float Px = -vertices[vertex_order[p]][MX];
+ float Py = vertices[vertex_order[p]][MY];
+
+ float ax = Cx - Bx; float ay = Cy - By;
+ float bx = Ax - Cx; float by = Ay - Cy;
+ float cx = Bx - Ax; float cy = By - Ay;
+ float apx = Px - Ax; float apy = Py - Ay;
+ float bpx = Px - Bx; float bpy = Py - By;
+ float cpx = Px - Cx; float cpy = Py - Cy;
+
+ float aCROSSbp = ax * bpy - ay * bpx;
+ float cCROSSap = cx * apy - cy * apx;
+ float bCROSScp = bx * cpy - by * cpx;
+
+ if ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)) {
+ snip = false;
+ }
+ }
+
+ if (snip) {
+ add_triangle(vertex_order[u], vertex_order[v], vertex_order[w]);
+
+ m++;
+
+ // remove v from remaining polygon
+ for (int s = v, t = v + 1; t < vc; s++, t++) {
+ vertex_order[s] = vertex_order[t];
+ }
+ vc--;
+
+ // reset error detection counter
+ count = 2 * vc;
+ }
+ }
+ }
+
+
+ private void toWorldNormal(float nx, float ny, float nz, float[] out) {
+ out[0] =
+ modelviewInv.m00*nx + modelviewInv.m10*ny +
+ modelviewInv.m20*nz + modelviewInv.m30;
+ out[1] =
+ modelviewInv.m01*nx + modelviewInv.m11*ny +
+ modelviewInv.m21*nz + modelviewInv.m31;
+ out[2] =
+ modelviewInv.m02*nx + modelviewInv.m12*ny +
+ modelviewInv.m22*nz + modelviewInv.m32;
+ out[3] =
+ modelviewInv.m03*nx + modelviewInv.m13*ny +
+ modelviewInv.m23*nz + modelviewInv.m33;
+
+ if (out[3] != 0 && out[3] != ONE) {
+ // divide by perspective coordinate
+ out[0] /= out[3]; out[1] /= out[3]; out[2] /= out[3];
+ }
+ out[3] = 1;
+
+ float nlen = mag(out[0], out[1], out[2]); // normalize
+ if (nlen != 0 && nlen != ONE) {
+ out[0] /= nlen; out[1] /= nlen; out[2] /= nlen;
+ }
+ }
+
+
+ private void calc_lighting_contribution(int vIndex,
+ float[] contribution) {
+ calc_lighting_contribution(vIndex, contribution, false);
+ }
+
+ private void calc_lighting_contribution(int vIndex,
+ float[] contribution,
+ boolean normalIsWorld) {
+ float[] v = vertices[vIndex];
+
+ float sr = v[SPR];
+ float sg = v[SPG];
+ float sb = v[SPB];
+
+ float wx = v[VX];
+ float wy = v[VY];
+ float wz = v[VZ];
+ float shine = v[SHINE];
+
+ float nx;
+ float ny;
+ float nz;
+ if (!normalIsWorld) {
+ toWorldNormal(v[NX], v[NY], v[NZ], worldNormal);
+ nx = worldNormal[X];
+ ny = worldNormal[Y];
+ nz = worldNormal[Z];
+ }
+ else {
+ nx = v[NX];
+ ny = v[NY];
+ nz = v[NZ];
+ }
+
+
+ // Since the camera space == world space,
+ // we can test for visibility by the dot product of
+ // the normal with the direction from pt. to eye.
+ float dir = dot(nx, ny, nz, -wx, -wy, -wz);
+ // If normal is away from camera, choose its opposite.
+ // If we add backface culling, this will be backfacing
+ // (but since this is per vertex, it's more complicated)
+ if (dir < 0) {
+ nx = -nx;
+ ny = -ny;
+ nz = -nz;
+ }
+
+ // These two terms will sum the contributions from the various lights
+ contribution[LIGHT_AMBIENT_R] = 0;
+ contribution[LIGHT_AMBIENT_G] = 0;
+ contribution[LIGHT_AMBIENT_B] = 0;
+
+ contribution[LIGHT_DIFFUSE_R] = 0;
+ contribution[LIGHT_DIFFUSE_G] = 0;
+ contribution[LIGHT_DIFFUSE_B] = 0;
+
+ contribution[LIGHT_SPECULAR_R] = 0;
+ contribution[LIGHT_SPECULAR_G] = 0;
+ contribution[LIGHT_SPECULAR_B] = 0;
+
+ // for (int i = 0; i < MAX_LIGHTS; i++) {
+ // if (!light[i]) continue;
+ for (int i = 0; i < lightCount; i++) {
+
+ float denom = lightFalloffConstant[i];
+ float spotTerm = 1;
+
+ if (lightType[i] == AMBIENT) {
+ if (lightFalloffQuadratic[i] != 0 || lightFalloffLinear[i] != 0) {
+ // Falloff depends on distance
+ float distSq = mag(lightPosition[i][0] - wx,
+ lightPosition[i][1] - wy,
+ lightPosition[i][2] - wz);
+ denom +=
+ lightFalloffQuadratic[i] * distSq +
+ lightFalloffLinear[i] * (float) sqrt(distSq);
+ }
+ if (denom == 0) denom = 1;
+
+ contribution[LIGHT_AMBIENT_R] += lightDiffuse[i][0] / denom;
+ contribution[LIGHT_AMBIENT_G] += lightDiffuse[i][1] / denom;
+ contribution[LIGHT_AMBIENT_B] += lightDiffuse[i][2] / denom;
+
+ } else {
+ // If not ambient, we must deal with direction
+
+ // li is the vector from the vertex to the light
+ float lix, liy, liz;
+ float lightDir_dot_li = 0;
+ float n_dot_li = 0;
+
+ if (lightType[i] == DIRECTIONAL) {
+ lix = -lightNormal[i][0];
+ liy = -lightNormal[i][1];
+ liz = -lightNormal[i][2];
+ denom = 1;
+ n_dot_li = (nx * lix + ny * liy + nz * liz);
+ // If light is lighting the face away from the camera, ditch
+ if (n_dot_li <= 0) {
+ continue;
+ }
+ } else { // Point or spot light (must deal also with light location)
+ lix = lightPosition[i][0] - wx;
+ liy = lightPosition[i][1] - wy;
+ liz = lightPosition[i][2] - wz;
+ // normalize
+ float distSq = mag(lix, liy, liz);
+ if (distSq != 0) {
+ lix /= distSq;
+ liy /= distSq;
+ liz /= distSq;
+ }
+ n_dot_li = (nx * lix + ny * liy + nz * liz);
+ // If light is lighting the face away from the camera, ditch
+ if (n_dot_li <= 0) {
+ continue;
+ }
+
+ if (lightType[i] == SPOT) { // Must deal with spot cone
+ lightDir_dot_li =
+ -(lightNormal[i][0] * lix +
+ lightNormal[i][1] * liy +
+ lightNormal[i][2] * liz);
+ // Outside of spot cone
+ if (lightDir_dot_li <= lightSpotAngleCos[i]) {
+ continue;
+ }
+ spotTerm = pow(lightDir_dot_li, lightSpotConcentration[i]);
+ }
+
+ if (lightFalloffQuadratic[i] != 0 || lightFalloffLinear[i] != 0) {
+ // Falloff depends on distance
+ denom +=
+ lightFalloffQuadratic[i] * distSq +
+ lightFalloffLinear[i] * (float) sqrt(distSq);
+ }
+ }
+ // Directional, point, or spot light:
+
+ // We know n_dot_li > 0 from above "continues"
+
+ if (denom == 0)
+ denom = 1;
+ float mul = n_dot_li * spotTerm / denom;
+ contribution[LIGHT_DIFFUSE_R] += lightDiffuse[i][0] * mul;
+ contribution[LIGHT_DIFFUSE_G] += lightDiffuse[i][1] * mul;
+ contribution[LIGHT_DIFFUSE_B] += lightDiffuse[i][2] * mul;
+
+ // SPECULAR
+
+ // If the material and light have a specular component.
+ if ((sr > 0 || sg > 0 || sb > 0) &&
+ (lightSpecular[i][0] > 0 ||
+ lightSpecular[i][1] > 0 ||
+ lightSpecular[i][2] > 0)) {
+
+ float vmag = mag(wx, wy, wz);
+ if (vmag != 0) {
+ wx /= vmag;
+ wy /= vmag;
+ wz /= vmag;
+ }
+ float sx = lix - wx;
+ float sy = liy - wy;
+ float sz = liz - wz;
+ vmag = mag(sx, sy, sz);
+ if (vmag != 0) {
+ sx /= vmag;
+ sy /= vmag;
+ sz /= vmag;
+ }
+ float s_dot_n = (sx * nx + sy * ny + sz * nz);
+
+ if (s_dot_n > 0) {
+ s_dot_n = pow(s_dot_n, shine);
+ mul = s_dot_n * spotTerm / denom;
+ contribution[LIGHT_SPECULAR_R] += lightSpecular[i][0] * mul;
+ contribution[LIGHT_SPECULAR_G] += lightSpecular[i][1] * mul;
+ contribution[LIGHT_SPECULAR_B] += lightSpecular[i][2] * mul;
+ }
+
+ }
+ }
+ }
+ /*target[toffset + 0] = min(1, er + dr * diffuse_r);
+ target[toffset + 1] = min(1, eg + dg * diffuse_g);
+ target[toffset + 2] = min(1, eb + db * diffuse_b);
+
+ target[SPR] = min(1, sr * specular_r);
+ target[SPG] = min(1, sg * specular_g);
+ target[SPB] = min(1, sb * specular_b);*/
+ return;
+ }
+
+
+ // Multiply the lighting contribution into the vertex's colors.
+ // Only do this when there is ONE lighting per vertex
+ // (MANUAL_VERTEX_NORMAL or SHAPE_NORMAL mode).
+ private void apply_lighting_contribution(int vIndex, float[] contribution) {
+ float[] v = vertices[vIndex];
+
+ v[R] = min(1, v[ER] + v[AR] * contribution[LIGHT_AMBIENT_R] +
+ v[DR] * contribution[LIGHT_DIFFUSE_R]);
+ v[G] = min(1, v[EG] + v[AG] * contribution[LIGHT_AMBIENT_G] +
+ v[DG] * contribution[LIGHT_DIFFUSE_G]);
+ v[B] = min(1, v[EB] + v[AB] * contribution[LIGHT_AMBIENT_R] +
+ v[DB] * contribution[LIGHT_DIFFUSE_B]);
+ v[A] = min(1, v[DA]);
+
+ v[SPR] = min(1, v[SPR] * contribution[LIGHT_SPECULAR_R]);
+ v[SPG] = min(1, v[SPG] * contribution[LIGHT_SPECULAR_G]);
+ v[SPB] = min(1, v[SPB] * contribution[LIGHT_SPECULAR_B]);
+ v[SPA] = min(1, v[SPA]);
+
+ v[BEEN_LIT] = 1;
+ }
+
+
+ private void light_vertex_always(int vIndex, float[] contribution) {
+ calc_lighting_contribution(vIndex, contribution);
+ apply_lighting_contribution(vIndex, contribution);
+ }
+
+
+ private void light_vertex_if_not_already_lit(int vIndex,
+ float[] contribution) {
+ if (vertices[vIndex][BEEN_LIT] == 0) {
+ light_vertex_always(vIndex, contribution);
+ }
+ }
+
+
+ private void copy_prelit_vertex_color_to_triangle(int triIndex, int vIndex,
+ int colorIndex) {
+ float[] triColor = triangleColors[triIndex][colorIndex];
+ float[] v = vertices[vIndex];
+
+ triColor[TRI_DIFFUSE_R] = v[R];
+ triColor[TRI_DIFFUSE_G] = v[G];
+ triColor[TRI_DIFFUSE_B] = v[B];
+ triColor[TRI_DIFFUSE_A] = v[A];
+ triColor[TRI_SPECULAR_R] = v[SPR];
+ triColor[TRI_SPECULAR_G] = v[SPG];
+ triColor[TRI_SPECULAR_B] = v[SPB];
+ triColor[TRI_SPECULAR_A] = v[SPA];
+ }
+
+
+ private void copy_vertex_color_to_triangle(int triIndex,
+ int vIndex, int colorIndex,
+ float[] lightContribution) {
+ float[] triColor = triangleColors[triIndex][colorIndex];
+ float[] v = vertices[vIndex];
+
+ triColor[TRI_DIFFUSE_R] =
+ min(1, v[ER] + v[AR] * lightContribution[LIGHT_AMBIENT_R] +
+ v[DR] * lightContribution[LIGHT_DIFFUSE_R]);
+ triColor[TRI_DIFFUSE_G] =
+ min(1, v[EG] + v[AG] * lightContribution[LIGHT_AMBIENT_G] +
+ v[DG] * lightContribution[LIGHT_DIFFUSE_G]);
+ triColor[TRI_DIFFUSE_B] =
+ min(1, v[EB] + v[AB] * lightContribution[LIGHT_AMBIENT_R] +
+ v[DB] * lightContribution[LIGHT_DIFFUSE_B]);
+ triColor[TRI_DIFFUSE_A] = min(1, v[DA]);
+
+ triColor[TRI_SPECULAR_R] =
+ min(1, v[SPR] * lightContribution[LIGHT_SPECULAR_R]);
+ triColor[TRI_SPECULAR_G] =
+ min(1, v[SPG] * lightContribution[LIGHT_SPECULAR_G]);
+ triColor[TRI_SPECULAR_B] =
+ min(1, v[SPB] * lightContribution[LIGHT_SPECULAR_B]);
+ triColor[TRI_SPECULAR_A] = min(1, v[SPA]);
+ }
+
+
+ private void light_triangle(int triIndex, float[] lightContribution) {
+ int vIndex = triangles[triIndex][VERTEX1];
+ copy_vertex_color_to_triangle(triIndex, vIndex, 0, lightContribution);
+ vIndex = triangles[triIndex][VERTEX2];
+ copy_vertex_color_to_triangle(triIndex, vIndex, 1, lightContribution);
+ vIndex = triangles[triIndex][VERTEX3];
+ copy_vertex_color_to_triangle(triIndex, vIndex, 2, lightContribution);
+ }
+
+
+ private void crossProduct(float[] u, float[] v, float[] out) {
+ out[0] = u[1]*v[2] - u[2]*v[1];
+ out[1] = u[2]*v[0] - u[0]*v[2];
+ out[2] = u[0]*v[1] - u[1]*v[0];
+ }
+
+
+ private void light_triangle(int triIndex) {
+ int vIndex;
+
+ // Handle lighting on, but no lights (in this case, just use emissive)
+ // This wont be used currently because lightCount == 0 is don't use
+ // lighting at all... So. OK. If that ever changes, use the below:
+ /*
+ if (lightCount == 0) {
+ vIndex = triangles[triIndex][VERTEX1];
+ copy_emissive_vertex_color_to_triangle(triIndex, vIndex, 0);
+ vIndex = triangles[triIndex][VERTEX2];
+ copy_emissive_vertex_color_to_triangle(triIndex, vIndex, 1);
+ vIndex = triangles[triIndex][VERTEX3];
+ copy_emissive_vertex_color_to_triangle(triIndex, vIndex, 2);
+ return;
+ }
+ */
+
+ // In MANUAL_VERTEX_NORMAL mode, we have a specific normal
+ // for each vertex. In that case, we light any verts that
+ // haven't already been lit and copy their colors straight
+ // into the triangle.
+ if (normalMode == MANUAL_VERTEX_NORMAL) {
+ vIndex = triangles[triIndex][VERTEX1];
+ light_vertex_if_not_already_lit(vIndex, tempLightingContribution);
+ copy_prelit_vertex_color_to_triangle(triIndex, vIndex, 0);
+
+ vIndex = triangles[triIndex][VERTEX2];
+ light_vertex_if_not_already_lit(vIndex, tempLightingContribution);
+ copy_prelit_vertex_color_to_triangle(triIndex, vIndex, 1);
+
+ vIndex = triangles[triIndex][VERTEX3];
+ light_vertex_if_not_already_lit(vIndex, tempLightingContribution);
+ copy_prelit_vertex_color_to_triangle(triIndex, vIndex, 2);
+
+ }
+
+ // If the lighting doesn't depend on the vertex position, do the
+ // following: We've already dealt with MANUAL_SHAPE_NORMAL mode before
+ // we got into this function, so here we only have to deal with
+ // AUTO_NORMAL mode. So we calculate the normal for this triangle,
+ // and use that for the lighting.
+ else if (!lightingDependsOnVertexPosition) {
+ vIndex = triangles[triIndex][VERTEX1];
+ int vIndex2 = triangles[triIndex][VERTEX2];
+ int vIndex3 = triangles[triIndex][VERTEX3];
+
+ /*
+ float[] dv1 = new float[] {vertices[vIndex2][VX] - vertices[vIndex][VX],
+ vertices[vIndex2][VY] - vertices[vIndex][VY],
+ vertices[vIndex2][VZ] - vertices[vIndex][VZ]};
+ float[] dv2 = new float[] {vertices[vIndex3][VX] - vertices[vIndex][VX],
+ vertices[vIndex3][VY] - vertices[vIndex][VY],
+ vertices[vIndex3][VZ] - vertices[vIndex][VZ]};
+ */
+ dv1[0] = vertices[vIndex2][VX] - vertices[vIndex][VX];
+ dv1[1] = vertices[vIndex2][VY] - vertices[vIndex][VY];
+ dv1[2] = vertices[vIndex2][VZ] - vertices[vIndex][VZ];
+
+ dv2[0] = vertices[vIndex3][VX] - vertices[vIndex][VX];
+ dv2[1] = vertices[vIndex3][VY] - vertices[vIndex][VY];
+ dv2[2] = vertices[vIndex3][VZ] - vertices[vIndex][VZ];
+
+ //float[] norm = new float[3];
+ crossProduct(dv1, dv2, norm);
+ float nMag = mag(norm[X], norm[Y], norm[Z]);
+ if (nMag != 0 && nMag != ONE) {
+ norm[X] /= nMag; norm[Y] /= nMag; norm[Z] /= nMag;
+ }
+ vertices[vIndex][NX] = norm[X];
+ vertices[vIndex][NY] = norm[Y];
+ vertices[vIndex][NZ] = norm[Z];
+
+ // The true at the end says the normal is already in world coordinates
+ calc_lighting_contribution(vIndex, tempLightingContribution, true);
+ copy_vertex_color_to_triangle(triIndex, vIndex, 0,
+ tempLightingContribution);
+ copy_vertex_color_to_triangle(triIndex, vIndex2, 1,
+ tempLightingContribution);
+ copy_vertex_color_to_triangle(triIndex, vIndex3, 2,
+ tempLightingContribution);
+ }
+
+ // If lighting is position-dependent
+ else {
+ if (normalMode == MANUAL_SHAPE_NORMAL) {
+ vIndex = triangles[triIndex][VERTEX1];
+ vertices[vIndex][NX] = vertices[vertex_start][NX];
+ vertices[vIndex][NY] = vertices[vertex_start][NY];
+ vertices[vIndex][NZ] = vertices[vertex_start][NZ];
+ calc_lighting_contribution(vIndex, tempLightingContribution);
+ copy_vertex_color_to_triangle(triIndex, vIndex, 0,
+ tempLightingContribution);
+
+ vIndex = triangles[triIndex][VERTEX2];
+ vertices[vIndex][NX] = vertices[vertex_start][NX];
+ vertices[vIndex][NY] = vertices[vertex_start][NY];
+ vertices[vIndex][NZ] = vertices[vertex_start][NZ];
+ calc_lighting_contribution(vIndex, tempLightingContribution);
+ copy_vertex_color_to_triangle(triIndex, vIndex, 1,
+ tempLightingContribution);
+
+ vIndex = triangles[triIndex][VERTEX3];
+ vertices[vIndex][NX] = vertices[vertex_start][NX];
+ vertices[vIndex][NY] = vertices[vertex_start][NY];
+ vertices[vIndex][NZ] = vertices[vertex_start][NZ];
+ calc_lighting_contribution(vIndex, tempLightingContribution);
+ copy_vertex_color_to_triangle(triIndex, vIndex, 2,
+ tempLightingContribution);
+ }
+
+ // lighting mode is AUTO_NORMAL
+ else {
+ vIndex = triangles[triIndex][VERTEX1];
+ int vIndex2 = triangles[triIndex][VERTEX2];
+ int vIndex3 = triangles[triIndex][VERTEX3];
+ /*
+ float[] dv1 = new float[] {vertices[vIndex2][VX] - vertices[vIndex][VX],
+ vertices[vIndex2][VY] - vertices[vIndex][VY],
+ vertices[vIndex2][VZ] - vertices[vIndex][VZ]};
+ float[] dv2 = new float[] {vertices[vIndex3][VX] - vertices[vIndex][VX],
+ vertices[vIndex3][VY] - vertices[vIndex][VY],
+ vertices[vIndex3][VZ] - vertices[vIndex][VZ]};
+ */
+ dv1[0] = vertices[vIndex2][VX] - vertices[vIndex][VX];
+ dv1[1] = vertices[vIndex2][VY] - vertices[vIndex][VY];
+ dv1[2] = vertices[vIndex2][VZ] - vertices[vIndex][VZ];
+ dv2[0] = vertices[vIndex3][VX] - vertices[vIndex][VX];
+ dv2[1] = vertices[vIndex3][VY] - vertices[vIndex][VY];
+ dv2[2] = vertices[vIndex3][VZ] - vertices[vIndex][VZ];
+
+ //float[] norm = new float[3];
+ crossProduct(dv1, dv2, norm);
+ float nMag = mag(norm[X], norm[Y], norm[Z]);
+ if (nMag != 0 && nMag != ONE) {
+ norm[X] /= nMag; norm[Y] /= nMag; norm[Z] /= nMag;
+ }
+ vertices[vIndex][NX] = norm[X];
+ vertices[vIndex][NY] = norm[Y];
+ vertices[vIndex][NZ] = norm[Z];
+ // The true at the end says the normal is already in world coordinates
+ calc_lighting_contribution(vIndex, tempLightingContribution, true);
+ copy_vertex_color_to_triangle(triIndex, vIndex, 0,
+ tempLightingContribution);
+
+ vertices[vIndex2][NX] = norm[X];
+ vertices[vIndex2][NY] = norm[Y];
+ vertices[vIndex2][NZ] = norm[Z];
+ // The true at the end says the normal is already in world coordinates
+ calc_lighting_contribution(vIndex2, tempLightingContribution, true);
+ copy_vertex_color_to_triangle(triIndex, vIndex2, 1,
+ tempLightingContribution);
+
+ vertices[vIndex3][NX] = norm[X];
+ vertices[vIndex3][NY] = norm[Y];
+ vertices[vIndex3][NZ] = norm[Z];
+ // The true at the end says the normal is already in world coordinates
+ calc_lighting_contribution(vIndex3, tempLightingContribution, true);
+ copy_vertex_color_to_triangle(triIndex, vIndex3, 2,
+ tempLightingContribution);
+ }
+ }
+ }
+
+
+ protected void handle_lighting() {
+
+ // If the lighting does not depend on vertex position and there is a single
+ // normal specified for this shape, go ahead and apply the same lighting
+ // contribution to every vertex in this shape (one lighting calc!)
+ if (!lightingDependsOnVertexPosition && normalMode == MANUAL_SHAPE_NORMAL) {
+ calc_lighting_contribution(vertex_start, tempLightingContribution);
+ for (int tri = 0; tri < triangleCount; tri++) {
+ light_triangle(tri, tempLightingContribution);
+ }
+ }
+ // Otherwise light each triangle individually...
+ else {
+ for (int tri = 0; tri < triangleCount; tri++) {
+ light_triangle(tri);
+ }
+ }
+ }
+
+
+ protected void handle_no_lighting() {
+ int vIndex;
+ for (int tri = 0; tri < triangleCount; tri++) {
+ vIndex = triangles[tri][VERTEX1];
+ copy_prelit_vertex_color_to_triangle(tri, vIndex, 0);
+ vIndex = triangles[tri][VERTEX2];
+ copy_prelit_vertex_color_to_triangle(tri, vIndex, 1);
+ vIndex = triangles[tri][VERTEX3];
+ copy_prelit_vertex_color_to_triangle(tri, vIndex, 2);
+ }
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // BASIC SHAPES
+
+
+ public void point(float x, float y) {
+ point(x, y, 0);
+ }
+
+
+ public void point(float x, float y, float z) {
+ /*
+ beginShape(POINTS);
+ vertex(x, y, z);
+ endShape();
+ */
+
+ // hacked workaround for carlos line bug
+ beginShape(LINES);
+ vertex(x, y, z);
+ vertex(x + EPSILON, y + EPSILON, z);
+ endShape();
+ }
+
+ /*
+ private void point3(float x, float y, float z, int color) {
+ // need to get scaled version of the stroke
+ float x1 = screenX(x - 0.5f, y - 0.5f, z);
+ float y1 = screenY(x - 0.5f, y - 0.5f, z);
+ float x2 = screenX(x + 0.5f, y + 0.5f, z);
+ float y2 = screenY(x + 0.5f, y + 0.5f, z);
+
+ float weight = (abs(x2 - x1) + abs(y2 - y1)) / 2f;
+ if (weight < 1.5f) {
+ int xx = (int) ((x1 + x2) / 2f);
+ int yy = (int) ((y1 + y2) / 2f);
+ //point0(xx, yy, z, color);
+ zbuffer[yy*width + xx] = screenZ(x, y, z);
+ //stencil?
+
+ } else {
+ // actually has some weight, need to draw shapes instead
+ // these will be
+ }
+ }
+ */
+
+
+ public void line(float x1, float y1, float x2, float y2) {
+ line(x1, y1, 0, x2, y2, 0);
+ }
+
+
+ public void line(float x1, float y1, float z1,
+ float x2, float y2, float z2) {
+ beginShape(LINES);
+ vertex(x1, y1, z1);
+ vertex(x2, y2, z2);
+ endShape();
+ }
+
+
+ public void triangle(float x1, float y1, float x2, float y2,
+ float x3, float y3) {
+ beginShape(TRIANGLES);
+ normal(0, 0, 1);
+ vertex(x1, y1);
+ vertex(x2, y2);
+ vertex(x3, y3);
+ endShape();
+ }
+
+
+ public void quad(float x1, float y1, float x2, float y2,
+ float x3, float y3, float x4, float y4) {
+ beginShape(QUADS);
+ normal(0, 0, 1);
+ vertex(x1, y1);
+ vertex(x2, y2);
+ vertex(x3, y3);
+ vertex(x4, y4);
+ endShape();
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // PLACED SHAPES
+
+
+ protected void rectImpl(float x1, float y1, float x2, float y2) {
+ quad(x1, y1, x2, y1, x2, y2, x1, y2);
+ }
+
+
+ protected void ellipseImpl(float x1, float y1, float w, float h) {
+ float hradius = w / 2f;
+ float vradius = h / 2f;
+
+ float centerX = x1 + hradius;
+ float centerY = y1 + vradius;
+
+ // adapt accuracy to radii used w/ a minimum of 4 segments [toxi]
+ // now uses current scale factors to determine "real" transformed radius
+
+ //int cAccuracy = (int)(4+Math.sqrt(hradius*abs(m00)+vradius*abs(m11))*2);
+ //int cAccuracy = (int)(4+Math.sqrt(hradius+vradius)*2);
+
+ // notched this up to *3 instead of *2 because things were
+ // looking a little rough, i.e. the calculate->arctangent example [fry]
+
+ // also removed the m00 and m11 because those were causing weirdness
+ // need an actual measure of magnitude in there [fry]
+
+ int cAccuracy = (int)(4+Math.sqrt(hradius+vradius)*3);
+
+ // [toxi031031] adapted to use new lookup tables
+ float inc = (float)SINCOS_LENGTH / cAccuracy;
+
+ float val = 0;
+ /*
+ beginShape(POLYGON);
+ for (int i = 0; i < cAccuracy; i++) {
+ vertex(centerX + cosLUT[(int) val] * hradius,
+ centerY + sinLUT[(int) val] * vradius);
+ val += inc;
+ }
+ endShape();
+ */
+
+ if (fill) {
+ boolean savedStroke = stroke;
+ stroke = false;
+
+ beginShape(TRIANGLE_FAN);
+ normal(0, 0, 1);
+ vertex(centerX, centerY);
+ for (int i = 0; i < cAccuracy; i++) {
+ vertex(centerX + cosLUT[(int) val] * hradius,
+ centerY + sinLUT[(int) val] * vradius);
+ val += inc;
+ }
+ // back to the beginning
+ vertex(centerX + cosLUT[0] * hradius,
+ centerY + sinLUT[0] * vradius);
+ endShape();
+
+ stroke = savedStroke;
+ }
+
+ if (stroke) {
+ boolean savedFill = fill;
+ fill = false;
+
+ val = 0;
+ beginShape(LINE_LOOP);
+ for (int i = 0; i < cAccuracy; i++) {
+ vertex(centerX + cosLUT[(int) val] * hradius,
+ centerY + sinLUT[(int) val] * vradius);
+ val += inc;
+ }
+ endShape();
+
+ fill = savedFill;
+ }
+ }
+
+
+ /**
+ * Start and stop are in radians, converted by the parent function.
+ * Note that the radians can be greater (or less) than TWO_PI.
+ * This is so that an arc can be drawn that crosses zero mark,
+ * and the user will still collect $200.
+ */
+ protected void arcImpl(float x1, float y1, float w, float h,
+ float start, float stop) {
+ float hr = w / 2f;
+ float vr = h / 2f;
+
+ float centerX = x1 + hr;
+ float centerY = y1 + vr;
+
+ if (fill) {
+ // shut off stroke for a minute
+ boolean savedStroke = stroke;
+ stroke = false;
+
+ int startLUT = (int) (0.5f + (start / TWO_PI) * SINCOS_LENGTH);
+ int stopLUT = (int) (0.5f + (stop / TWO_PI) * SINCOS_LENGTH);
+
+ beginShape(TRIANGLE_FAN);
+ vertex(centerX, centerY);
+ int increment = 1; // what's a good algorithm? stopLUT - startLUT;
+ for (int i = startLUT; i < stopLUT; i += increment) {
+ int ii = i % SINCOS_LENGTH;
+ vertex(centerX + cosLUT[ii] * hr,
+ centerY + sinLUT[ii] * vr);
+ }
+ // draw last point explicitly for accuracy
+ vertex(centerX + cosLUT[stopLUT % SINCOS_LENGTH] * hr,
+ centerY + sinLUT[stopLUT % SINCOS_LENGTH] * vr);
+ endShape();
+
+ stroke = savedStroke;
+ }
+
+ if (stroke) {
+ // Almost identical to above, but this uses a LINE_STRIP
+ // and doesn't include the first (center) vertex.
+
+ boolean savedFill = fill;
+ fill = false;
+
+ int startLUT = (int) (0.5f + (start / TWO_PI) * SINCOS_LENGTH);
+ int stopLUT = (int) (0.5f + (stop / TWO_PI) * SINCOS_LENGTH);
+
+ beginShape(LINE_STRIP);
+ int increment = 1; // what's a good algorithm? stopLUT - startLUT;
+ for (int i = startLUT; i < stopLUT; i += increment) {
+ int ii = i % SINCOS_LENGTH;
+ vertex(centerX + cosLUT[ii] * hr,
+ centerY + sinLUT[ii] * vr);
+ }
+ // draw last point explicitly for accuracy
+ vertex(centerX + cosLUT[stopLUT % SINCOS_LENGTH] * hr,
+ centerY + sinLUT[stopLUT % SINCOS_LENGTH] * vr);
+ endShape();
+
+ fill = savedFill;
+ }
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // 3D BOX
+
+
+ public void box(float size) {
+ box(size, size, size);
+ }
+
+
+ // OPT this isn't the least bit efficient
+ // because it redraws lines along the vertices
+ // ugly ugly ugly!
+ public void box(float w, float h, float d) {
+ float x1 = -w/2f; float x2 = w/2f;
+ float y1 = -h/2f; float y2 = h/2f;
+ float z1 = -d/2f; float z2 = d/2f;
+
+ if (triangle != null) { // triangle is null in gl
+ triangle.setCulling(true);
+ }
+
+ beginShape(QUADS);
+
+ // front
+ normal(0, 0, 1);
+ vertex(x1, y1, z1);
+ vertex(x2, y1, z1);
+ vertex(x2, y2, z1);
+ vertex(x1, y2, z1);
+
+ // right
+ normal(1, 0, 0);
+ vertex(x2, y1, z1);
+ vertex(x2, y1, z2);
+ vertex(x2, y2, z2);
+ vertex(x2, y2, z1);
+
+ // back
+ normal(0, 0, -1);
+ vertex(x2, y1, z2);
+ vertex(x1, y1, z2);
+ vertex(x1, y2, z2);
+ vertex(x2, y2, z2);
+
+ // left
+ normal(-1, 0, 0);
+ vertex(x1, y1, z2);
+ vertex(x1, y1, z1);
+ vertex(x1, y2, z1);
+ vertex(x1, y2, z2);
+
+ // top
+ normal(0, 1, 0);
+ vertex(x1, y1, z2);
+ vertex(x2, y1, z2);
+ vertex(x2, y1, z1);
+ vertex(x1, y1, z1);
+
+ // bottom
+ normal(0, -1, 0);
+ vertex(x1, y2, z1);
+ vertex(x2, y2, z1);
+ vertex(x2, y2, z2);
+ vertex(x1, y2, z2);
+
+ endShape();
+
+ if (triangle != null) { // triangle is null in gl
+ triangle.setCulling(false);
+ }
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // 3D SPHERE
+
+
+ // [toxi031031] used by the new sphere code below
+ // precompute vertices along unit sphere with new detail setting
+
+ public void sphereDetail(int res) {
+ if (res < 3) res = 3; // force a minimum res
+ if (res == sphereDetail) return;
+
+ float delta = (float)SINCOS_LENGTH/res;
+ float[] cx = new float[res];
+ float[] cz = new float[res];
+ // calc unit circle in XZ plane
+ for (int i = 0; i < res; i++) {
+ cx[i] = cosLUT[(int) (i*delta) % SINCOS_LENGTH];
+ cz[i] = sinLUT[(int) (i*delta) % SINCOS_LENGTH];
+ }
+ // computing vertexlist
+ // vertexlist starts at south pole
+ int vertCount = res * (res-1) + 2;
+ int currVert = 0;
+
+ // re-init arrays to store vertices
+ sphereX = new float[vertCount];
+ sphereY = new float[vertCount];
+ sphereZ = new float[vertCount];
+
+ float angle_step = (SINCOS_LENGTH*0.5f)/res;
+ float angle = angle_step;
+
+ // step along Y axis
+ for (int i = 1; i < res; i++) {
+ float curradius = sinLUT[(int) angle % SINCOS_LENGTH];
+ float currY = -cosLUT[(int) angle % SINCOS_LENGTH];
+ for (int j = 0; j < res; j++) {
+ sphereX[currVert] = cx[j] * curradius;
+ sphereY[currVert] = currY;
+ sphereZ[currVert++] = cz[j] * curradius;
+ }
+ angle += angle_step;
+ }
+ sphereDetail = res;
+ }
+
+
+ /**
+ * Draw a sphere with radius r centered at coordinate 0, 0, 0.
+ *
+ * [toxi031031] new sphere code. removed all multiplies with
+ * radius, as scale() will take care of that anyway
+ *
+ * [toxi031223] updated sphere code (removed modulos)
+ * and introduced sphereAt(x,y,z,r)
+ * to avoid additional translate()'s on the user/sketch side
+ *
+ */
+ public void sphere(float r) {
+ float x = 0; // TODO clean this back up again
+ float y = 0;
+ float z = 0;
+
+ if (sphereDetail == 0) {
+ sphereDetail(30);
+ }
+
+ int v1,v11,v2;
+ pushMatrix();
+ if (x!=0f && y!=0f && z!=0f) translate(x,y,z);
+ scale(r);
+
+ if (triangle != null) { // triangle is null in gl
+ triangle.setCulling(true);
+ }
+
+ // 1st ring from south pole
+ beginShape(TRIANGLE_STRIP);
+ for (int i = 0; i < sphereDetail; i++) {
+ normal(0, -1, 0);
+ vertex(0, -1, 0);
+ normal(sphereX[i], sphereY[i], sphereZ[i]);
+ vertex(sphereX[i], sphereY[i], sphereZ[i]);
+ }
+ //normal(0, -1, 0);
+ vertex(0, -1, 0);
+ normal(sphereX[0], sphereY[0], sphereZ[0]);
+ vertex(sphereX[0], sphereY[0], sphereZ[0]);
+ endShape();
+
+ // middle rings
+ int voff = 0;
+ for(int i = 2; i < sphereDetail; i++) {
+ v1=v11=voff;
+ voff += sphereDetail;
+ v2=voff;
+ beginShape(TRIANGLE_STRIP);
+ for (int j = 0; j < sphereDetail; j++) {
+ normal(sphereX[v1], sphereY[v1], sphereZ[v1]);
+ vertex(sphereX[v1], sphereY[v1], sphereZ[v1++]);
+ normal(sphereX[v2], sphereY[v2], sphereZ[v2]);
+ vertex(sphereX[v2], sphereY[v2], sphereZ[v2++]);
+ }
+ // close each ring
+ v1=v11;
+ v2=voff;
+ normal(sphereX[v1], sphereY[v1], sphereZ[v1]);
+ vertex(sphereX[v1], sphereY[v1], sphereZ[v1]);
+ normal(sphereX[v2], sphereY[v2], sphereZ[v2]);
+ vertex(sphereX[v2], sphereY[v2], sphereZ[v2]);
+ endShape();
+ }
+
+ // add the northern cap
+ beginShape(TRIANGLE_STRIP);
+ for (int i = 0; i < sphereDetail; i++) {
+ v2 = voff + i;
+ normal(sphereX[v2], sphereY[v2], sphereZ[v2]);
+ vertex(sphereX[v2], sphereY[v2], sphereZ[v2]);
+ normal(0, 1, 0);
+ vertex(0, 1, 0);
+ }
+ normal(sphereX[voff], sphereY[voff], sphereZ[voff]);
+ vertex(sphereX[voff], sphereY[voff], sphereZ[voff]);
+ normal(0, 1, 0);
+ vertex(0, 1, 0);
+ endShape();
+ popMatrix();
+
+ if (triangle != null) { // triangle is null in gl
+ triangle.setCulling(false);
+ }
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // CURVES
+
+
+ public void bezier(float x1, float y1,
+ float x2, float y2,
+ float x3, float y3,
+ float x4, float y4) {
+ bezier(x1, y1, 0,
+ x2, y2, 0,
+ x3, y3, 0,
+ x4, y4, 0);
+ }
+
+
+ public void bezier(float x1, float y1, float z1,
+ float x2, float y2, float z2,
+ float x3, float y3, float z3,
+ float x4, float y4, float z4) {
+ beginShape(LINE_STRIP);
+ vertex(x1, y1, z1);
+ bezierVertex(x2, y2, z2,
+ x3, y3, z3,
+ x4, y4, z4);
+ endShape();
+ }
+
+
+ public void curve(float x1, float y1,
+ float x2, float y2,
+ float x3, float y3,
+ float x4, float y4) {
+ curve(x1, y1, 0,
+ x2, y2, 0,
+ x3, y3, 0,
+ x4, y4, 0);
+ }
+
+
+ public void curve(float x1, float y1, float z1,
+ float x2, float y2, float z2,
+ float x3, float y3, float z3,
+ float x4, float y4, float z4) {
+ beginShape(LINE_STRIP);
+ curveVertex(x1, y1, z1);
+ curveVertex(x2, y2, z2);
+ curveVertex(x3, y3, z3);
+ curveVertex(x4, y4, z4);
+ endShape();
+ }
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ protected void imageImpl(PImage image,
+ float x1, float y1, float x2, float y2,
+ int u1, int v1, int u2, int v2) {
+
+ //float x2 = x1 + w;
+ //float y2 = y1 + h;
+
+ boolean savedStroke = stroke;
+ boolean savedFill = fill;
+ int savedTextureMode = textureMode;
+
+ stroke = false;
+ fill = true;
+ textureMode = IMAGE;
+
+ float savedFillR = fillR;
+ float savedFillG = fillG;
+ float savedFillB = fillB;
+ float savedFillA = fillA;
+
+ if (tint) {
+ fillR = tintR;
+ fillG = tintG;
+ fillB = tintB;
+ fillA = tintA;
+
+ } else {
+ fillR = 1;
+ fillG = 1;
+ fillB = 1;
+ fillA = 1;
+ }
+
+ //System.out.println(fill + " " + fillR + " " + fillG + " " + fillB);
+
+ beginShape(QUADS);
+ texture(image);
+ vertex(x1, y1, u1, v1);
+ vertex(x1, y2, u1, v2);
+ vertex(x2, y2, u2, v2);
+ vertex(x2, y1, u2, v1);
+ endShape();
+
+ stroke = savedStroke;
+ fill = savedFill;
+ textureMode = savedTextureMode;
+
+ fillR = savedFillR;
+ fillG = savedFillG;
+ fillB = savedFillB;
+ fillA = savedFillA;
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // MATRIX TRANSFORMATIONS
+
+
+ public void translate(float tx, float ty) {
+ translate(tx, ty, 0);
+ }
+
+
+ public void translate(float tx, float ty, float tz) {
+ forwardTransform.translate(tx, ty, tz);
+ reverseTransform.invTranslate(tx, ty, tz);
+ }
+
+
+ /**
+ * Two dimensional rotation. Same as rotateZ (this is identical
+ * to a 3D rotation along the z-axis) but included for clarity --
+ * it'd be weird for people drawing 2D graphics to be using rotateZ.
+ * And they might kick our a-- for the confusion.
+ */
+ public void rotate(float angle) {
+ rotateZ(angle);
+ }
+
+
+ // OPT could save several multiplies for the 0s and 1s by just
+ // putting the multMatrix code here and removing uneccessary terms
+
+ public void rotateX(float angle) {
+ forwardTransform.rotateX(angle);
+ reverseTransform.invRotateX(angle);
+ }
+
+
+ public void rotateY(float angle) {
+ forwardTransform.rotateY(angle);
+ reverseTransform.invRotateY(angle);
+ }
+
+
+ /**
+ * Rotate in the XY plane by an angle.
+ *
+ * Note that this doesn't internally set the number of
+ * dimensions to three, since rotateZ() is the same as a
+ * 2D rotate in the XY plane.
+ */
+ public void rotateZ(float angle) {
+ forwardTransform.rotateZ(angle);
+ reverseTransform.invRotateZ(angle);
+ }
+
+
+ /**
+ * Rotate around an arbitrary vector, similar to glRotate(),
+ * except that it takes radians (instead of degrees).
+ */
+ public void rotate(float angle, float v0, float v1, float v2) {
+ forwardTransform.rotate(angle, v0, v1, v2);
+ reverseTransform.invRotate(angle, v0, v1, v2);
+ }
+
+
+ /**
+ * Same as scale(s, s, s);
+ */
+ public void scale(float s) {
+ scale(s, s, s);
+ }
+
+
+ /**
+ * Not recommended for use in 3D, because the z-dimension is just
+ * scaled by 1, since there's no way to know what else to scale it by.
+ * Equivalent to scale(sx, sy, 1);
+ */
+ public void scale(float sx, float sy) {
+ scale(sx, sy, 1);
+ }
+
+
+ /**
+ * Scale in three dimensions.
+ */
+ public void scale(float x, float y, float z) {
+ forwardTransform.scale(x, y, z);
+ reverseTransform.invScale(x, y, z);
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // TRANSFORMATION MATRIX
+
+
+ public void pushMatrix() {
+ if (!modelview.push()) {
+ throw new RuntimeException("Too many calls to pushMatrix()");
+ }
+ // Do this to the inverse regardless of the lights
+ // to keep stack pointers in sync
+ modelviewInv.push();
+ }
+
+
+ public void popMatrix() {
+ if (!modelview.pop()) {
+ throw new RuntimeException("Too many calls to popMatrix() " +
+ "(and not enough to pushMatrix)");
+ }
+ // Do this to the inverse regardless of the lights
+ // to keep stack pointers in sync
+ modelviewInv.pop();
+ }
+
+
+ /**
+ * Load identity as the transform/model matrix.
+ * Same as glLoadIdentity().
+ */
+ public void resetMatrix() {
+ forwardTransform.reset();
+ reverseTransform.reset();
+ }
+
+
+ public void applyMatrix(float n00, float n01, float n02,
+ float n10, float n11, float n12) {
+ throw new RuntimeException("Use applyMatrix() with a 4x4 matrix " +
+ "when using OPENGL or P3D");
+ }
+
+ /**
+ * Apply a 4x4 transformation matrix. Same as glMultMatrix().
+ * This call will be slow because it will try to calculate the
+ * inverse of the transform. So avoid it whenever possible.
+ */
+ public void applyMatrix(float n00, float n01, float n02, float n03,
+ float n10, float n11, float n12, float n13,
+ float n20, float n21, float n22, float n23,
+ float n30, float n31, float n32, float n33) {
+
+ forwardTransform.apply(n00, n01, n02, n03,
+ n10, n11, n12, n13,
+ n20, n21, n22, n23,
+ n30, n31, n32, n33);
+
+ reverseTransform.invApply(n00, n01, n02, n03,
+ n10, n11, n12, n13,
+ n20, n21, n22, n23,
+ n30, n31, n32, n33);
+ }
+
+
+ /**
+ * Load the modelview into m00, m01, et al so that it can be used.
+ */
+ public void loadMatrix() {
+ m00 = modelview.m00;
+ m01 = modelview.m01;
+ m02 = modelview.m02;
+ m03 = modelview.m03;
+
+ m10 = modelview.m10;
+ m11 = modelview.m11;
+ m12 = modelview.m12;
+ m13 = modelview.m13;
+
+ m20 = modelview.m20;
+ m21 = modelview.m21;
+ m22 = modelview.m22;
+ m23 = modelview.m23;
+
+ m30 = modelview.m30;
+ m31 = modelview.m31;
+ m32 = modelview.m32;
+ m33 = modelview.m33;
+ }
+
+
+ /**
+ * Print the current model (or "transformation") matrix.
+ */
+ public void printMatrix() {
+ modelview.print();
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // CAMERA and PERSPECTIVE
+
+
+ /**
+ * Set matrix mode to the camera matrix (instead of the current
+ * transformation matrix). This means applyMatrix, resetMatrix, etc.
+ * will affect the camera.
+ *
+ * beginCamera();
+ * translate(0, 0, 10);
+ * endCamera();
+ *
+ * at the top of draw(), will result in a runaway camera that shoots
+ * infinitely out of the screen over time. In order to prevent this,
+ * it is necessary to call some function that does a hard reset of the
+ * camera transform matrix inside of begin/endCamera. Two options are
+ *
+ * camera(); // sets up the nice default camera transform
+ * resetMatrix(); // sets up the identity camera transform
+ *
+ * So to rotate a camera a constant amount, you might try
+ *
+ * beginCamera();
+ * camera();
+ * rotateY(PI/8);
+ * endCamera();
+ *
+ */
+ public void beginCamera() {
+ if (manipulatingCamera) {
+ throw new RuntimeException("beginCamera() cannot be called again " +
+ "before endCamera()");
+ } else {
+ manipulatingCamera = true;
+ forwardTransform = cameraInv;
+ reverseTransform = camera;
+ }
+ }
+
+
+ /**
+ * Record the current settings into the camera matrix, and set
+ * the matrix mode back to the current transformation matrix.
+ *
+ * camera(); or
+ * camera(ex, ey, ez, cx, cy, cz, ux, uy, uz);
+ *
+ * do not need to be called from with beginCamera();/endCamera();
+ * That's because they always apply to the camera transformation,
+ * and they always totally replace it. That means that any coordinate
+ * transforms done before camera(); in draw() will be wiped out.
+ * It also means that camera() always operates in untransformed world
+ * coordinates. Therefore it is always redundant to call resetMatrix();
+ * before camera(); This isn't technically true of gluLookat, but it's
+ * pretty much how it's used.
+ *
+ * beginCamera();
+ * rotateY(PI/80);
+ * endCamera();
+ *
+ * will result in a camera that spins without stopping. If you want to
+ * just rotate a small constant amount, try this:
+ *
+ * beginCamera();
+ * camera(); // sets up the default view
+ * rotateY(PI/80);
+ * endCamera();
+ *
+ * That will rotate a little off of the default view. Note that this
+ * is entirely equivalent to
+ *
+ * camera(); // sets up the default view
+ * beginCamera();
+ * rotateY(PI/80);
+ * endCamera();
+ *
+ * because camera() doesn't care whether or not it's inside a
+ * begin/end clause. Basically it's safe to use camera() or
+ * camera(ex, ey, ez, cx, cy, cz, ux, uy, uz) as naked calls because
+ * they do all the matrix resetting automatically.
+ */
+ public void camera(float eyeX, float eyeY, float eyeZ,
+ float centerX, float centerY, float centerZ,
+ float upX, float upY, float upZ) {
+ float z0 = eyeX - centerX;
+ float z1 = eyeY - centerY;
+ float z2 = eyeZ - centerZ;
+ float mag = sqrt(z0*z0 + z1*z1 + z2*z2);
+
+ if (mag != 0) {
+ z0 /= mag;
+ z1 /= mag;
+ z2 /= mag;
+ }
+
+ float y0 = upX;
+ float y1 = upY;
+ float y2 = upZ;
+
+ float x0 = y1*z2 - y2*z1;
+ float x1 = -y0*z2 + y2*z0;
+ float x2 = y0*z1 - y1*z0;
+
+ y0 = z1*x2 - z2*x1;
+ y1 = -z0*x2 + z2*x0;
+ y2 = z0*x1 - z1*x0;
+
+ mag = sqrt(x0*x0 + x1*x1 + x2*x2);
+ if (mag != 0) {
+ x0 /= mag;
+ x1 /= mag;
+ x2 /= mag;
+ }
+
+ mag = sqrt(y0*y0 + y1*y1 + y2*y2);
+ if (mag != 0) {
+ y0 /= mag;
+ y1 /= mag;
+ y2 /= mag;
+ }
+
+ // just does an apply to the main matrix,
+ // since that'll be copied out on endCamera
+ camera.set(x0, x1, x2, 0,
+ y0, y1, y2, 0,
+ z0, z1, z2, 0,
+ 0, 0, 0, 1);
+ camera.translate(-eyeX, -eyeY, -eyeZ);
+
+ cameraInv.reset();
+ cameraInv.invApply(x0, x1, x2, 0,
+ y0, y1, y2, 0,
+ z0, z1, z2, 0,
+ 0, 0, 0, 1);
+ cameraInv.invTranslate(-eyeX, -eyeY, -eyeZ);
+
+ modelview.set(camera);
+ modelviewInv.set(cameraInv);
+ }
+
+
+ /**
+ * Print the current camera matrix.
+ */
+ public void printCamera() {
+ camera.print();
+ }
+
+
+ /**
+ * Calls ortho() with the proper parameters for Processing's
+ * standard orthographic projection.
+ */
+ public void ortho() {
+ ortho(0, width, 0, height, -10, 10);
+ }
+
+
+ /**
+ * Similar to gluOrtho(), but wipes out the current projection matrix.
+ *
+ *
+ * Each of these three functions completely replaces the projection
+ * matrix with a new one. They can be called inside setup(), and their
+ * effects will be felt inside draw(). At the top of draw(), the projection
+ * matrix is not reset. Therefore the last projection function to be
+ * called always dominates. On resize, the default projection is always
+ * established, which has perspective.
+ *
+ * The Lighting Skinny:
+ *
+ * The way lighting works is complicated enough that it's worth
+ * producing a document to describe it. Lighting calculations proceed
+ * pretty much exactly as described in the OpenGL red book.
+ *
+ * Light-affecting material properties:
+ *
+ * AMBIENT COLOR
+ * - multiplies by light's ambient component
+ * - for believability this should match diffuse color
+ *
+ * DIFFUSE COLOR
+ * - multiplies by light's diffuse component
+ *
+ * SPECULAR COLOR
+ * - multiplies by light's specular component
+ * - usually less colored than diffuse/ambient
+ *
+ * SHININESS
+ * - the concentration of specular effect
+ * - this should be set pretty high (20-50) to see really
+ * noticeable specularity
+ *
+ * EMISSIVE COLOR
+ * - constant additive color effect
+ *
+ * Light types:
+ *
+ * AMBIENT
+ * - one color
+ * - no specular color
+ * - no direction
+ * - may have falloff (constant, linear, and quadratic)
+ * - may have position (which matters in non-constant falloff case)
+ * - multiplies by a material's ambient reflection
+ *
+ * DIRECTIONAL
+ * - has diffuse color
+ * - has specular color
+ * - has direction
+ * - no position
+ * - no falloff
+ * - multiplies by a material's diffuse and specular reflections
+ *
+ * POINT
+ * - has diffuse color
+ * - has specular color
+ * - has position
+ * - no direction
+ * - may have falloff (constant, linear, and quadratic)
+ * - multiplies by a material's diffuse and specular reflections
+ *
+ * SPOT
+ * - has diffuse color
+ * - has specular color
+ * - has position
+ * - has direction
+ * - has cone angle (set to half the total cone angle)
+ * - has concentration value
+ * - may have falloff (constant, linear, and quadratic)
+ * - multiplies by a material's diffuse and specular reflections
+ *
+ * Normal modes:
+ *
+ * All of the primitives (rect, box, sphere, etc.) have their normals
+ * set nicely. During beginShape/endShape normals can be set by the user.
+ *
+ * AUTO-NORMAL
+ * - if no normal is set during the shape, we are in auto-normal mode
+ * - auto-normal calculates one normal per triangle (face-normal mode)
+ *
+ * SHAPE-NORMAL
+ * - if one normal is set during the shape, it will be used for
+ * all vertices
+ *
+ * VERTEX-NORMAL
+ * - if multiple normals are set, each normal applies to
+ * subsequent vertices
+ * - (except for the first one, which applies to previous
+ * and subsequent vertices)
+ *
+ * Efficiency consequences:
+ *
+ * There is a major efficiency consequence of position-dependent
+ * lighting calculations per vertex. (See below for determining
+ * whether lighting is vertex position-dependent.) If there is no
+ * position dependency then the only factors that affect the lighting
+ * contribution per vertex are its colors and its normal.
+ * There is a major efficiency win if
+ *
+ * 1) lighting is not position dependent
+ * 2) we are in AUTO-NORMAL or SHAPE-NORMAL mode
+ *
+ * because then we can calculate one lighting contribution per shape
+ * (SHAPE-NORMAL) or per triangle (AUTO-NORMAL) and simply multiply it
+ * into the vertex colors. The converse is our worst-case performance when
+ *
+ * 1) lighting is position dependent
+ * 2) we are in AUTO-NORMAL mode
+ *
+ * because then we must calculate lighting per-face * per-vertex.
+ * Each vertex has a different lighting contribution per face in
+ * which it appears. Yuck.
+ *
+ * Determining vertex position dependency:
+ *
+ * If any of the following factors are TRUE then lighting is
+ * vertex position dependent:
+ *
+ * 1) Any lights uses non-constant falloff
+ * 2) There are any point or spot lights
+ * 3) There is a light with specular color AND there is a
+ * material with specular color
+ *
+ * So worth noting is that default lighting (a no-falloff ambient
+ * and a directional without specularity) is not position-dependent.
+ * We should capitalize.
+ *
+ * Simon Greenwold, April 2005
+ *
+ */
+ public void lights() {
+ // need to make sure colorMode is RGB 255 here
+ int colorModeSaved = colorMode;
+ colorMode = RGB;
+
+ lightFalloff(1, 0, 0);
+ lightSpecular(0, 0, 0);
+
+ ambientLight(colorModeX * 0.5f,
+ colorModeY * 0.5f,
+ colorModeZ * 0.5f);
+ directionalLight(colorModeX * 0.5f,
+ colorModeY * 0.5f,
+ colorModeZ * 0.5f,
+ 0, 0, -1);
+
+ colorMode = colorModeSaved;
+
+ lightingDependsOnVertexPosition = false;
+ }
+
+
+ /**
+ * Add an ambient light based on the current color mode.
+ */
+ public void ambientLight(float r, float g, float b) {
+ ambientLight(r, g, b, 0, 0, 0);
+ }
+
+
+ /**
+ * Add an ambient light based on the current color mode.
+ * This version includes an (x, y, z) position for situations
+ * where the falloff distance is used.
+ */
+ public void ambientLight(float r, float g, float b,
+ float x, float y, float z) {
+ if (lightCount == MAX_LIGHTS) {
+ throw new RuntimeException("can only create " + MAX_LIGHTS + " lights");
+ }
+ colorCalc(r, g, b);
+ lightDiffuse[lightCount][0] = calcR;
+ lightDiffuse[lightCount][1] = calcG;
+ lightDiffuse[lightCount][2] = calcB;
+
+ lightType[lightCount] = AMBIENT;
+ lightFalloffConstant[lightCount] = currentLightFalloffConstant;
+ lightFalloffLinear[lightCount] = currentLightFalloffLinear;
+ lightFalloffQuadratic[lightCount] = currentLightFalloffQuadratic;
+ lightPosition(lightCount, x, y, z);
+ lightCount++;
+ //return lightCount-1;
+ }
+
+
+ public void directionalLight(float r, float g, float b,
+ float nx, float ny, float nz) {
+ if (lightCount == MAX_LIGHTS) {
+ throw new RuntimeException("can only create " + MAX_LIGHTS + " lights");
+ }
+ colorCalc(r, g, b);
+ lightDiffuse[lightCount][0] = calcR;
+ lightDiffuse[lightCount][1] = calcG;
+ lightDiffuse[lightCount][2] = calcB;
+
+ lightType[lightCount] = DIRECTIONAL;
+ lightFalloffConstant[lightCount] = currentLightFalloffConstant;
+ lightFalloffLinear[lightCount] = currentLightFalloffLinear;
+ lightFalloffQuadratic[lightCount] = currentLightFalloffQuadratic;
+ lightSpecular[lightCount][0] = currentLightSpecular[0];
+ lightSpecular[lightCount][1] = currentLightSpecular[1];
+ lightSpecular[lightCount][2] = currentLightSpecular[2];
+ lightDirection(lightCount, nx, ny, nz);
+ lightCount++;
+ }
+
+
+ public void pointLight(float r, float g, float b,
+ float x, float y, float z) {
+ if (lightCount == MAX_LIGHTS) {
+ throw new RuntimeException("can only create " + MAX_LIGHTS + " lights");
+ }
+ colorCalc(r, g, b);
+ lightDiffuse[lightCount][0] = calcR;
+ lightDiffuse[lightCount][1] = calcG;
+ lightDiffuse[lightCount][2] = calcB;
+
+ lightType[lightCount] = POINT;
+ lightFalloffConstant[lightCount] = currentLightFalloffConstant;
+ lightFalloffLinear[lightCount] = currentLightFalloffLinear;
+ lightFalloffQuadratic[lightCount] = currentLightFalloffQuadratic;
+ lightSpecular[lightCount][0] = currentLightSpecular[0];
+ lightSpecular[lightCount][1] = currentLightSpecular[1];
+ lightSpecular[lightCount][2] = currentLightSpecular[2];
+ lightPosition(lightCount, x, y, z);
+ lightCount++;
+
+ lightingDependsOnVertexPosition = true;
+ }
+
+
+ public void spotLight(float r, float g, float b,
+ float x, float y, float z,
+ float nx, float ny, float nz,
+ float angle, float concentration) {
+ if (lightCount == MAX_LIGHTS) {
+ throw new RuntimeException("can only create " + MAX_LIGHTS + " lights");
+ }
+ colorCalc(r, g, b);
+ lightDiffuse[lightCount][0] = calcR;
+ lightDiffuse[lightCount][1] = calcG;
+ lightDiffuse[lightCount][2] = calcB;
+
+ lightType[lightCount] = SPOT;
+ lightFalloffConstant[lightCount] = currentLightFalloffConstant;
+ lightFalloffLinear[lightCount] = currentLightFalloffLinear;
+ lightFalloffQuadratic[lightCount] = currentLightFalloffQuadratic;
+ lightSpecular[lightCount][0] = currentLightSpecular[0];
+ lightSpecular[lightCount][1] = currentLightSpecular[1];
+ lightSpecular[lightCount][2] = currentLightSpecular[2];
+ lightPosition(lightCount, x, y, z);
+ lightDirection(lightCount, nx, ny, nz);
+ lightSpotAngle[lightCount] = angle;
+ lightSpotAngleCos[lightCount] = max(0, cos(angle));
+ lightSpotConcentration[lightCount] = concentration;
+ lightCount++;
+
+ lightingDependsOnVertexPosition = true;
+ }
+
+
+ /**
+ * Set the light falloff rates for the last light that was created.
+ * Default is lightFalloff(1, 0, 0).
+ */
+ public void lightFalloff(float constant, float linear, float quadratic) {
+ currentLightFalloffConstant = constant;
+ currentLightFalloffLinear = linear;
+ currentLightFalloffQuadratic = quadratic;
+
+ lightingDependsOnVertexPosition = true;
+ }
+
+
+ /**
+ * Set the specular color of the last light created.
+ */
+ public void lightSpecular(float x, float y, float z) {
+ colorCalc(x, y, z);
+ currentLightSpecular[0] = calcR;
+ currentLightSpecular[1] = calcG;
+ currentLightSpecular[2] = calcB;
+
+ lightingDependsOnVertexPosition = true;
+ }
+
+
+ /**
+ * internal function to set the light position
+ * based on the current modelview matrix.
+ */
+ protected void lightPosition(int num, float x, float y, float z) {
+ lightPosition[num][0] =
+ modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
+ lightPosition[num][1] =
+ modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
+ lightPosition[num][2] =
+ modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
+ }
+
+
+ /**
+ * internal function to set the light direction
+ * based on the current modelview matrix.
+ */
+ protected void lightDirection(int num, float x, float y, float z) {
+ // Multiply by inverse transpose.
+ lightNormal[num][0] =
+ modelviewInv.m00*x + modelviewInv.m10*y +
+ modelviewInv.m20*z + modelviewInv.m30;
+ lightNormal[num][1] =
+ modelviewInv.m01*x + modelviewInv.m11*y +
+ modelviewInv.m21*z + modelviewInv.m31;
+ lightNormal[num][2] =
+ modelviewInv.m02*x + modelviewInv.m12*y +
+ modelviewInv.m22*z + modelviewInv.m32;
+
+ float n = mag(lightNormal[num]);
+ if (n == 0 || n == 1) return;
+
+ lightNormal[num][0] /= n;
+ lightNormal[num][1] /= n;
+ lightNormal[num][2] /= n;
+ }
+
+
+
+ //////////////////////////////////////////////////////////////
+
+ // BACKGROUND
+
+
+ /**
+ * Takes an RGB or RGBA image and sets it as the background.
+ *
- *
- * Luminance conversion code contributed by
- * toxi
- *
- * Gaussian blur code contributed by
- * Mario Klingemann
- */
- public void filter(int kind) {
- beginPixels();
-
- switch (kind) {
- case BLUR:
- // TODO write basic low-pass filter blur here
- // what does photoshop do on the edges with this guy?
- // better yet.. why bother? just use gaussian with radius 1
- filter(BLUR, 1);
- break;
-
- case GRAY:
- // Converts RGB image data into grayscale using
- // weighted RGB components, and keeps alpha channel intact.
- // [toxi 040115]
- for (int i = 0; i < pixels.length; i++) {
- int col = pixels[i];
- // luminance = 0.3*red + 0.59*green + 0.11*blue
- // 0.30 * 256 = 77
- // 0.59 * 256 = 151
- // 0.11 * 256 = 28
- int lum = (77*(col>>16&0xff) + 151*(col>>8&0xff) + 28*(col&0xff))>>8;
- pixels[i] = (col & ALPHA_MASK) | lum<<16 | lum<<8 | lum;
- }
- break;
-
- case INVERT:
- for (int i = 0; i < pixels.length; i++) {
- //pixels[i] = 0xff000000 |
- pixels[i] ^= 0xffffff;
- }
- break;
-
- case POSTERIZE:
- throw new RuntimeException("Use filter(POSTERIZE, int levels) " +
- "instead of filter(POSTERIZE)");
-
- case RGB:
- for (int i = 0; i < pixels.length; i++) {
- pixels[i] |= 0xff000000;
- }
- format = RGB;
- break;
-
- case THRESHOLD:
- filter(THRESHOLD, 0.5f);
- break;
-
- // [toxi20050728] added new filters
- case ERODE:
- dilate(true);
- break;
-
- case DILATE:
- dilate(false);
- break;
- }
- endPixels(); // mark as modified
- }
-
-
- /**
- * Method to apply a variety of basic filters to this image.
- * These filters all take a parameter.
- *
- *
- * Gaussian blur code contributed by
- * Mario Klingemann
- * and later updated by toxi for better speed.
- */
- public void filter(int kind, float param) {
- beginPixels();
-
- switch (kind) {
- case BLUR:
- if (format == ALPHA)
- blurAlpha(param);
- else if (format == ARGB)
- blurARGB(param);
- else
- blurRGB(param);
- break;
-
- case GRAY:
- throw new RuntimeException("Use filter(GRAY) instead of " +
- "filter(GRAY, param)");
-
- case INVERT:
- throw new RuntimeException("Use filter(INVERT) instead of " +
- "filter(INVERT, param)");
-
- case OPAQUE:
- throw new RuntimeException("Use filter(OPAQUE) instead of " +
- "filter(OPAQUE, param)");
-
- case POSTERIZE:
- int levels = (int)param;
- if ((levels < 2) || (levels > 255)) {
- throw new RuntimeException("Levels must be between 2 and 255 for " +
- "filter(POSTERIZE, levels)");
- }
- // TODO not optimized
- int levels256 = 256 / levels;
- int levels1 = levels - 1;
- for (int i = 0; i < pixels.length; i++) {
- int rlevel = ((pixels[i] >> 16) & 0xff) / levels256;
- int glevel = ((pixels[i] >> 8) & 0xff) / levels256;
- int blevel = (pixels[i] & 0xff) / levels256;
-
- rlevel = (rlevel * 255 / levels1) & 0xff;
- glevel = (glevel * 255 / levels1) & 0xff;
- blevel = (blevel * 255 / levels1) & 0xff;
-
- pixels[i] = ((0xff000000 & pixels[i]) |
- (rlevel << 16) |
- (glevel << 8) |
- blevel);
- }
- break;
-
- case THRESHOLD: // greater than or equal to the threshold
- int thresh = (int) (param * 255);
- for (int i = 0; i < pixels.length; i++) {
- int max = Math.max((pixels[i] & RED_MASK) >> 16,
- Math.max((pixels[i] & GREEN_MASK) >> 8,
- (pixels[i] & BLUE_MASK)));
- pixels[i] = (pixels[i] & ALPHA_MASK) |
- ((max < thresh) ? 0x000000 : 0xffffff);
- }
- break;
-
- // [toxi20050728] added new filters
- case ERODE:
- throw new RuntimeException("Use filter(ERODE) instead of " +
- "filter(ERODE, param)");
- case DILATE:
- throw new RuntimeException("Use filter(DILATE) instead of " +
- "filter(DILATE, param)");
- }
- endPixels(); // mark as modified
- }
-
-
- /**
- * Optimized code for building the blur kernel.
- * further optimized blur code (approx. 15% for radius=20)
- * bigger speed gains for larger radii (~30%)
- * added support for various image types (ALPHA, RGB, ARGB)
- * [toxi 050728]
- */
- protected void buildBlurKernel(float r) {
- int radius = (int) (r * 3.5f);
- radius = (radius < 1) ? 1 : ((radius < 248) ? radius : 248);
- if (blurRadius != radius) {
- blurRadius = radius;
- blurKernelSize = 1 + blurRadius<<1;
- blurKernel = new int[blurKernelSize];
- blurMult = new int[blurKernelSize][256];
-
- int bk,bki;
- int[] bm,bmi;
-
- for (int i = 1, radiusi = radius - 1; i < radius; i++) {
- blurKernel[radius+i] = blurKernel[radiusi] = bki = radiusi * radiusi;
- bm=blurMult[radius+i];
- bmi=blurMult[radiusi--];
- for (int j = 0; j < 256; j++)
- bm[j] = bmi[j] = bki*j;
- }
- bk = blurKernel[radius] = radius * radius;
- bm = blurMult[radius];
- for (int j = 0; j < 256; j++)
- bm[j] = bk*j;
- }
- }
-
- protected void blurAlpha(float r) {
- int sum, /*cr, cg,*/ cb; //, k;
- int /*pixel,*/ read, ri, /*roff,*/ ym, ymi, /*riw,*/ bk0;
- int b2[] = new int[pixels.length];
- int yi = 0;
-
- buildBlurKernel(r);
-
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- //cb = cg = cr = sum = 0;
- cb = sum = 0;
- read = x - blurRadius;
- if (read<0) {
- bk0=-read;
- read=0;
- } else {
- if (read >= width)
- break;
- bk0=0;
- }
- for (int i = bk0; i < blurKernelSize; i++) {
- if (read >= width)
- break;
- int c = pixels[read + yi];
- int[] bm=blurMult[i];
- cb += bm[c & BLUE_MASK];
- sum += blurKernel[i];
- read++;
- }
- ri = yi + x;
- b2[ri] = cb / sum;
- }
- yi += width;
- }
-
- yi = 0;
- ym=-blurRadius;
- ymi=ym*width;
-
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- //cb = cg = cr = sum = 0;
- cb = sum = 0;
- if (ym<0) {
- bk0 = ri = -ym;
- read = x;
- } else {
- if (ym >= height)
- break;
- bk0 = 0;
- ri = ym;
- read = x + ymi;
- }
- for (int i = bk0; i < blurKernelSize; i++) {
- if (ri >= height)
- break;
- int[] bm=blurMult[i];
- cb += bm[b2[read]];
- sum += blurKernel[i];
- ri++;
- read += width;
- }
- pixels[x+yi] = (cb/sum);
- }
- yi += width;
- ymi += width;
- ym++;
- }
- }
-
- protected void blurRGB(float r) {
- int sum, cr, cg, cb; //, k;
- int /*pixel,*/ read, ri, /*roff,*/ ym, ymi, /*riw,*/ bk0;
- int r2[] = new int[pixels.length];
- int g2[] = new int[pixels.length];
- int b2[] = new int[pixels.length];
- int yi = 0;
-
- buildBlurKernel(r);
-
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- cb = cg = cr = sum = 0;
- read = x - blurRadius;
- if (read<0) {
- bk0=-read;
- read=0;
- } else {
- if (read >= width)
- break;
- bk0=0;
- }
- for (int i = bk0; i < blurKernelSize; i++) {
- if (read >= width)
- break;
- int c = pixels[read + yi];
- int[] bm=blurMult[i];
- cr += bm[(c & RED_MASK) >> 16];
- cg += bm[(c & GREEN_MASK) >> 8];
- cb += bm[c & BLUE_MASK];
- sum += blurKernel[i];
- read++;
- }
- ri = yi + x;
- r2[ri] = cr / sum;
- g2[ri] = cg / sum;
- b2[ri] = cb / sum;
- }
- yi += width;
- }
-
- yi = 0;
- ym=-blurRadius;
- ymi=ym*width;
-
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- cb = cg = cr = sum = 0;
- if (ym<0) {
- bk0 = ri = -ym;
- read = x;
- } else {
- if (ym >= height)
- break;
- bk0 = 0;
- ri = ym;
- read = x + ymi;
- }
- for (int i = bk0; i < blurKernelSize; i++) {
- if (ri >= height)
- break;
- int[] bm=blurMult[i];
- cr += bm[r2[read]];
- cg += bm[g2[read]];
- cb += bm[b2[read]];
- sum += blurKernel[i];
- ri++;
- read += width;
- }
- pixels[x+yi] = 0xff000000 | (cr/sum)<<16 | (cg/sum)<<8 | (cb/sum);
- }
- yi += width;
- ymi += width;
- ym++;
- }
- }
-
- protected void blurARGB(float r) {
- int sum, cr, cg, cb, ca;
- int /*pixel,*/ read, ri, /*roff,*/ ym, ymi, /*riw,*/ bk0;
- int wh = pixels.length;
- int r2[] = new int[wh];
- int g2[] = new int[wh];
- int b2[] = new int[wh];
- int a2[] = new int[wh];
- int yi = 0;
-
- buildBlurKernel(r);
-
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- cb = cg = cr = ca = sum = 0;
- read = x - blurRadius;
- if (read<0) {
- bk0=-read;
- read=0;
- } else {
- if (read >= width)
- break;
- bk0=0;
- }
- for (int i = bk0; i < blurKernelSize; i++) {
- if (read >= width)
- break;
- int c = pixels[read + yi];
- int[] bm=blurMult[i];
- ca += bm[(c & ALPHA_MASK) >>> 24];
- cr += bm[(c & RED_MASK) >> 16];
- cg += bm[(c & GREEN_MASK) >> 8];
- cb += bm[c & BLUE_MASK];
- sum += blurKernel[i];
- read++;
- }
- ri = yi + x;
- a2[ri] = ca / sum;
- r2[ri] = cr / sum;
- g2[ri] = cg / sum;
- b2[ri] = cb / sum;
- }
- yi += width;
- }
-
- yi = 0;
- ym=-blurRadius;
- ymi=ym*width;
-
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- cb = cg = cr = ca = sum = 0;
- if (ym<0) {
- bk0 = ri = -ym;
- read = x;
- } else {
- if (ym >= height)
- break;
- bk0 = 0;
- ri = ym;
- read = x + ymi;
- }
- for (int i = bk0; i < blurKernelSize; i++) {
- if (ri >= height)
- break;
- int[] bm=blurMult[i];
- ca += bm[a2[read]];
- cr += bm[r2[read]];
- cg += bm[g2[read]];
- cb += bm[b2[read]];
- sum += blurKernel[i];
- ri++;
- read += width;
- }
- pixels[x+yi] = (ca/sum)<<24 | (cr/sum)<<16 | (cg/sum)<<8 | (cb/sum);
- }
- yi += width;
- ymi += width;
- ym++;
- }
- }
-
- /**
- * Generic dilate/erode filter using luminance values
- * as decision factor. [toxi 050728]
- */
- protected void dilate(boolean isInverted) {
- int currIdx=0;
- int maxIdx=pixels.length;
- int[] out=new int[maxIdx];
-
- if (!isInverted) {
- // erosion (grow light areas)
- while (currIdx
- * As of revision 0100, this function requires an absolute path, - * in order to avoid confusion. To save inside the sketch folder, - * use the function savePath() from PApplet, or use saveFrame() instead. - *
- * As of revision 0115, when using Java 1.4 and later, you can write
- * to several formats besides tga and tiff. If Java 1.4 is installed
- * and the extension used is supported (usually png, jpg, jpeg, bmp,
- * and tiff), then those methods will be used to write the image.
- * To get a list of the supported formats for writing, use:
- * println(javax.imageio.ImageIO.getReaderFormatNames())
- *
- * To use the original built-in image writers, use .tga as the extension, - * or don't include an extension, in which case .tif will be added. - *
- * The ImageIO API claims to support wbmp files, however they probably - * require a black and white image. Basic testing produced a zero-length - * file with no error. - */ - public void save(String filename) { // ignore - boolean success = false; - - File file = new File(filename); - if (!file.isAbsolute()) { - System.err.println("PImage.save() requires an absolute path, " + - "you might need to use savePath()."); - return; - } - - try { - OutputStream os = null; - - if (PApplet.javaVersion >= 1.4f) { - if (saveImageFormats == null) { - //saveImageFormats = javax.imageio.ImageIO.getWriterFormatNames(); - try { - Class ioClass = Class.forName("javax.imageio.ImageIO"); - Method getFormatNamesMethod = - ioClass.getMethod("getWriterFormatNames", (Class[]) null); - saveImageFormats = (String[]) - getFormatNamesMethod.invoke((Class[]) null, (Object[]) null); - } catch (Exception e) { - e.printStackTrace(); - } - } - if (saveImageFormats != null) { - for (int i = 0; i < saveImageFormats.length; i++) { - //System.out.println(saveImageFormats[i]); - if (filename.endsWith("." + saveImageFormats[i])) { - saveImageIO(filename); - return; - } - } - } - } - - if (filename.toLowerCase().endsWith(".tga")) { - os = new BufferedOutputStream(new FileOutputStream(filename), 32768); - success = saveTGA(os); //, pixels, width, height, format); - - } else { - if (!filename.toLowerCase().endsWith(".tif") && - !filename.toLowerCase().endsWith(".tiff")) { - // if no .tif extension, add it.. - filename += ".tif"; - } - os = new BufferedOutputStream(new FileOutputStream(filename), 32768); - success = saveTIFF(os); //, pixels, width, height); - } - os.flush(); - os.close(); - - } catch (IOException e) { - //System.err.println("Error while saving image."); - e.printStackTrace(); - success = false; - } - if (!success) { - throw new RuntimeException("Error while saving image."); - } - } -} - +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-06 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.awt.image.*; +import java.io.*; +import java.lang.reflect.*; + +import javax.imageio.*; + + +/** + * Storage class for pixel data. + *
+ * Code for copying, resizing, scaling, and blending contributed + * by toxi + *
+ */
+public class PImage implements PConstants, Cloneable {
+
+ /**
+ * Format for this image, one of RGB, ARGB or ALPHA.
+ * note that RGB images still require 0xff in the high byte
+ * because of how they'll be manipulated by other functions
+ */
+ public int format;
+
+ public int pixels[];
+ public int width, height;
+ // would scan line be useful? maybe for pow of 2 gl textures
+
+ // note! inherited by PGraphics
+ public int imageMode = CORNER;
+ public boolean smooth = false;
+
+ /** native storage for java 1.3 image object */
+ //public Object image;
+
+ /** for subclasses that need to store info about the image */
+ public Object cache;
+
+ /** modified portion of the image */
+ public boolean modified;
+ public int mx1, my1, mx2, my2;
+
+ // private fields
+ private int fracU, ifU, fracV, ifV, u1, u2, v1, v2, sX, sY, iw, iw1, ih1;
+ private int ul, ll, ur, lr, cUL, cLL, cUR, cLR;
+ private int srcXOffset, srcYOffset;
+ private int r, g, b, a;
+ private int[] srcBuffer;
+
+ // fixed point precision is limited to 15 bits!!
+ static final int PRECISIONB = 15;
+ static final int PRECISIONF = 1 << PRECISIONB;
+ static final int PREC_MAXVAL = PRECISIONF-1;
+ static final int PREC_ALPHA_SHIFT = 24-PRECISIONB;
+ static final int PREC_RED_SHIFT = 16-PRECISIONB;
+
+ // internal kernel stuff for the gaussian blur filter
+ int blurRadius;
+ int blurKernelSize;
+ int[] blurKernel;
+ int[][] blurMult;
+
+
+ //////////////////////////////////////////////////////////////
+
+
+ /**
+ * Create an empty image object, set its format to RGB.
+ * The pixel array is not allocated.
+ */
+ public PImage() {
+ format = RGB; // makes sure that this guy is useful
+ cache = null;
+ }
+
+
+ /**
+ * Create a new RGB (alpha ignored) image of a specific size.
+ * All pixels are set to zero, meaning black, but since the
+ * alpha is zero, it will be transparent.
+ */
+ public PImage(int width, int height) {
+ init(width, height, RGB);
+ //this(new int[width * height], width, height, ARGB);
+ // toxi: is it maybe better to init the image with max alpha enabled?
+ //for(int i=0; i
+ * This is not currently used by any of the renderers, however the api + * is structured this way in the hope of being able to use this to + * speed things up in the future. + *
+ * Note that when using imageMode(CORNERS), + * the x2 and y2 positions are non-inclusive. + */ + public void endPixels(int x1, int y1, int x2, int y2) { + //if (!modified) { // could just set directly, but.. + //} + + if (imageMode == CORNER) { // x2, y2 are w/h + x2 += x1; + y2 += y1; + } + + if (!modified) { + mx1 = x1; + mx2 = x2; + my1 = y1; + my2 = y2; + modified = true; + + } else { + if (x1 < mx1) mx1 = x1; + if (x1 > mx2) mx2 = x1; + if (y1 < my1) my1 = y1; + if (y1 > my2) my2 = y1; + + if (x2 < mx1) mx1 = x2; + if (x2 > mx2) mx2 = x2; + if (y2 < my1) my1 = y2; + if (y2 > my2) my2 = y2; + } + } + + + //public void pixelsUpdated() { + //mx1 = Integer.MAX_VALUE; + //my1 = Integer.MAX_VALUE; + //mx2 = -Integer.MAX_VALUE; + //my2 = -Integer.MAX_VALUE; + //modified = false; + //} + + + ////////////////////////////////////////////////////////////// + + // GET/SET PIXELS + + + /** + * Returns an ARGB "color" type (a packed 32 bit int with the color. + * If the coordinate is outside the image, zero is returned + * (black, but completely transparent). + *
+ * If the image is in RGB format (i.e. on a PVideo object), + * the value will get its high bits set, just to avoid cases where + * they haven't been set already. + *
+ * If the image is in ALPHA format, this returns a white color + * that has its alpha value set. + *
+ * This function is included primarily for beginners. It is quite + * slow because it has to check to see if the x, y that was provided + * is inside the bounds, and then has to check to see what image + * type it is. If you want things to be more efficient, access the + * pixels[] array directly. + */ + public int get(int x, int y) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return 0; + + switch (format) { + case RGB: + return pixels[y*width + x] | 0xff000000; + + case ARGB: + return pixels[y*width + x]; + + case ALPHA: + return (pixels[y*width + x] << 24) | 0xffffff; + } + return 0; + } + + + /** + * Grab a subsection of a PImage, and copy it into a fresh PImage. + * This honors imageMode() for the coordinates. + */ + public PImage get(int x, int y, int w, int h) { + if (imageMode == CORNERS) { // if CORNER, do nothing + //x2 += x1; y2 += y1; + // w/h are x2/y2 in this case, bring em down to size + w = (w - x); + h = (h - x); + } + + if (x < 0) { + w += x; // clip off the left edge + x = 0; + } + if (y < 0) { + h += y; // clip off some of the height + y = 0; + } + + if (x + w > width) w = width - x; + if (y + h > height) h = height - y; + + PImage newbie = new PImage(new int[w*h], w, h, format); + + int index = y*width + x; + int index2 = 0; + for (int row = y; row < y+h; row++) { + System.arraycopy(pixels, index, + newbie.pixels, index2, w); + index+=width; + index2+=w; + } + return newbie; + } + + + /** + * Returns a copy of this PImage. Equivalent to get(0, 0, width, height). + */ + public PImage get() { + try { + return (PImage) clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } + + + /** + * Silently ignores if the coordinate is outside the image. + */ + public void set(int x, int y, int c) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return; + pixels[y*width + x] = c; + } + + + public void set(int dx, int dy, PImage src) { + int sx = 0; + int sy = 0; + int sw = src.width; + int sh = src.height; + + if (dx < 0) { // off left edge + sx -= dx; + sw += dx; + dx = 0; + } + if (dy < 0) { // off top edge + sy -= dy; + sh += dy; + dy = 0; + } + if (dx + sw > width) { // off right edge + sw = width - dx; + } + if (dy + sh > height) { // off bottom edge + sh = height - dy; + } + + // this could be nonexistant + if ((sw <= 0) || (sh <= 0)) return; + + setImpl(dx, dy, sx, sy, sw, sh, src); + } + + + /** + * Internal function to actually handle setting a block of pixels that + * has already been properly cropped from the image to a valid region. + */ + protected void setImpl(int dx, int dy, int sx, int sy, int sw, int sh, + PImage src) { + int srcOffset = sy * src.width + sx; + int dstOffset = dy * width + dx; + + for (int y = sy; y < sy + sh; y++) { + System.arraycopy(src.pixels, srcOffset, pixels, dstOffset, sw); + srcOffset += src.width; + dstOffset += width; + } + } + + + + ////////////////////////////////////////////////////////////// + + // ALPHA CHANNEL + + + /** + * Set alpha channel for an image. Black colors in the source + * image will make the destination image completely transparent, + * and white will make things fully opaque. Gray values will + * be in-between steps. + *
+ * Strictly speaking the "blue" value from the source image is + * used as the alpha color. For a fully grayscale image, this + * is correct, but for a color image it's not 100% accurate. + * For a more accurate conversion, first use filter(GRAY) + * which will make the image into a "correct" grayscake by + * performing a proper luminance-based conversion. + */ + public void mask(int alpha[]) { + // don't execute if mask image is different size + if (alpha.length != pixels.length) { + throw new RuntimeException("The PImage used with mask() must be " + + "the same size as the applet."); + } + for (int i = 0; i < pixels.length; i++) { + pixels[i] = ((alpha[i] & 0xff) << 24) | (pixels[i] & 0xffffff); + } + format = ARGB; + } + + + /** + * Set alpha channel for an image using another image as the source. + */ + public void mask(PImage alpha) { + mask(alpha.pixels); + } + + + /** + * Method to apply a variety of basic filters to this image. + *
+ *
+ *
+ * As of revision 0100, this function requires an absolute path, + * in order to avoid confusion. To save inside the sketch folder, + * use the function savePath() from PApplet, or use saveFrame() instead. + *
+ * As of revision 0115, when using Java 1.4 and later, you can write
+ * to several formats besides tga and tiff. If Java 1.4 is installed
+ * and the extension used is supported (usually png, jpg, jpeg, bmp,
+ * and tiff), then those methods will be used to write the image.
+ * To get a list of the supported formats for writing, use:
+ * println(javax.imageio.ImageIO.getReaderFormatNames())
+ *
+ * To use the original built-in image writers, use .tga as the extension, + * or don't include an extension, in which case .tif will be added. + *
+ * The ImageIO API claims to support wbmp files, however they probably + * require a black and white image. Basic testing produced a zero-length + * file with no error. + */ + public void save(String filename) { // ignore + boolean success = false; + + File file = new File(filename); + if (!file.isAbsolute()) { + System.err.println("PImage.save() requires an absolute path, " + + "you might need to use savePath()."); + return; + } + + try { + OutputStream os = null; + + if (PApplet.javaVersion >= 1.4f) { + if (saveImageFormats == null) { + //saveImageFormats = javax.imageio.ImageIO.getWriterFormatNames(); + try { + Class ioClass = Class.forName("javax.imageio.ImageIO"); + Method getFormatNamesMethod = + ioClass.getMethod("getWriterFormatNames", (Class[]) null); + saveImageFormats = (String[]) + getFormatNamesMethod.invoke((Class[]) null, (Object[]) null); + } catch (Exception e) { + e.printStackTrace(); + } + } + if (saveImageFormats != null) { + for (int i = 0; i < saveImageFormats.length; i++) { + //System.out.println(saveImageFormats[i]); + if (filename.endsWith("." + saveImageFormats[i])) { + saveImageIO(filename); + return; + } + } + } + } + + if (filename.toLowerCase().endsWith(".tga")) { + os = new BufferedOutputStream(new FileOutputStream(filename), 32768); + success = saveTGA(os); //, pixels, width, height, format); + + } else { + if (!filename.toLowerCase().endsWith(".tif") && + !filename.toLowerCase().endsWith(".tiff")) { + // if no .tif extension, add it.. + filename += ".tif"; + } + os = new BufferedOutputStream(new FileOutputStream(filename), 32768); + success = saveTIFF(os); //, pixels, width, height); + } + os.flush(); + os.close(); + + } catch (IOException e) { + //System.err.println("Error while saving image."); + e.printStackTrace(); + success = false; + } + if (!success) { + throw new RuntimeException("Error while saving image."); + } + } +} + diff --git a/core/PLine.java b/core/src/processing/core/PLine.java similarity index 96% rename from core/PLine.java rename to core/src/processing/core/PLine.java index d0b45b01c..12314323d 100644 --- a/core/PLine.java +++ b/core/src/processing/core/PLine.java @@ -1,1290 +1,1290 @@ -/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2004-06 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; - - -/** - * Code for rendering lines. - *
- * This code will soon be removed.
- * Written by Carlos Rocha.
- */
-public class PLine implements PConstants
-{
- private int[] m_pixels;
- private float[] m_zbuffer;
- //private int[] m_stencil;
-
- private int m_index;
-
- static final int R_COLOR = 0x1;
- static final int R_ALPHA = 0x2;
- static final int R_SPATIAL = 0x8;
- static final int R_THICK = 0x4;
- static final int R_SMOOTH = 0x10;
-
- private int SCREEN_WIDTH;
- private int SCREEN_HEIGHT;
- private int SCREEN_WIDTH1;
- private int SCREEN_HEIGHT1;
-
- public boolean INTERPOLATE_RGB;
- public boolean INTERPOLATE_ALPHA;
- public boolean INTERPOLATE_Z;
- public boolean INTERPOLATE_THICK;
-
- // antialias
- private boolean SMOOTH;
-
- // blender
- //private boolean BLENDER;
-
- // stroke color
- private int m_stroke;
-
- // draw flags
- public int m_drawFlags;
-
- // vertex coordinates
- private float[] x_array;
- private float[] y_array;
- private float[] z_array;
-
- // vertex intensity
- private float[] r_array;
- private float[] g_array;
- private float[] b_array;
- private float[] a_array;
-
- // vertex offsets
- private int o0;
- private int o1;
-
- // start values
- private float m_r0;
- private float m_g0;
- private float m_b0;
- private float m_a0;
- private float m_z0;
-
- // deltas
- private float dz;
-
- // rgba deltas
- private float dr;
- private float dg;
- private float db;
- private float da;
-
- private PGraphics3 parent;
-
-
- public PLine(PGraphics3 g) {
- INTERPOLATE_Z = false;
-
- x_array = new float[2];
- y_array = new float[2];
- z_array = new float[2];
- r_array = new float[2];
- g_array = new float[2];
- b_array = new float[2];
- a_array = new float[2];
-
- this.parent = g;
- }
-
-
- public void reset() {
- // reset these in case PGraphics was resized
- SCREEN_WIDTH = parent.width;
- SCREEN_HEIGHT = parent.height;
- SCREEN_WIDTH1 = SCREEN_WIDTH-1;
- SCREEN_HEIGHT1 = SCREEN_HEIGHT-1;
-
- m_pixels = parent.pixels;
- //m_stencil = parent.stencil;
- m_zbuffer = parent.zbuffer;
-
- // other things to reset
-
- INTERPOLATE_RGB = false;
- INTERPOLATE_ALPHA = false;
- //INTERPOLATE_Z = false;
- m_drawFlags = 0;
- m_index = 0;
- //BLENDER = false;
- }
-
-
- public void setVertices(float x0, float y0, float z0,
- float x1, float y1, float z1) {
- // [rocha] fixed z drawing, so whenever a line turns on
- // z interpolation, all the lines are z interpolated
- if (z0 != z1 || z0!=0.0f || z1!=0.0f || INTERPOLATE_Z) {
- INTERPOLATE_Z = true;
- m_drawFlags |= R_SPATIAL;
- } else {
- INTERPOLATE_Z = false;
- m_drawFlags &= ~R_SPATIAL;
- }
-
- z_array[0] = z0;
- z_array[1] = z1;
-
- x_array[0] = x0;
- x_array[1] = x1;
-
- y_array[0] = y0;
- y_array[1] = y1;
- }
-
- public void setIntensities(float r0, float g0, float b0, float a0,
- float r1, float g1, float b1, float a1) {
- a_array[0] = (a0 * 253f + 1.0f) * 65536f;
- a_array[1] = (a1 * 253f + 1.0f) * 65536f;
-
- // check if we need alpha or not?
- if ((a0 != 1.0f) || (a1 != 1.0f)) {
- INTERPOLATE_ALPHA = true;
- m_drawFlags |= R_ALPHA;
- } else {
- INTERPOLATE_ALPHA = false;
- m_drawFlags &= ~R_ALPHA;
- }
-
- // extra scaling added to prevent color "overflood" due to rounding errors
- r_array[0] = (r0 * 253f + 1.0f) * 65536f;
- r_array[1] = (r1 * 253f + 1.0f) * 65536f;
-
- g_array[0] = (g0 * 253f + 1.0f) * 65536f;
- g_array[1] = (g1 * 253f + 1.0f) * 65536f;
-
- b_array[0] = (b0 * 253f + 1.0f) * 65536f;
- b_array[1] = (b1 * 253f + 1.0f) * 65536f;
-
- // check if we need to interpolate the intensity values
- if (r0 != r1) {
- INTERPOLATE_RGB = true;
- m_drawFlags |= R_COLOR;
-
- } else if (g0 != g1) {
- INTERPOLATE_RGB = true;
- m_drawFlags |= R_COLOR;
-
- } else if (b0 != b1) {
- INTERPOLATE_RGB = true;
- m_drawFlags |= R_COLOR;
-
- } else {
- // when plain we use the stroke color of the first vertex
- m_stroke = 0xFF000000 |
- ((int)(255*r0) << 16) | ((int)(255*g0) << 8) | (int)(255*b0);
- INTERPOLATE_RGB = false;
- m_drawFlags &= ~R_COLOR;
- }
- }
-
-
- public void setIndex(int index) {
- m_index = index;
- //BLENDER = false;
- if (m_index != -1) {
- //BLENDER = true;
- } else {
- m_index = 0;
- }
- }
-
-
- public void draw() {
- int xi;
- int yi;
- int length;
- boolean visible = true;
-
- if (parent.smooth) {
- SMOOTH = true;
- m_drawFlags |= R_SMOOTH;
-
- } else {
- SMOOTH = false;
- m_drawFlags &= ~R_SMOOTH;
- }
-
- // line hack
- if (parent.hints[NO_FLYING_POO]) {
- float nwidth2 = -SCREEN_WIDTH;
- float nheight2 = -SCREEN_HEIGHT;
- float width2 = SCREEN_WIDTH * 2;
- float height2 = SCREEN_HEIGHT * 2;
- if ((x_array[1] < nwidth2) ||
- (x_array[1] > width2) ||
- (x_array[0] < nwidth2) ||
- (x_array[0] > width2) ||
- (y_array[1] < nheight2) ||
- (y_array[1] > height2) ||
- (y_array[0] < nheight2) ||
- (y_array[0] > height2)) {
- return; // this is a bad line
- }
- }
-
- ///////////////////////////////////////
- // line clipping
- visible = lineClipping();
- if (!visible) {
- return;
- }
-
- ///////////////////////////////////////
- // calculate line values
- int shortLen;
- int longLen;
- boolean yLonger;
- int dt;
-
- yLonger = false;
-
- // HACK for drawing lines left-to-right for rev 0069
- // some kind of bug exists with the line-stepping algorithm
- // that causes strange patterns in the anti-aliasing.
- // [040228 fry]
- //
- // swap rgba as well as the coords.. oops
- // [040712 fry]
- //
- if (x_array[1] < x_array[0]) {
- float t;
-
- t = x_array[1]; x_array[1] = x_array[0]; x_array[0] = t;
- t = y_array[1]; y_array[1] = y_array[0]; y_array[0] = t;
- t = z_array[1]; z_array[1] = z_array[0]; z_array[0] = t;
-
- t = r_array[1]; r_array[1] = r_array[0]; r_array[0] = t;
- t = g_array[1]; g_array[1] = g_array[0]; g_array[0] = t;
- t = b_array[1]; b_array[1] = b_array[0]; b_array[0] = t;
- t = a_array[1]; a_array[1] = a_array[0]; a_array[0] = t;
- }
-
- // important - don't change the casts
- // is needed this way for line drawing algorithm
- longLen = (int)x_array[1] - (int)x_array[0];
- shortLen = (int)y_array[1] - (int)y_array[0];
-
- if (Math.abs(shortLen) > Math.abs(longLen)) {
- int swap = shortLen;
- shortLen = longLen;
- longLen = swap;
- yLonger = true;
- }
-
- // now we sort points so longLen is always positive
- // and we always start drawing from x[0], y[0]
- if (longLen < 0) {
- // swap order
- o0 = 1;
- o1 = 0;
-
- xi = (int) x_array[1];
- yi = (int) y_array[1];
-
- length = -longLen;
-
- } else {
- o0 = 0;
- o1 = 1;
-
- xi = (int) x_array[0];
- yi = (int) y_array[0];
-
- length = longLen;
- }
-
- // calculate dt
- if (length == 0) {
- dt = 0;
- } else {
- dt = (shortLen << 16) / longLen;
- }
-
- m_r0 = r_array[o0];
- m_g0 = g_array[o0];
- m_b0 = b_array[o0];
-
- if (INTERPOLATE_RGB) {
- dr = (r_array[o1] - r_array[o0]) / length;
- dg = (g_array[o1] - g_array[o0]) / length;
- db = (b_array[o1] - b_array[o0]) / length;
- } else {
- dr = 0;
- dg = 0;
- db = 0;
- }
-
- m_a0 = a_array[o0];
-
- if (INTERPOLATE_ALPHA) {
- da = (a_array[o1] - a_array[o0]) / length;
- } else {
- da = 0;
- }
-
- m_z0 = z_array[o0];
- //z0 += -0.001f; // [rocha] ugly fix for z buffer precision
-
- if (INTERPOLATE_Z) {
- dz = (z_array[o1] - z_array[o0]) / length;
- } else {
- dz = 0;
- }
-
- // draw thin points
- if (length == 0) {
- if (INTERPOLATE_ALPHA) {
- drawPoint_alpha(xi, yi);
- } else {
- drawPoint(xi, yi);
- }
- return;
- }
-
- /*
- // draw antialias polygon lines for non stroked polygons
- if (BLENDER && SMOOTH) {
- // fix for endpoints not being drawn
- // [rocha]
- drawPoint_alpha((int)x_array[0], (int)x_array[0]);
- drawPoint_alpha((int)x_array[1], (int)x_array[1]);
-
- drawline_blender(x_array[0], y_array[0], x_array[1], y_array[1]);
- return;
- }
- */
-
- // draw normal strokes
- if (SMOOTH) {
- drawLine_smooth(xi, yi, dt, length, yLonger);
-
- } else {
- if (m_drawFlags == 0) {
- drawLine_plain(xi, yi, dt, length, yLonger);
-
- } else if (m_drawFlags == R_ALPHA) {
- drawLine_plain_alpha(xi, yi, dt, length, yLonger);
-
- } else if (m_drawFlags == R_COLOR) {
- drawLine_color(xi, yi, dt, length, yLonger);
-
- } else if (m_drawFlags == (R_COLOR + R_ALPHA)) {
- drawLine_color_alpha(xi, yi, dt, length, yLonger);
-
- } else if (m_drawFlags == R_SPATIAL) {
- drawLine_plain_spatial(xi, yi, dt, length, yLonger);
-
- } else if (m_drawFlags == (R_SPATIAL + R_ALPHA)) {
- drawLine_plain_alpha_spatial(xi, yi, dt, length, yLonger);
-
- } else if (m_drawFlags == (R_SPATIAL + R_COLOR)) {
- drawLine_color_spatial(xi, yi, dt, length, yLonger);
-
- } else if (m_drawFlags == (R_SPATIAL + R_COLOR + R_ALPHA)) {
- drawLine_color_alpha_spatial(xi, yi, dt, length, yLonger);
- }
- }
- }
-
-
- public boolean lineClipping() {
- // new cohen-sutherland clipping code, as old one was buggy [toxi]
- // get the "dips" for the points to clip
- int code1 = lineClipCode(x_array[0], y_array[0]);
- int code2 = lineClipCode(x_array[1], y_array[1]);
- int dip = code1 | code2;
-
- if ((code1 & code2)!=0) {
-
- return false;
-
- } else if (dip != 0) {
-
- // now calculate the clipped points
- float a0 = 0, a1 = 1, a = 0;
-
- for (int i = 0; i < 4; i++) {
- if (((dip>>i)%2)==1){
- a = lineSlope(x_array[0], y_array[0], x_array[1], y_array[1], i+1);
- if (((code1 >> i) % 2) == 1) {
- a0 = (a>a0)?a:a0; // max(a,a0)
- } else {
- a1 = (a
+ * This code will soon be removed.
+ * Written by Carlos Rocha.
+ */
+public class PLine implements PConstants
+{
+ private int[] m_pixels;
+ private float[] m_zbuffer;
+ //private int[] m_stencil;
+
+ private int m_index;
+
+ static final int R_COLOR = 0x1;
+ static final int R_ALPHA = 0x2;
+ static final int R_SPATIAL = 0x8;
+ static final int R_THICK = 0x4;
+ static final int R_SMOOTH = 0x10;
+
+ private int SCREEN_WIDTH;
+ private int SCREEN_HEIGHT;
+ private int SCREEN_WIDTH1;
+ private int SCREEN_HEIGHT1;
+
+ public boolean INTERPOLATE_RGB;
+ public boolean INTERPOLATE_ALPHA;
+ public boolean INTERPOLATE_Z;
+ public boolean INTERPOLATE_THICK;
+
+ // antialias
+ private boolean SMOOTH;
+
+ // blender
+ //private boolean BLENDER;
+
+ // stroke color
+ private int m_stroke;
+
+ // draw flags
+ public int m_drawFlags;
+
+ // vertex coordinates
+ private float[] x_array;
+ private float[] y_array;
+ private float[] z_array;
+
+ // vertex intensity
+ private float[] r_array;
+ private float[] g_array;
+ private float[] b_array;
+ private float[] a_array;
+
+ // vertex offsets
+ private int o0;
+ private int o1;
+
+ // start values
+ private float m_r0;
+ private float m_g0;
+ private float m_b0;
+ private float m_a0;
+ private float m_z0;
+
+ // deltas
+ private float dz;
+
+ // rgba deltas
+ private float dr;
+ private float dg;
+ private float db;
+ private float da;
+
+ private PGraphics3 parent;
+
+
+ public PLine(PGraphics3 g) {
+ INTERPOLATE_Z = false;
+
+ x_array = new float[2];
+ y_array = new float[2];
+ z_array = new float[2];
+ r_array = new float[2];
+ g_array = new float[2];
+ b_array = new float[2];
+ a_array = new float[2];
+
+ this.parent = g;
+ }
+
+
+ public void reset() {
+ // reset these in case PGraphics was resized
+ SCREEN_WIDTH = parent.width;
+ SCREEN_HEIGHT = parent.height;
+ SCREEN_WIDTH1 = SCREEN_WIDTH-1;
+ SCREEN_HEIGHT1 = SCREEN_HEIGHT-1;
+
+ m_pixels = parent.pixels;
+ //m_stencil = parent.stencil;
+ m_zbuffer = parent.zbuffer;
+
+ // other things to reset
+
+ INTERPOLATE_RGB = false;
+ INTERPOLATE_ALPHA = false;
+ //INTERPOLATE_Z = false;
+ m_drawFlags = 0;
+ m_index = 0;
+ //BLENDER = false;
+ }
+
+
+ public void setVertices(float x0, float y0, float z0,
+ float x1, float y1, float z1) {
+ // [rocha] fixed z drawing, so whenever a line turns on
+ // z interpolation, all the lines are z interpolated
+ if (z0 != z1 || z0!=0.0f || z1!=0.0f || INTERPOLATE_Z) {
+ INTERPOLATE_Z = true;
+ m_drawFlags |= R_SPATIAL;
+ } else {
+ INTERPOLATE_Z = false;
+ m_drawFlags &= ~R_SPATIAL;
+ }
+
+ z_array[0] = z0;
+ z_array[1] = z1;
+
+ x_array[0] = x0;
+ x_array[1] = x1;
+
+ y_array[0] = y0;
+ y_array[1] = y1;
+ }
+
+ public void setIntensities(float r0, float g0, float b0, float a0,
+ float r1, float g1, float b1, float a1) {
+ a_array[0] = (a0 * 253f + 1.0f) * 65536f;
+ a_array[1] = (a1 * 253f + 1.0f) * 65536f;
+
+ // check if we need alpha or not?
+ if ((a0 != 1.0f) || (a1 != 1.0f)) {
+ INTERPOLATE_ALPHA = true;
+ m_drawFlags |= R_ALPHA;
+ } else {
+ INTERPOLATE_ALPHA = false;
+ m_drawFlags &= ~R_ALPHA;
+ }
+
+ // extra scaling added to prevent color "overflood" due to rounding errors
+ r_array[0] = (r0 * 253f + 1.0f) * 65536f;
+ r_array[1] = (r1 * 253f + 1.0f) * 65536f;
+
+ g_array[0] = (g0 * 253f + 1.0f) * 65536f;
+ g_array[1] = (g1 * 253f + 1.0f) * 65536f;
+
+ b_array[0] = (b0 * 253f + 1.0f) * 65536f;
+ b_array[1] = (b1 * 253f + 1.0f) * 65536f;
+
+ // check if we need to interpolate the intensity values
+ if (r0 != r1) {
+ INTERPOLATE_RGB = true;
+ m_drawFlags |= R_COLOR;
+
+ } else if (g0 != g1) {
+ INTERPOLATE_RGB = true;
+ m_drawFlags |= R_COLOR;
+
+ } else if (b0 != b1) {
+ INTERPOLATE_RGB = true;
+ m_drawFlags |= R_COLOR;
+
+ } else {
+ // when plain we use the stroke color of the first vertex
+ m_stroke = 0xFF000000 |
+ ((int)(255*r0) << 16) | ((int)(255*g0) << 8) | (int)(255*b0);
+ INTERPOLATE_RGB = false;
+ m_drawFlags &= ~R_COLOR;
+ }
+ }
+
+
+ public void setIndex(int index) {
+ m_index = index;
+ //BLENDER = false;
+ if (m_index != -1) {
+ //BLENDER = true;
+ } else {
+ m_index = 0;
+ }
+ }
+
+
+ public void draw() {
+ int xi;
+ int yi;
+ int length;
+ boolean visible = true;
+
+ if (parent.smooth) {
+ SMOOTH = true;
+ m_drawFlags |= R_SMOOTH;
+
+ } else {
+ SMOOTH = false;
+ m_drawFlags &= ~R_SMOOTH;
+ }
+
+ // line hack
+ if (parent.hints[NO_FLYING_POO]) {
+ float nwidth2 = -SCREEN_WIDTH;
+ float nheight2 = -SCREEN_HEIGHT;
+ float width2 = SCREEN_WIDTH * 2;
+ float height2 = SCREEN_HEIGHT * 2;
+ if ((x_array[1] < nwidth2) ||
+ (x_array[1] > width2) ||
+ (x_array[0] < nwidth2) ||
+ (x_array[0] > width2) ||
+ (y_array[1] < nheight2) ||
+ (y_array[1] > height2) ||
+ (y_array[0] < nheight2) ||
+ (y_array[0] > height2)) {
+ return; // this is a bad line
+ }
+ }
+
+ ///////////////////////////////////////
+ // line clipping
+ visible = lineClipping();
+ if (!visible) {
+ return;
+ }
+
+ ///////////////////////////////////////
+ // calculate line values
+ int shortLen;
+ int longLen;
+ boolean yLonger;
+ int dt;
+
+ yLonger = false;
+
+ // HACK for drawing lines left-to-right for rev 0069
+ // some kind of bug exists with the line-stepping algorithm
+ // that causes strange patterns in the anti-aliasing.
+ // [040228 fry]
+ //
+ // swap rgba as well as the coords.. oops
+ // [040712 fry]
+ //
+ if (x_array[1] < x_array[0]) {
+ float t;
+
+ t = x_array[1]; x_array[1] = x_array[0]; x_array[0] = t;
+ t = y_array[1]; y_array[1] = y_array[0]; y_array[0] = t;
+ t = z_array[1]; z_array[1] = z_array[0]; z_array[0] = t;
+
+ t = r_array[1]; r_array[1] = r_array[0]; r_array[0] = t;
+ t = g_array[1]; g_array[1] = g_array[0]; g_array[0] = t;
+ t = b_array[1]; b_array[1] = b_array[0]; b_array[0] = t;
+ t = a_array[1]; a_array[1] = a_array[0]; a_array[0] = t;
+ }
+
+ // important - don't change the casts
+ // is needed this way for line drawing algorithm
+ longLen = (int)x_array[1] - (int)x_array[0];
+ shortLen = (int)y_array[1] - (int)y_array[0];
+
+ if (Math.abs(shortLen) > Math.abs(longLen)) {
+ int swap = shortLen;
+ shortLen = longLen;
+ longLen = swap;
+ yLonger = true;
+ }
+
+ // now we sort points so longLen is always positive
+ // and we always start drawing from x[0], y[0]
+ if (longLen < 0) {
+ // swap order
+ o0 = 1;
+ o1 = 0;
+
+ xi = (int) x_array[1];
+ yi = (int) y_array[1];
+
+ length = -longLen;
+
+ } else {
+ o0 = 0;
+ o1 = 1;
+
+ xi = (int) x_array[0];
+ yi = (int) y_array[0];
+
+ length = longLen;
+ }
+
+ // calculate dt
+ if (length == 0) {
+ dt = 0;
+ } else {
+ dt = (shortLen << 16) / longLen;
+ }
+
+ m_r0 = r_array[o0];
+ m_g0 = g_array[o0];
+ m_b0 = b_array[o0];
+
+ if (INTERPOLATE_RGB) {
+ dr = (r_array[o1] - r_array[o0]) / length;
+ dg = (g_array[o1] - g_array[o0]) / length;
+ db = (b_array[o1] - b_array[o0]) / length;
+ } else {
+ dr = 0;
+ dg = 0;
+ db = 0;
+ }
+
+ m_a0 = a_array[o0];
+
+ if (INTERPOLATE_ALPHA) {
+ da = (a_array[o1] - a_array[o0]) / length;
+ } else {
+ da = 0;
+ }
+
+ m_z0 = z_array[o0];
+ //z0 += -0.001f; // [rocha] ugly fix for z buffer precision
+
+ if (INTERPOLATE_Z) {
+ dz = (z_array[o1] - z_array[o0]) / length;
+ } else {
+ dz = 0;
+ }
+
+ // draw thin points
+ if (length == 0) {
+ if (INTERPOLATE_ALPHA) {
+ drawPoint_alpha(xi, yi);
+ } else {
+ drawPoint(xi, yi);
+ }
+ return;
+ }
+
+ /*
+ // draw antialias polygon lines for non stroked polygons
+ if (BLENDER && SMOOTH) {
+ // fix for endpoints not being drawn
+ // [rocha]
+ drawPoint_alpha((int)x_array[0], (int)x_array[0]);
+ drawPoint_alpha((int)x_array[1], (int)x_array[1]);
+
+ drawline_blender(x_array[0], y_array[0], x_array[1], y_array[1]);
+ return;
+ }
+ */
+
+ // draw normal strokes
+ if (SMOOTH) {
+ drawLine_smooth(xi, yi, dt, length, yLonger);
+
+ } else {
+ if (m_drawFlags == 0) {
+ drawLine_plain(xi, yi, dt, length, yLonger);
+
+ } else if (m_drawFlags == R_ALPHA) {
+ drawLine_plain_alpha(xi, yi, dt, length, yLonger);
+
+ } else if (m_drawFlags == R_COLOR) {
+ drawLine_color(xi, yi, dt, length, yLonger);
+
+ } else if (m_drawFlags == (R_COLOR + R_ALPHA)) {
+ drawLine_color_alpha(xi, yi, dt, length, yLonger);
+
+ } else if (m_drawFlags == R_SPATIAL) {
+ drawLine_plain_spatial(xi, yi, dt, length, yLonger);
+
+ } else if (m_drawFlags == (R_SPATIAL + R_ALPHA)) {
+ drawLine_plain_alpha_spatial(xi, yi, dt, length, yLonger);
+
+ } else if (m_drawFlags == (R_SPATIAL + R_COLOR)) {
+ drawLine_color_spatial(xi, yi, dt, length, yLonger);
+
+ } else if (m_drawFlags == (R_SPATIAL + R_COLOR + R_ALPHA)) {
+ drawLine_color_alpha_spatial(xi, yi, dt, length, yLonger);
+ }
+ }
+ }
+
+
+ public boolean lineClipping() {
+ // new cohen-sutherland clipping code, as old one was buggy [toxi]
+ // get the "dips" for the points to clip
+ int code1 = lineClipCode(x_array[0], y_array[0]);
+ int code2 = lineClipCode(x_array[1], y_array[1]);
+ int dip = code1 | code2;
+
+ if ((code1 & code2)!=0) {
+
+ return false;
+
+ } else if (dip != 0) {
+
+ // now calculate the clipped points
+ float a0 = 0, a1 = 1, a = 0;
+
+ for (int i = 0; i < 4; i++) {
+ if (((dip>>i)%2)==1){
+ a = lineSlope(x_array[0], y_array[0], x_array[1], y_array[1], i+1);
+ if (((code1 >> i) % 2) == 1) {
+ a0 = (a>a0)?a:a0; // max(a,a0)
+ } else {
+ a1 = (a
- * Likely to be removed before 1.0 as it's no longer particularly used.
- */
-public class PPolygon implements PConstants {
- static final int DEFAULT_SIZE = 64; // this is needed for spheres
- float vertices[][] = new float[DEFAULT_SIZE][VERTEX_FIELD_COUNT];
- int vertexCount;
-
- // really this is "debug" but..
- static final boolean FRY = false;
-
- // after some fiddling, this seems to produce the best results
- //static final int ZBUFFER_MIN_COVERAGE = 204;
-
- float r[] = new float[DEFAULT_SIZE]; // storage used by incrementalize
- float dr[] = new float[DEFAULT_SIZE];
- float l[] = new float[DEFAULT_SIZE]; // more storage for incrementalize
- float dl[] = new float[DEFAULT_SIZE];
- float sp[] = new float[DEFAULT_SIZE]; // temporary storage for scanline
- float sdp[] = new float[DEFAULT_SIZE];
-
- // color and xyz are always interpolated
- boolean interpX;
- boolean interpZ;
- boolean interpUV; // is this necessary? could just check timage != null
- boolean interpARGB;
-
- int rgba;
- int r2, g2, b2, a2, a2orig;
-
- boolean noDepthTest;
-
- PGraphics parent;
- int pixels[];
-
- // the parent's width/height,
- // or if smooth is enabled, parent's w/h scaled
- // up by the smooth dimension
- int width, height;
- int width1, height1;
-
- PImage timage;
- int tpixels[];
- int theight, twidth;
- int theight1, twidth1;
- int tformat;
-
- // temp fix to behave like SMOOTH_IMAGES
- boolean texture_smooth;
-
- // for anti-aliasing
- static final int SUBXRES = 8;
- static final int SUBXRES1 = 7;
- static final int SUBYRES = 8;
- static final int SUBYRES1 = 7;
- static final int MAX_COVERAGE = SUBXRES * SUBYRES;
-
- boolean smooth;
- int firstModY;
- int lastModY;
- int lastY;
- int aaleft[] = new int[SUBYRES];
- int aaright[] = new int[SUBYRES];
- int aaleftmin, aarightmin;
- int aaleftmax, aarightmax;
- int aaleftfull, aarightfull;
-
- final private int MODYRES(int y) {
- return (y & SUBYRES1);
- }
-
-
- public PPolygon(PGraphics iparent) {
- parent = iparent;
- reset(0);
- }
-
-
- public void reset(int count) {
- vertexCount = count;
- interpX = true;
- interpZ = true;
- interpUV = false;
- interpARGB = true;
- timage = null;
- }
-
-
- public float[] nextVertex() {
- if (vertexCount == vertices.length) {
- //parent.message(CHATTER, "re-allocating for " +
- // (vertexCount*2) + " vertices");
- float temp[][] = new float[vertexCount<<1][VERTEX_FIELD_COUNT];
- System.arraycopy(vertices, 0, temp, 0, vertexCount);
- vertices = temp;
-
- r = new float[vertices.length];
- dr = new float[vertices.length];
- l = new float[vertices.length];
- dl = new float[vertices.length];
- sp = new float[vertices.length];
- sdp = new float[vertices.length];
- }
- return vertices[vertexCount++]; // returns v[0], sets vc to 1
- }
-
-
- /**
- * Return true if this vertex is redundant. If so, will also
- * decrement the vertex count.
- */
- /*
- public boolean redundantVertex(float x, float y, float z) {
- // because vertexCount will be 2 when setting vertex[1]
- if (vertexCount < 2) return false;
-
- // vertexCount-1 is the current vertex that would be used
- // vertexCount-2 would be the previous feller
- if ((Math.abs(vertices[vertexCount-2][MX] - x) < EPSILON) &&
- (Math.abs(vertices[vertexCount-2][MY] - y) < EPSILON) &&
- (Math.abs(vertices[vertexCount-2][MZ] - z) < EPSILON)) {
- vertexCount--;
- return true;
- }
- return false;
- }
- */
-
-
- public void texture(PImage image) {
- this.timage = image;
- this.tpixels = image.pixels;
- this.twidth = image.width;
- this.theight = image.height;
- this.tformat = image.format;
-
- twidth1 = twidth - 1;
- theight1 = theight - 1;
- interpUV = true;
- }
-
-
- public void render() {
- if (vertexCount < 3) return;
-
- // these may have changed due to a resize()
- // so they should be refreshed here
- pixels = parent.pixels;
- //zbuffer = parent.zbuffer;
-
- noDepthTest = parent.hints[DISABLE_DEPTH_TEST];
- smooth = parent.smooth;
-
- // by default, text turns on smooth for the textures
- // themselves. but this should be shut off if the hint
- // for DISABLE_TEXT_SMOOTH is set.
- texture_smooth = (//parent.drawing_text &&
- !parent.hints[DISABLE_TEXT_SMOOTH]);
-
- width = smooth ? parent.width*SUBXRES : parent.width;
- height = smooth ? parent.height*SUBYRES : parent.height;
-
- width1 = width - 1;
- height1 = height - 1;
-
- if (!interpARGB) {
- r2 = (int) (vertices[0][R] * 255);
- g2 = (int) (vertices[0][G] * 255);
- b2 = (int) (vertices[0][B] * 255);
- a2 = (int) (vertices[0][A] * 255);
- a2orig = a2; // save an extra copy
- rgba = 0xff000000 | (r2 << 16) | (g2 << 8) | b2;
- }
-
- for (int i = 0; i < vertexCount; i++) {
- r[i] = 0; dr[i] = 0; l[i] = 0; dl[i] = 0;
- }
-
- // hack to not make polygons fly into the screen
- if (parent.hints[NO_FLYING_POO]) {
- float nwidth2 = -width * 2;
- float nheight2 = -height * 2;
- float width2 = width * 2;
- float height2 = height * 2;
- for (int i = 0; i < vertexCount; i++) {
- if ((vertices[i][X] < nwidth2) ||
- (vertices[i][X] > width2) ||
- (vertices[i][Y] < nheight2) ||
- (vertices[i][Y] > height2)) {
- return; // this is a bad poly
- }
- }
- }
-
- if (smooth) {
- for (int i = 0; i < vertexCount; i++) {
- vertices[i][X] *= SUBXRES;
- vertices[i][Y] *= SUBYRES;
- }
- firstModY = -1;
- }
-
- // find top vertex (y is zero at top, higher downwards)
- int topi = 0;
- float ymin = vertices[0][Y];
- float ymax = vertices[0][Y]; // fry 031001
- for (int i = 1; i < vertexCount; i++) {
- if (vertices[i][Y] < ymin) {
- ymin = vertices[i][Y];
- topi = i;
- }
- if (vertices[i][Y] > ymax) ymax = vertices[i][Y];
- }
-
- // the last row is an exceptional case, because there won't
- // necessarily be 8 rows of subpixel lines that will force
- // the final line to render. so instead, the algo keeps track
- // of the lastY (in subpixel resolution) that will be rendered
- // and that will force a scanline to happen the same as
- // every eighth in the other situations
- //lastY = -1; // fry 031001
- lastY = (int) (ymax - 0.5f); // global to class bc used by other fxns
-
- int lefti = topi; // li, index of left vertex
- int righti = topi; // ri, index of right vertex
- int y = (int) (ymin + 0.5f); // current scan line
- int lefty = y - 1; // lower end of left edge
- int righty = y - 1; // lower end of right edge
-
- interpX = true;
-
- int remaining = vertexCount;
-
- // scan in y, activating new edges on left & right
- // as scan line passes over new vertices
- while (remaining > 0) {
- // advance left edge?
- while ((lefty <= y) && (remaining > 0)) {
- remaining--;
- // step ccw down left side
- int i = (lefti != 0) ? (lefti-1) : (vertexCount-1);
- incrementalize_y(vertices[lefti], vertices[i], l, dl, y);
- lefty = (int) (vertices[i][Y] + 0.5f);
- lefti = i;
- }
-
- // advance right edge?
- while ((righty <= y) && (remaining > 0)) {
- remaining--;
- // step cw down right edge
- int i = (righti != vertexCount-1) ? (righti + 1) : 0;
- incrementalize_y(vertices[righti], vertices[i], r, dr, y);
- righty = (int) (vertices[i][Y] + 0.5f);
- righti = i;
- }
-
- // do scanlines till end of l or r edge
- while (y < lefty && y < righty) {
- // this doesn't work because it's not always set here
- //if (remaining == 0) {
- //lastY = (lefty < righty) ? lefty-1 : righty-1;
- //System.out.println("lastY is " + lastY);
- //}
-
- if ((y >= 0) && (y < height)) {
- //try { // hopefully this bug is fixed
- if (l[X] <= r[X]) scanline(y, l, r);
- else scanline(y, r, l);
- //} catch (ArrayIndexOutOfBoundsException e) {
- //e.printStackTrace();
- //}
- }
- y++;
- // this increment probably needs to be different
- // UV and RGB shouldn't be incremented until line is emitted
- increment(l, dl);
- increment(r, dr);
- }
- }
- //if (smooth) {
- //System.out.println("y/lasty/lastmody = " + y + " " + lastY + " " + lastModY);
- //}
- }
-
-
- public void unexpand() {
- if (smooth) {
- for (int i = 0; i < vertexCount; i++) {
- vertices[i][X] /= SUBXRES;
- vertices[i][Y] /= SUBYRES;
- }
- }
- }
-
-
- private void scanline(int y, float l[], float r[]) {
- //System.out.println("scanline " + y);
- for (int i = 0; i < vertexCount; i++) { // should be moved later
- sp[i] = 0; sdp[i] = 0;
- }
-
- // this rounding doesn't seem to be relevant with smooth
- int lx = (int) (l[X] + 0.49999f); // ceil(l[X]-.5);
- if (lx < 0) lx = 0;
- int rx = (int) (r[X] - 0.5f);
- if (rx > width1) rx = width1;
-
- if (lx > rx) return;
-
- if (smooth) {
- int mody = MODYRES(y);
-
- aaleft[mody] = lx;
- aaright[mody] = rx;
-
- if (firstModY == -1) {
- firstModY = mody;
- aaleftmin = lx; aaleftmax = lx;
- aarightmin = rx; aarightmax = rx;
-
- } else {
- if (aaleftmin > aaleft[mody]) aaleftmin = aaleft[mody];
- if (aaleftmax < aaleft[mody]) aaleftmax = aaleft[mody];
- if (aarightmin > aaright[mody]) aarightmin = aaright[mody];
- if (aarightmax < aaright[mody]) aarightmax = aaright[mody];
- }
-
- lastModY = mody; // moved up here (before the return) 031001
- // not the eighth (or lastY) line, so not scanning this time
- if ((mody != SUBYRES1) && (y != lastY)) return;
- //lastModY = mody; // eeK! this was missing
- //return;
-
- //if (y == lastY) {
- //System.out.println("y is lasty");
- //}
- //lastModY = mody;
- aaleftfull = aaleftmax/SUBXRES + 1;
- aarightfull = aarightmin/SUBXRES - 1;
- }
-
- // this is the setup, based on lx
- incrementalize_x(l, r, sp, sdp, lx);
-
- // scan in x, generating pixels
- // using parent.width to get actual pixel index
- // rather than scaled by smooth factor
- int offset = smooth ? parent.width * (y / SUBYRES) : parent.width*y;
-
- int truelx = 0, truerx = 0;
- if (smooth) {
- truelx = lx / SUBXRES;
- truerx = (rx + SUBXRES1) / SUBXRES;
-
- lx = aaleftmin / SUBXRES;
- rx = (aarightmax + SUBXRES1) / SUBXRES;
- if (lx < 0) lx = 0;
- if (rx > parent.width1) rx = parent.width1;
- }
-
- interpX = false;
- int tr, tg, tb, ta;
-
- for (int x = lx; x <= rx; x++) {
- // added == because things on same plane weren't replacing each other
- // makes for strangeness in 3D, but totally necessary for 2D
- //if (noDepthTest || (sp[Z] <= zbuffer[offset+x])) {
- if (true) {
-
- // map texture based on U, V coords in sp[U] and sp[V]
- if (interpUV) {
- int tu = (int)sp[U];
- int tv = (int)sp[V];
-
- if (tu > twidth1) tu = twidth1;
- if (tv > theight1) tv = theight1;
- if (tu < 0) tu = 0;
- if (tv < 0) tv = 0;
-
- int txy = tv*twidth + tu;
-
- if (smooth || texture_smooth) {
- //if (FRY) System.out.println("sp u v = " + sp[U] + " " + sp[V]);
- //System.out.println("sp u v = " + sp[U] + " " + sp[V]);
- // tuf1/tvf1 is the amount of coverage for the adjacent
- // pixel, which is the decimal percentage.
- int tuf1 = (int) (255f * (sp[U] - (float)tu));
- int tvf1 = (int) (255f * (sp[V] - (float)tv));
-
- // the closer sp[U or V] is to the decimal being zero
- // the more coverage it should get of the original pixel
- int tuf = 255 - tuf1;
- int tvf = 255 - tvf1;
-
- // this code sucks! filled with bugs and slow as hell!
- int pixel00 = tpixels[txy];
- int pixel01 = (tv < theight1) ?
- tpixels[txy + twidth] : tpixels[txy];
- int pixel10 = (tu < twidth1) ?
- tpixels[txy + 1] : tpixels[txy];
- int pixel11 = ((tv < theight1) && (tu < twidth1)) ?
- tpixels[txy + twidth + 1] : tpixels[txy];
-
- int p00, p01, p10, p11;
- int px0, px1; //, pxy;
-
- if (tformat == ALPHA) {
- px0 = (pixel00*tuf + pixel10*tuf1) >> 8;
- px1 = (pixel01*tuf + pixel11*tuf1) >> 8;
- ta = (((px0*tvf + px1*tvf1) >> 8) *
- (interpARGB ? ((int) (sp[A]*255)) : a2orig)) >> 8;
-
- } else if (tformat == ARGB) {
- p00 = (pixel00 >> 24) & 0xff;
- p01 = (pixel01 >> 24) & 0xff;
- p10 = (pixel10 >> 24) & 0xff;
- p11 = (pixel11 >> 24) & 0xff;
-
- px0 = (p00*tuf + p10*tuf1) >> 8;
- px1 = (p01*tuf + p11*tuf1) >> 8;
- ta = (((px0*tvf + px1*tvf1) >> 8) *
- (interpARGB ? ((int) (sp[A]*255)) : a2orig)) >> 8;
-
- } else { // RGB image, no alpha
- ta = interpARGB ? ((int) (sp[A]*255)) : a2orig;
- }
-
- if ((tformat == RGB) || (tformat == ARGB)) {
- p00 = (pixel00 >> 16) & 0xff; // red
- p01 = (pixel01 >> 16) & 0xff;
- p10 = (pixel10 >> 16) & 0xff;
- p11 = (pixel11 >> 16) & 0xff;
-
- px0 = (p00*tuf + p10*tuf1) >> 8;
- px1 = (p01*tuf + p11*tuf1) >> 8;
- tr = (((px0*tvf + px1*tvf1) >> 8) *
- (interpARGB ? ((int) sp[R]*255) : r2)) >> 8;
-
-
- p00 = (pixel00 >> 8) & 0xff; // green
- p01 = (pixel01 >> 8) & 0xff;
- p10 = (pixel10 >> 8) & 0xff;
- p11 = (pixel11 >> 8) & 0xff;
-
- px0 = (p00*tuf + p10*tuf1) >> 8;
- px1 = (p01*tuf + p11*tuf1) >> 8;
- tg = (((px0*tvf + px1*tvf1) >> 8) *
- (interpARGB ? ((int) sp[G]*255) : g2)) >> 8;
-
-
- p00 = pixel00 & 0xff; // blue
- p01 = pixel01 & 0xff;
- p10 = pixel10 & 0xff;
- p11 = pixel11 & 0xff;
-
- px0 = (p00*tuf + p10*tuf1) >> 8;
- px1 = (p01*tuf + p11*tuf1) >> 8;
- tb = (((px0*tvf + px1*tvf1) >> 8) *
- (interpARGB ? ((int) sp[B]*255) : b2)) >> 8;
-
- } else { // alpha image, only use current fill color
- if (interpARGB) {
- tr = (int) (sp[R] * 255);
- tg = (int) (sp[G] * 255);
- tb = (int) (sp[B] * 255);
-
- } else {
- tr = r2;
- tg = g2;
- tb = b2;
- }
- }
-
- // get coverage for pixel if smooth
- // checks smooth again here because of
- // hints[SMOOTH_IMAGES] used up above
- int weight = smooth ? coverage(x) : 255;
- if (weight != 255) ta = ta*weight >> 8;
-
- } else { // no smooth, just get the pixels
- int tpixel = tpixels[txy];
-
- // TODO i doubt splitting these guys really gets us
- // all that much speed.. is it worth it?
- if (tformat == ALPHA) {
- ta = tpixel;
-
- if (interpARGB) {
- tr = (int) sp[R]*255;
- tg = (int) sp[G]*255;
- tb = (int) sp[B]*255;
- if (sp[A] != 1) {
- ta = (((int) sp[A]*255) * ta) >> 8;
- }
-
- } else {
- tr = r2;
- tg = g2;
- tb = b2;
- ta = (a2orig * ta) >> 8;
- }
-
- } else { // RGB or ARGB
- ta = (tformat == RGB) ? 255 : (tpixel >> 24) & 0xff;
-
- if (interpARGB) {
- tr = (((int) sp[R]*255) * ((tpixel >> 16) & 0xff)) >> 8;
- tg = (((int) sp[G]*255) * ((tpixel >> 8) & 0xff)) >> 8;
- tb = (((int) sp[B]*255) * ((tpixel) & 0xff)) >> 8;
- ta = (((int) sp[A]*255) * ta) >> 8;
-
- } else {
- tr = (r2 * ((tpixel >> 16) & 0xff)) >> 8;
- tg = (g2 * ((tpixel >> 8) & 0xff)) >> 8;
- tb = (b2 * ((tpixel) & 0xff)) >> 8;
- ta = (a2orig * ta) >> 8;
- }
- }
- }
-
- if ((ta == 254) || (ta == 255)) { // if (ta & 0xf8) would be good
- // no need to blend
- pixels[offset+x] = 0xff000000 | (tr << 16) | (tg << 8) | tb;
- //zbuffer[offset+x] = sp[Z];
-
- } else {
- // blend with pixel on screen
- int a1 = 255-ta;
- int r1 = (pixels[offset+x] >> 16) & 0xff;
- int g1 = (pixels[offset+x] >> 8) & 0xff;
- int b1 = (pixels[offset+x]) & 0xff;
-
- pixels[offset+x] = 0xff000000 |
- (((tr*ta + r1*a1) >> 8) << 16) |
- ((tg*ta + g1*a1) & 0xff00) |
- ((tb*ta + b1*a1) >> 8);
- //if (ta > ZBUFFER_MIN_COVERAGE) zbuffer[offset+x] = sp[Z];
- }
-
- } else { // no image applied
- int weight = smooth ? coverage(x) : 255;
-
- if (interpARGB) {
- r2 = (int) (sp[R] * 255);
- g2 = (int) (sp[G] * 255);
- b2 = (int) (sp[B] * 255);
- if (sp[A] != 1) weight = (weight * ((int) (sp[A] * 255))) >> 8;
- if (weight == 255) {
- rgba = 0xff000000 | (r2 << 16) | (g2 << 8) | b2;
- }
- } else {
- if (a2orig != 255) weight = (weight * a2orig) >> 8;
- }
-
- if (weight == 255) {
- // no blend, no aa, just the rgba
- pixels[offset+x] = rgba;
- //zbuffer[offset+x] = sp[Z];
-
- } else {
- int r1 = (pixels[offset+x] >> 16) & 0xff;
- int g1 = (pixels[offset+x] >> 8) & 0xff;
- int b1 = (pixels[offset+x]) & 0xff;
- a2 = weight;
-
- int a1 = 255 - a2;
- pixels[offset+x] = (0xff000000 |
- ((r1*a1 + r2*a2) >> 8) << 16 |
- // use & instead of >> and << below
- ((g1*a1 + g2*a2) >> 8) << 8 |
- ((b1*a1 + b2*a2) >> 8));
-
- //if (a2 > ZBUFFER_MIN_COVERAGE) zbuffer[offset+x] = sp[Z];
- }
- }
- }
- // if smooth enabled, don't increment values
- // for the pixel in the stretch out version
- // of the scanline used to get smooth edges.
- if (!smooth || ((x >= truelx) && (x <= truerx))) {
- increment(sp, sdp);
- }
- }
- firstModY = -1;
- interpX = true;
- }
-
-
- // x is in screen, not huge 8x coordinates
- private int coverage(int x) {
- if ((x >= aaleftfull) && (x <= aarightfull) &&
- // important since not all SUBYRES lines may have been covered
- (firstModY == 0) && (lastModY == SUBYRES1)) {
- return 255;
- }
-
- int pixelLeft = x*SUBXRES; // huh?
- int pixelRight = pixelLeft + 8;
-
- int amt = 0;
- for (int i = firstModY; i <= lastModY; i++) {
- if ((aaleft[i] > pixelRight) || (aaright[i] < pixelLeft)) {
- continue;
- }
- // does this need a +1 ?
- amt += ((aaright[i] < pixelRight ? aaright[i] : pixelRight) -
- (aaleft[i] > pixelLeft ? aaleft[i] : pixelLeft));
- }
- amt <<= 2;
- return (amt == 256) ? 255 : amt;
- }
-
-
- private void incrementalize_y(float p1[], float p2[],
- float p[], float dp[], int y) {
- float delta = p2[Y] - p1[Y];
- if (delta == 0) delta = ONE;
- float fraction = y + HALF - p1[Y];
-
- if (interpX) {
- dp[X] = (p2[X] - p1[X]) / delta;
- p[X] = p1[X] + dp[X] * fraction;
- }
- if (interpZ) {
- dp[Z] = (p2[Z] - p1[Z]) / delta;
- p[Z] = p1[Z] + dp[Z] * fraction;
- }
-
- if (interpARGB) {
- dp[R] = (p2[R] - p1[R]) / delta;
- dp[G] = (p2[G] - p1[G]) / delta;
- dp[B] = (p2[B] - p1[B]) / delta;
- dp[A] = (p2[A] - p1[A]) / delta;
- p[R] = p1[R] + dp[R] * fraction;
- p[G] = p1[G] + dp[G] * fraction;
- p[B] = p1[B] + dp[B] * fraction;
- p[A] = p1[A] + dp[A] * fraction;
- }
-
- if (interpUV) {
- dp[U] = (p2[U] - p1[U]) / delta;
- dp[V] = (p2[V] - p1[V]) / delta;
-
- //if (smooth) {
- //p[U] = p1[U]; //+ dp[U] * fraction;
- //p[V] = p1[V]; //+ dp[V] * fraction;
-
- //} else {
- p[U] = p1[U] + dp[U] * fraction;
- p[V] = p1[V] + dp[V] * fraction;
- //}
- if (FRY) System.out.println("inc y p[U] p[V] = " + p[U] + " " + p[V]);
- }
- }
-
-
- private void incrementalize_x(float p1[], float p2[],
- float p[], float dp[], int x) {
- float delta = p2[X] - p1[X];
- if (delta == 0) delta = ONE;
- float fraction = x + HALF - p1[X];
- if (smooth) {
- delta /= SUBXRES;
- fraction /= SUBXRES;
- }
-
- if (interpX) {
- dp[X] = (p2[X] - p1[X]) / delta;
- p[X] = p1[X] + dp[X] * fraction;
- }
- if (interpZ) {
- dp[Z] = (p2[Z] - p1[Z]) / delta;
- p[Z] = p1[Z] + dp[Z] * fraction;
- }
-
- if (interpARGB) {
- dp[R] = (p2[R] - p1[R]) / delta;
- dp[G] = (p2[G] - p1[G]) / delta;
- dp[B] = (p2[B] - p1[B]) / delta;
- dp[A] = (p2[A] - p1[A]) / delta;
- p[R] = p1[R] + dp[R] * fraction;
- p[G] = p1[G] + dp[G] * fraction;
- p[B] = p1[B] + dp[B] * fraction;
- p[A] = p1[A] + dp[A] * fraction;
- }
-
- if (interpUV) {
- if (FRY) System.out.println("delta, frac = " + delta + ", " + fraction);
- dp[U] = (p2[U] - p1[U]) / delta;
- dp[V] = (p2[V] - p1[V]) / delta;
-
- //if (smooth) {
- //p[U] = p1[U];
- // offset for the damage that will be done by the
- // 8 consecutive calls to scanline
- // agh.. this won't work b/c not always 8 calls before render
- // maybe lastModY - firstModY + 1 instead?
- if (FRY) System.out.println("before inc x p[V] = " + p[V] + " " + p1[V] + " " + p2[V]);
- //p[V] = p1[V] - SUBXRES1 * fraction;
-
- //} else {
- p[U] = p1[U] + dp[U] * fraction;
- p[V] = p1[V] + dp[V] * fraction;
- //}
- }
- }
-
-
- private void increment(float p[], float dp[]) {
- if (interpX) p[X] += dp[X];
- if (interpZ) p[Z] += dp[Z];
-
- if (interpARGB) {
- p[R] += dp[R];
- p[G] += dp[G];
- p[B] += dp[B];
- p[A] += dp[A];
- }
-
- if (interpUV) {
- if (FRY) System.out.println("increment() " + p[V] + " " + dp[V]);
- p[U] += dp[U];
- p[V] += dp[V];
- }
- }
-}
+/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ Part of the Processing project - http://processing.org
+
+ Copyright (c) 2004-06 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;
+
+
+/**
+ * zbuffer polygon rendering object for PGraphics.
+ *
+ * Likely to be removed before 1.0 as it's no longer particularly used.
+ */
+public class PPolygon implements PConstants {
+ static final int DEFAULT_SIZE = 64; // this is needed for spheres
+ float vertices[][] = new float[DEFAULT_SIZE][VERTEX_FIELD_COUNT];
+ int vertexCount;
+
+ // really this is "debug" but..
+ static final boolean FRY = false;
+
+ // after some fiddling, this seems to produce the best results
+ //static final int ZBUFFER_MIN_COVERAGE = 204;
+
+ float r[] = new float[DEFAULT_SIZE]; // storage used by incrementalize
+ float dr[] = new float[DEFAULT_SIZE];
+ float l[] = new float[DEFAULT_SIZE]; // more storage for incrementalize
+ float dl[] = new float[DEFAULT_SIZE];
+ float sp[] = new float[DEFAULT_SIZE]; // temporary storage for scanline
+ float sdp[] = new float[DEFAULT_SIZE];
+
+ // color and xyz are always interpolated
+ boolean interpX;
+ boolean interpZ;
+ boolean interpUV; // is this necessary? could just check timage != null
+ boolean interpARGB;
+
+ int rgba;
+ int r2, g2, b2, a2, a2orig;
+
+ boolean noDepthTest;
+
+ PGraphics parent;
+ int pixels[];
+
+ // the parent's width/height,
+ // or if smooth is enabled, parent's w/h scaled
+ // up by the smooth dimension
+ int width, height;
+ int width1, height1;
+
+ PImage timage;
+ int tpixels[];
+ int theight, twidth;
+ int theight1, twidth1;
+ int tformat;
+
+ // temp fix to behave like SMOOTH_IMAGES
+ boolean texture_smooth;
+
+ // for anti-aliasing
+ static final int SUBXRES = 8;
+ static final int SUBXRES1 = 7;
+ static final int SUBYRES = 8;
+ static final int SUBYRES1 = 7;
+ static final int MAX_COVERAGE = SUBXRES * SUBYRES;
+
+ boolean smooth;
+ int firstModY;
+ int lastModY;
+ int lastY;
+ int aaleft[] = new int[SUBYRES];
+ int aaright[] = new int[SUBYRES];
+ int aaleftmin, aarightmin;
+ int aaleftmax, aarightmax;
+ int aaleftfull, aarightfull;
+
+ final private int MODYRES(int y) {
+ return (y & SUBYRES1);
+ }
+
+
+ public PPolygon(PGraphics iparent) {
+ parent = iparent;
+ reset(0);
+ }
+
+
+ public void reset(int count) {
+ vertexCount = count;
+ interpX = true;
+ interpZ = true;
+ interpUV = false;
+ interpARGB = true;
+ timage = null;
+ }
+
+
+ public float[] nextVertex() {
+ if (vertexCount == vertices.length) {
+ //parent.message(CHATTER, "re-allocating for " +
+ // (vertexCount*2) + " vertices");
+ float temp[][] = new float[vertexCount<<1][VERTEX_FIELD_COUNT];
+ System.arraycopy(vertices, 0, temp, 0, vertexCount);
+ vertices = temp;
+
+ r = new float[vertices.length];
+ dr = new float[vertices.length];
+ l = new float[vertices.length];
+ dl = new float[vertices.length];
+ sp = new float[vertices.length];
+ sdp = new float[vertices.length];
+ }
+ return vertices[vertexCount++]; // returns v[0], sets vc to 1
+ }
+
+
+ /**
+ * Return true if this vertex is redundant. If so, will also
+ * decrement the vertex count.
+ */
+ /*
+ public boolean redundantVertex(float x, float y, float z) {
+ // because vertexCount will be 2 when setting vertex[1]
+ if (vertexCount < 2) return false;
+
+ // vertexCount-1 is the current vertex that would be used
+ // vertexCount-2 would be the previous feller
+ if ((Math.abs(vertices[vertexCount-2][MX] - x) < EPSILON) &&
+ (Math.abs(vertices[vertexCount-2][MY] - y) < EPSILON) &&
+ (Math.abs(vertices[vertexCount-2][MZ] - z) < EPSILON)) {
+ vertexCount--;
+ return true;
+ }
+ return false;
+ }
+ */
+
+
+ public void texture(PImage image) {
+ this.timage = image;
+ this.tpixels = image.pixels;
+ this.twidth = image.width;
+ this.theight = image.height;
+ this.tformat = image.format;
+
+ twidth1 = twidth - 1;
+ theight1 = theight - 1;
+ interpUV = true;
+ }
+
+
+ public void render() {
+ if (vertexCount < 3) return;
+
+ // these may have changed due to a resize()
+ // so they should be refreshed here
+ pixels = parent.pixels;
+ //zbuffer = parent.zbuffer;
+
+ noDepthTest = parent.hints[DISABLE_DEPTH_TEST];
+ smooth = parent.smooth;
+
+ // by default, text turns on smooth for the textures
+ // themselves. but this should be shut off if the hint
+ // for DISABLE_TEXT_SMOOTH is set.
+ texture_smooth = (//parent.drawing_text &&
+ !parent.hints[DISABLE_TEXT_SMOOTH]);
+
+ width = smooth ? parent.width*SUBXRES : parent.width;
+ height = smooth ? parent.height*SUBYRES : parent.height;
+
+ width1 = width - 1;
+ height1 = height - 1;
+
+ if (!interpARGB) {
+ r2 = (int) (vertices[0][R] * 255);
+ g2 = (int) (vertices[0][G] * 255);
+ b2 = (int) (vertices[0][B] * 255);
+ a2 = (int) (vertices[0][A] * 255);
+ a2orig = a2; // save an extra copy
+ rgba = 0xff000000 | (r2 << 16) | (g2 << 8) | b2;
+ }
+
+ for (int i = 0; i < vertexCount; i++) {
+ r[i] = 0; dr[i] = 0; l[i] = 0; dl[i] = 0;
+ }
+
+ // hack to not make polygons fly into the screen
+ if (parent.hints[NO_FLYING_POO]) {
+ float nwidth2 = -width * 2;
+ float nheight2 = -height * 2;
+ float width2 = width * 2;
+ float height2 = height * 2;
+ for (int i = 0; i < vertexCount; i++) {
+ if ((vertices[i][X] < nwidth2) ||
+ (vertices[i][X] > width2) ||
+ (vertices[i][Y] < nheight2) ||
+ (vertices[i][Y] > height2)) {
+ return; // this is a bad poly
+ }
+ }
+ }
+
+ if (smooth) {
+ for (int i = 0; i < vertexCount; i++) {
+ vertices[i][X] *= SUBXRES;
+ vertices[i][Y] *= SUBYRES;
+ }
+ firstModY = -1;
+ }
+
+ // find top vertex (y is zero at top, higher downwards)
+ int topi = 0;
+ float ymin = vertices[0][Y];
+ float ymax = vertices[0][Y]; // fry 031001
+ for (int i = 1; i < vertexCount; i++) {
+ if (vertices[i][Y] < ymin) {
+ ymin = vertices[i][Y];
+ topi = i;
+ }
+ if (vertices[i][Y] > ymax) ymax = vertices[i][Y];
+ }
+
+ // the last row is an exceptional case, because there won't
+ // necessarily be 8 rows of subpixel lines that will force
+ // the final line to render. so instead, the algo keeps track
+ // of the lastY (in subpixel resolution) that will be rendered
+ // and that will force a scanline to happen the same as
+ // every eighth in the other situations
+ //lastY = -1; // fry 031001
+ lastY = (int) (ymax - 0.5f); // global to class bc used by other fxns
+
+ int lefti = topi; // li, index of left vertex
+ int righti = topi; // ri, index of right vertex
+ int y = (int) (ymin + 0.5f); // current scan line
+ int lefty = y - 1; // lower end of left edge
+ int righty = y - 1; // lower end of right edge
+
+ interpX = true;
+
+ int remaining = vertexCount;
+
+ // scan in y, activating new edges on left & right
+ // as scan line passes over new vertices
+ while (remaining > 0) {
+ // advance left edge?
+ while ((lefty <= y) && (remaining > 0)) {
+ remaining--;
+ // step ccw down left side
+ int i = (lefti != 0) ? (lefti-1) : (vertexCount-1);
+ incrementalize_y(vertices[lefti], vertices[i], l, dl, y);
+ lefty = (int) (vertices[i][Y] + 0.5f);
+ lefti = i;
+ }
+
+ // advance right edge?
+ while ((righty <= y) && (remaining > 0)) {
+ remaining--;
+ // step cw down right edge
+ int i = (righti != vertexCount-1) ? (righti + 1) : 0;
+ incrementalize_y(vertices[righti], vertices[i], r, dr, y);
+ righty = (int) (vertices[i][Y] + 0.5f);
+ righti = i;
+ }
+
+ // do scanlines till end of l or r edge
+ while (y < lefty && y < righty) {
+ // this doesn't work because it's not always set here
+ //if (remaining == 0) {
+ //lastY = (lefty < righty) ? lefty-1 : righty-1;
+ //System.out.println("lastY is " + lastY);
+ //}
+
+ if ((y >= 0) && (y < height)) {
+ //try { // hopefully this bug is fixed
+ if (l[X] <= r[X]) scanline(y, l, r);
+ else scanline(y, r, l);
+ //} catch (ArrayIndexOutOfBoundsException e) {
+ //e.printStackTrace();
+ //}
+ }
+ y++;
+ // this increment probably needs to be different
+ // UV and RGB shouldn't be incremented until line is emitted
+ increment(l, dl);
+ increment(r, dr);
+ }
+ }
+ //if (smooth) {
+ //System.out.println("y/lasty/lastmody = " + y + " " + lastY + " " + lastModY);
+ //}
+ }
+
+
+ public void unexpand() {
+ if (smooth) {
+ for (int i = 0; i < vertexCount; i++) {
+ vertices[i][X] /= SUBXRES;
+ vertices[i][Y] /= SUBYRES;
+ }
+ }
+ }
+
+
+ private void scanline(int y, float l[], float r[]) {
+ //System.out.println("scanline " + y);
+ for (int i = 0; i < vertexCount; i++) { // should be moved later
+ sp[i] = 0; sdp[i] = 0;
+ }
+
+ // this rounding doesn't seem to be relevant with smooth
+ int lx = (int) (l[X] + 0.49999f); // ceil(l[X]-.5);
+ if (lx < 0) lx = 0;
+ int rx = (int) (r[X] - 0.5f);
+ if (rx > width1) rx = width1;
+
+ if (lx > rx) return;
+
+ if (smooth) {
+ int mody = MODYRES(y);
+
+ aaleft[mody] = lx;
+ aaright[mody] = rx;
+
+ if (firstModY == -1) {
+ firstModY = mody;
+ aaleftmin = lx; aaleftmax = lx;
+ aarightmin = rx; aarightmax = rx;
+
+ } else {
+ if (aaleftmin > aaleft[mody]) aaleftmin = aaleft[mody];
+ if (aaleftmax < aaleft[mody]) aaleftmax = aaleft[mody];
+ if (aarightmin > aaright[mody]) aarightmin = aaright[mody];
+ if (aarightmax < aaright[mody]) aarightmax = aaright[mody];
+ }
+
+ lastModY = mody; // moved up here (before the return) 031001
+ // not the eighth (or lastY) line, so not scanning this time
+ if ((mody != SUBYRES1) && (y != lastY)) return;
+ //lastModY = mody; // eeK! this was missing
+ //return;
+
+ //if (y == lastY) {
+ //System.out.println("y is lasty");
+ //}
+ //lastModY = mody;
+ aaleftfull = aaleftmax/SUBXRES + 1;
+ aarightfull = aarightmin/SUBXRES - 1;
+ }
+
+ // this is the setup, based on lx
+ incrementalize_x(l, r, sp, sdp, lx);
+
+ // scan in x, generating pixels
+ // using parent.width to get actual pixel index
+ // rather than scaled by smooth factor
+ int offset = smooth ? parent.width * (y / SUBYRES) : parent.width*y;
+
+ int truelx = 0, truerx = 0;
+ if (smooth) {
+ truelx = lx / SUBXRES;
+ truerx = (rx + SUBXRES1) / SUBXRES;
+
+ lx = aaleftmin / SUBXRES;
+ rx = (aarightmax + SUBXRES1) / SUBXRES;
+ if (lx < 0) lx = 0;
+ if (rx > parent.width1) rx = parent.width1;
+ }
+
+ interpX = false;
+ int tr, tg, tb, ta;
+
+ for (int x = lx; x <= rx; x++) {
+ // added == because things on same plane weren't replacing each other
+ // makes for strangeness in 3D, but totally necessary for 2D
+ //if (noDepthTest || (sp[Z] <= zbuffer[offset+x])) {
+ if (true) {
+
+ // map texture based on U, V coords in sp[U] and sp[V]
+ if (interpUV) {
+ int tu = (int)sp[U];
+ int tv = (int)sp[V];
+
+ if (tu > twidth1) tu = twidth1;
+ if (tv > theight1) tv = theight1;
+ if (tu < 0) tu = 0;
+ if (tv < 0) tv = 0;
+
+ int txy = tv*twidth + tu;
+
+ if (smooth || texture_smooth) {
+ //if (FRY) System.out.println("sp u v = " + sp[U] + " " + sp[V]);
+ //System.out.println("sp u v = " + sp[U] + " " + sp[V]);
+ // tuf1/tvf1 is the amount of coverage for the adjacent
+ // pixel, which is the decimal percentage.
+ int tuf1 = (int) (255f * (sp[U] - (float)tu));
+ int tvf1 = (int) (255f * (sp[V] - (float)tv));
+
+ // the closer sp[U or V] is to the decimal being zero
+ // the more coverage it should get of the original pixel
+ int tuf = 255 - tuf1;
+ int tvf = 255 - tvf1;
+
+ // this code sucks! filled with bugs and slow as hell!
+ int pixel00 = tpixels[txy];
+ int pixel01 = (tv < theight1) ?
+ tpixels[txy + twidth] : tpixels[txy];
+ int pixel10 = (tu < twidth1) ?
+ tpixels[txy + 1] : tpixels[txy];
+ int pixel11 = ((tv < theight1) && (tu < twidth1)) ?
+ tpixels[txy + twidth + 1] : tpixels[txy];
+
+ int p00, p01, p10, p11;
+ int px0, px1; //, pxy;
+
+ if (tformat == ALPHA) {
+ px0 = (pixel00*tuf + pixel10*tuf1) >> 8;
+ px1 = (pixel01*tuf + pixel11*tuf1) >> 8;
+ ta = (((px0*tvf + px1*tvf1) >> 8) *
+ (interpARGB ? ((int) (sp[A]*255)) : a2orig)) >> 8;
+
+ } else if (tformat == ARGB) {
+ p00 = (pixel00 >> 24) & 0xff;
+ p01 = (pixel01 >> 24) & 0xff;
+ p10 = (pixel10 >> 24) & 0xff;
+ p11 = (pixel11 >> 24) & 0xff;
+
+ px0 = (p00*tuf + p10*tuf1) >> 8;
+ px1 = (p01*tuf + p11*tuf1) >> 8;
+ ta = (((px0*tvf + px1*tvf1) >> 8) *
+ (interpARGB ? ((int) (sp[A]*255)) : a2orig)) >> 8;
+
+ } else { // RGB image, no alpha
+ ta = interpARGB ? ((int) (sp[A]*255)) : a2orig;
+ }
+
+ if ((tformat == RGB) || (tformat == ARGB)) {
+ p00 = (pixel00 >> 16) & 0xff; // red
+ p01 = (pixel01 >> 16) & 0xff;
+ p10 = (pixel10 >> 16) & 0xff;
+ p11 = (pixel11 >> 16) & 0xff;
+
+ px0 = (p00*tuf + p10*tuf1) >> 8;
+ px1 = (p01*tuf + p11*tuf1) >> 8;
+ tr = (((px0*tvf + px1*tvf1) >> 8) *
+ (interpARGB ? ((int) sp[R]*255) : r2)) >> 8;
+
+
+ p00 = (pixel00 >> 8) & 0xff; // green
+ p01 = (pixel01 >> 8) & 0xff;
+ p10 = (pixel10 >> 8) & 0xff;
+ p11 = (pixel11 >> 8) & 0xff;
+
+ px0 = (p00*tuf + p10*tuf1) >> 8;
+ px1 = (p01*tuf + p11*tuf1) >> 8;
+ tg = (((px0*tvf + px1*tvf1) >> 8) *
+ (interpARGB ? ((int) sp[G]*255) : g2)) >> 8;
+
+
+ p00 = pixel00 & 0xff; // blue
+ p01 = pixel01 & 0xff;
+ p10 = pixel10 & 0xff;
+ p11 = pixel11 & 0xff;
+
+ px0 = (p00*tuf + p10*tuf1) >> 8;
+ px1 = (p01*tuf + p11*tuf1) >> 8;
+ tb = (((px0*tvf + px1*tvf1) >> 8) *
+ (interpARGB ? ((int) sp[B]*255) : b2)) >> 8;
+
+ } else { // alpha image, only use current fill color
+ if (interpARGB) {
+ tr = (int) (sp[R] * 255);
+ tg = (int) (sp[G] * 255);
+ tb = (int) (sp[B] * 255);
+
+ } else {
+ tr = r2;
+ tg = g2;
+ tb = b2;
+ }
+ }
+
+ // get coverage for pixel if smooth
+ // checks smooth again here because of
+ // hints[SMOOTH_IMAGES] used up above
+ int weight = smooth ? coverage(x) : 255;
+ if (weight != 255) ta = ta*weight >> 8;
+
+ } else { // no smooth, just get the pixels
+ int tpixel = tpixels[txy];
+
+ // TODO i doubt splitting these guys really gets us
+ // all that much speed.. is it worth it?
+ if (tformat == ALPHA) {
+ ta = tpixel;
+
+ if (interpARGB) {
+ tr = (int) sp[R]*255;
+ tg = (int) sp[G]*255;
+ tb = (int) sp[B]*255;
+ if (sp[A] != 1) {
+ ta = (((int) sp[A]*255) * ta) >> 8;
+ }
+
+ } else {
+ tr = r2;
+ tg = g2;
+ tb = b2;
+ ta = (a2orig * ta) >> 8;
+ }
+
+ } else { // RGB or ARGB
+ ta = (tformat == RGB) ? 255 : (tpixel >> 24) & 0xff;
+
+ if (interpARGB) {
+ tr = (((int) sp[R]*255) * ((tpixel >> 16) & 0xff)) >> 8;
+ tg = (((int) sp[G]*255) * ((tpixel >> 8) & 0xff)) >> 8;
+ tb = (((int) sp[B]*255) * ((tpixel) & 0xff)) >> 8;
+ ta = (((int) sp[A]*255) * ta) >> 8;
+
+ } else {
+ tr = (r2 * ((tpixel >> 16) & 0xff)) >> 8;
+ tg = (g2 * ((tpixel >> 8) & 0xff)) >> 8;
+ tb = (b2 * ((tpixel) & 0xff)) >> 8;
+ ta = (a2orig * ta) >> 8;
+ }
+ }
+ }
+
+ if ((ta == 254) || (ta == 255)) { // if (ta & 0xf8) would be good
+ // no need to blend
+ pixels[offset+x] = 0xff000000 | (tr << 16) | (tg << 8) | tb;
+ //zbuffer[offset+x] = sp[Z];
+
+ } else {
+ // blend with pixel on screen
+ int a1 = 255-ta;
+ int r1 = (pixels[offset+x] >> 16) & 0xff;
+ int g1 = (pixels[offset+x] >> 8) & 0xff;
+ int b1 = (pixels[offset+x]) & 0xff;
+
+ pixels[offset+x] = 0xff000000 |
+ (((tr*ta + r1*a1) >> 8) << 16) |
+ ((tg*ta + g1*a1) & 0xff00) |
+ ((tb*ta + b1*a1) >> 8);
+ //if (ta > ZBUFFER_MIN_COVERAGE) zbuffer[offset+x] = sp[Z];
+ }
+
+ } else { // no image applied
+ int weight = smooth ? coverage(x) : 255;
+
+ if (interpARGB) {
+ r2 = (int) (sp[R] * 255);
+ g2 = (int) (sp[G] * 255);
+ b2 = (int) (sp[B] * 255);
+ if (sp[A] != 1) weight = (weight * ((int) (sp[A] * 255))) >> 8;
+ if (weight == 255) {
+ rgba = 0xff000000 | (r2 << 16) | (g2 << 8) | b2;
+ }
+ } else {
+ if (a2orig != 255) weight = (weight * a2orig) >> 8;
+ }
+
+ if (weight == 255) {
+ // no blend, no aa, just the rgba
+ pixels[offset+x] = rgba;
+ //zbuffer[offset+x] = sp[Z];
+
+ } else {
+ int r1 = (pixels[offset+x] >> 16) & 0xff;
+ int g1 = (pixels[offset+x] >> 8) & 0xff;
+ int b1 = (pixels[offset+x]) & 0xff;
+ a2 = weight;
+
+ int a1 = 255 - a2;
+ pixels[offset+x] = (0xff000000 |
+ ((r1*a1 + r2*a2) >> 8) << 16 |
+ // use & instead of >> and << below
+ ((g1*a1 + g2*a2) >> 8) << 8 |
+ ((b1*a1 + b2*a2) >> 8));
+
+ //if (a2 > ZBUFFER_MIN_COVERAGE) zbuffer[offset+x] = sp[Z];
+ }
+ }
+ }
+ // if smooth enabled, don't increment values
+ // for the pixel in the stretch out version
+ // of the scanline used to get smooth edges.
+ if (!smooth || ((x >= truelx) && (x <= truerx))) {
+ increment(sp, sdp);
+ }
+ }
+ firstModY = -1;
+ interpX = true;
+ }
+
+
+ // x is in screen, not huge 8x coordinates
+ private int coverage(int x) {
+ if ((x >= aaleftfull) && (x <= aarightfull) &&
+ // important since not all SUBYRES lines may have been covered
+ (firstModY == 0) && (lastModY == SUBYRES1)) {
+ return 255;
+ }
+
+ int pixelLeft = x*SUBXRES; // huh?
+ int pixelRight = pixelLeft + 8;
+
+ int amt = 0;
+ for (int i = firstModY; i <= lastModY; i++) {
+ if ((aaleft[i] > pixelRight) || (aaright[i] < pixelLeft)) {
+ continue;
+ }
+ // does this need a +1 ?
+ amt += ((aaright[i] < pixelRight ? aaright[i] : pixelRight) -
+ (aaleft[i] > pixelLeft ? aaleft[i] : pixelLeft));
+ }
+ amt <<= 2;
+ return (amt == 256) ? 255 : amt;
+ }
+
+
+ private void incrementalize_y(float p1[], float p2[],
+ float p[], float dp[], int y) {
+ float delta = p2[Y] - p1[Y];
+ if (delta == 0) delta = ONE;
+ float fraction = y + HALF - p1[Y];
+
+ if (interpX) {
+ dp[X] = (p2[X] - p1[X]) / delta;
+ p[X] = p1[X] + dp[X] * fraction;
+ }
+ if (interpZ) {
+ dp[Z] = (p2[Z] - p1[Z]) / delta;
+ p[Z] = p1[Z] + dp[Z] * fraction;
+ }
+
+ if (interpARGB) {
+ dp[R] = (p2[R] - p1[R]) / delta;
+ dp[G] = (p2[G] - p1[G]) / delta;
+ dp[B] = (p2[B] - p1[B]) / delta;
+ dp[A] = (p2[A] - p1[A]) / delta;
+ p[R] = p1[R] + dp[R] * fraction;
+ p[G] = p1[G] + dp[G] * fraction;
+ p[B] = p1[B] + dp[B] * fraction;
+ p[A] = p1[A] + dp[A] * fraction;
+ }
+
+ if (interpUV) {
+ dp[U] = (p2[U] - p1[U]) / delta;
+ dp[V] = (p2[V] - p1[V]) / delta;
+
+ //if (smooth) {
+ //p[U] = p1[U]; //+ dp[U] * fraction;
+ //p[V] = p1[V]; //+ dp[V] * fraction;
+
+ //} else {
+ p[U] = p1[U] + dp[U] * fraction;
+ p[V] = p1[V] + dp[V] * fraction;
+ //}
+ if (FRY) System.out.println("inc y p[U] p[V] = " + p[U] + " " + p[V]);
+ }
+ }
+
+
+ private void incrementalize_x(float p1[], float p2[],
+ float p[], float dp[], int x) {
+ float delta = p2[X] - p1[X];
+ if (delta == 0) delta = ONE;
+ float fraction = x + HALF - p1[X];
+ if (smooth) {
+ delta /= SUBXRES;
+ fraction /= SUBXRES;
+ }
+
+ if (interpX) {
+ dp[X] = (p2[X] - p1[X]) / delta;
+ p[X] = p1[X] + dp[X] * fraction;
+ }
+ if (interpZ) {
+ dp[Z] = (p2[Z] - p1[Z]) / delta;
+ p[Z] = p1[Z] + dp[Z] * fraction;
+ }
+
+ if (interpARGB) {
+ dp[R] = (p2[R] - p1[R]) / delta;
+ dp[G] = (p2[G] - p1[G]) / delta;
+ dp[B] = (p2[B] - p1[B]) / delta;
+ dp[A] = (p2[A] - p1[A]) / delta;
+ p[R] = p1[R] + dp[R] * fraction;
+ p[G] = p1[G] + dp[G] * fraction;
+ p[B] = p1[B] + dp[B] * fraction;
+ p[A] = p1[A] + dp[A] * fraction;
+ }
+
+ if (interpUV) {
+ if (FRY) System.out.println("delta, frac = " + delta + ", " + fraction);
+ dp[U] = (p2[U] - p1[U]) / delta;
+ dp[V] = (p2[V] - p1[V]) / delta;
+
+ //if (smooth) {
+ //p[U] = p1[U];
+ // offset for the damage that will be done by the
+ // 8 consecutive calls to scanline
+ // agh.. this won't work b/c not always 8 calls before render
+ // maybe lastModY - firstModY + 1 instead?
+ if (FRY) System.out.println("before inc x p[V] = " + p[V] + " " + p1[V] + " " + p2[V]);
+ //p[V] = p1[V] - SUBXRES1 * fraction;
+
+ //} else {
+ p[U] = p1[U] + dp[U] * fraction;
+ p[V] = p1[V] + dp[V] * fraction;
+ //}
+ }
+ }
+
+
+ private void increment(float p[], float dp[]) {
+ if (interpX) p[X] += dp[X];
+ if (interpZ) p[Z] += dp[Z];
+
+ if (interpARGB) {
+ p[R] += dp[R];
+ p[G] += dp[G];
+ p[B] += dp[B];
+ p[A] += dp[A];
+ }
+
+ if (interpUV) {
+ if (FRY) System.out.println("increment() " + p[V] + " " + dp[V]);
+ p[U] += dp[U];
+ p[V] += dp[V];
+ }
+ }
+}
diff --git a/core/PShape.java b/core/src/processing/core/PShape.java
similarity index 100%
rename from core/PShape.java
rename to core/src/processing/core/PShape.java
diff --git a/core/PTriangle.java b/core/src/processing/core/PTriangle.java
similarity index 96%
rename from core/PTriangle.java
rename to core/src/processing/core/PTriangle.java
index 7f7d4c0ea..830f87e24 100644
--- a/core/PTriangle.java
+++ b/core/src/processing/core/PTriangle.java
@@ -1,2541 +1,2541 @@
-/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
-
-/*
- Part of the Processing project - http://processing.org
-
- Copyright (c) 2004-06 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;
-
-/**
- * Handles rendering of single (tesselated) triangles in 3D.
- *
- * Written by sami www.sumea.com
- */
-public class PTriangle implements PConstants
-{
- static final int R_GOURAUD = 0x1;
- static final int R_TEXTURE8 = 0x2;
- static final int R_TEXTURE24 = 0x4;
- static final int R_TEXTURE32 = 0x8;
- static final int R_ALPHA = 0x10;
-
- private int[] m_pixels;
- private int[] m_texture;
- private int[] m_stencil;
- private float[] m_zbuffer;
-
- private int SCREEN_WIDTH;
- private int SCREEN_HEIGHT;
- //private int SCREEN_WIDTH1;
- //private int SCREEN_HEIGHT1;
-
- private int TEX_WIDTH;
- private int TEX_HEIGHT;
- private float F_TEX_WIDTH;
- private float F_TEX_HEIGHT;
-
- public boolean INTERPOLATE_UV;
- public boolean INTERPOLATE_RGB;
- public boolean INTERPOLATE_ALPHA;
-
- // Vertex coordinates
- private float[] x_array;
- private float[] y_array;
- private float[] z_array;
-
- // U,V coordinates
- private float[] u_array;
- private float[] v_array;
-
- // Vertex Intensity
- private float[] r_array;
- private float[] g_array;
- private float[] b_array;
- private float[] a_array;
-
- // vertex offsets
- private int o0;
- private int o1;
- private int o2;
-
- /* rgb & a */
- private float r0;
- private float r1;
- private float r2;
- private float g0;
- private float g1;
- private float g2;
- private float b0;
- private float b1;
- private float b2;
- private float a0;
- private float a1;
- private float a2;
-
- /* accurate texture uv coordinates */
- private float u0;
- private float u1;
- private float u2;
- private float v0;
- private float v1;
- private float v2;
-
- /* deltas */
- //private float dx0;
- //private float dx1;
- private float dx2;
- private float dy0;
- private float dy1;
- private float dy2;
- private float dz0;
- //private float dz1;
- private float dz2;
-
- /* texture deltas */
- private float du0;
- //private float du1;
- private float du2;
- private float dv0;
- //private float dv1;
- private float dv2;
-
- /* rgba deltas */
- private float dr0;
- //private float dr1;
- private float dr2;
- private float dg0;
- //private float dg1;
- private float dg2;
- private float db0;
- //private float db1;
- private float db2;
- private float da0;
- //private float da1;
- private float da2;
-
- /* */
- private float uleft;
- private float vleft;
- private float uleftadd;
- private float vleftadd;
-
- /* polyedge positions & adds */
- private float xleft;
- private float xrght;
- private float xadd1;
- private float xadd2;
- private float zleft;
- private float zleftadd;
-
- /* rgba positions & adds */
- private float rleft;
- private float gleft;
- private float bleft;
- private float aleft;
- private float rleftadd;
- private float gleftadd;
- private float bleftadd;
- private float aleftadd;
-
- /* other somewhat useful variables :) */
- private float dta;
- //private float dta2;
- private float temp;
- private float width;
-
- /* integer poly UV adds */
- private int iuadd;
- private int ivadd;
- private int iradd;
- private int igadd;
- private int ibadd;
- private int iaadd;
- private float izadd;
-
- /* fill color */
- private int m_fill;
-
- /* draw flags */
- public int m_drawFlags;
-
- /* current poly number */
- private int m_index;
-
- /** */
- private PGraphics3 parent;
-
- private boolean noDepthTest;
-
- /** */
- private boolean m_culling;
-
- /** */
- private boolean m_singleRight;
-
- /** */
- private boolean m_bilinear;
-
- public PTriangle(PGraphics3 g) {
- //SCREEN_WIDTH = g.width;
- //SCREEN_HEIGHT = g.height;
- //SCREEN_WIDTH1 = SCREEN_WIDTH-1;
- //SCREEN_HEIGHT1 = SCREEN_HEIGHT-1;
-
- //m_pixels = g.pixels;
- //m_stencil = g.stencil;
- //m_zbuffer = g.zbuffer;
-
- x_array = new float[3];
- y_array = new float[3];
- z_array = new float[3];
- u_array = new float[3];
- v_array = new float[3];
- r_array = new float[3];
- g_array = new float[3];
- b_array = new float[3];
- a_array = new float[3];
-
- this.parent = g;
- reset();
- }
-
- /**
- * Resets polygon attributes
- */
- public void reset() {
- // reset these in case PGraphics was resized
-
- SCREEN_WIDTH = parent.width;
- SCREEN_HEIGHT = parent.height;
- //SCREEN_WIDTH1 = SCREEN_WIDTH-1;
- //SCREEN_HEIGHT1 = SCREEN_HEIGHT-1;
-
- m_pixels = parent.pixels;
- m_stencil = parent.stencil;
- m_zbuffer = parent.zbuffer;
-
- noDepthTest = parent.hints[DISABLE_DEPTH_TEST];
-
- // other things to reset
-
- INTERPOLATE_UV = false;
- INTERPOLATE_RGB = false;
- INTERPOLATE_ALPHA = false;
- //m_tImage = null;
- m_texture = null;
- m_drawFlags = 0;
- }
-
- /**
- * Sets backface culling on/off
- */
- public void setCulling(boolean tf) {
- m_culling = tf;
- }
-
-
- /**
- * Sets vertex coordinates for the triangle
- */
- public void setVertices(float x0, float y0, float z0,
- float x1, float y1, float z1,
- float x2, float y2, float z2) {
- x_array[0] = x0;
- x_array[1] = x1;
- x_array[2] = x2;
-
- y_array[0] = y0;
- y_array[1] = y1;
- y_array[2] = y2;
-
- z_array[0] = z0;
- z_array[1] = z1;
- z_array[2] = z2;
- }
-
- /**
- * Sets the UV coordinates of the texture
- */
- public void setUV(float u0, float v0,
- float u1, float v1,
- float u2, float v2) {
- // sets & scales uv texture coordinates to center of the pixel
- u_array[0] = (u0 * F_TEX_WIDTH + 0.5f) * 65536f;
- u_array[1] = (u1 * F_TEX_WIDTH + 0.5f) * 65536f;
- u_array[2] = (u2 * F_TEX_WIDTH + 0.5f) * 65536f;
- v_array[0] = (v0 * F_TEX_HEIGHT + 0.5f) * 65536f;
- v_array[1] = (v1 * F_TEX_HEIGHT + 0.5f) * 65536f;
- v_array[2] = (v2 * F_TEX_HEIGHT + 0.5f) * 65536f;
- }
-
- /**
- * Sets vertex intensities in 0xRRGGBBAA format
- */
- public void setIntensities( float r0, float g0, float b0, float a0,
- float r1, float g1, float b1, float a1,
- float r2, float g2, float b2, float a2) {
- // Check if we need alpha or not?
- if ((a0 != 1.0f) || (a1 != 1.0f) || (a2 != 1.0f)) {
- INTERPOLATE_ALPHA = true;
- a_array[0] = (a0 * 253f + 1.0f) * 65536f;
- a_array[1] = (a1 * 253f + 1.0f) * 65536f;
- a_array[2] = (a2 * 253f + 1.0f) * 65536f;
- m_drawFlags|=R_ALPHA;
- } else {
- INTERPOLATE_ALPHA = false;
- m_drawFlags&=~R_ALPHA;
- }
-
- // Check if we need to interpolate the intensity values
- if ((r0 != r1) || (r1 != r2)) {
- INTERPOLATE_RGB = true;
- m_drawFlags|=R_GOURAUD;
- } else if ((g0 != g1) || (g1 != g2)) {
- INTERPOLATE_RGB = true;
- m_drawFlags|=R_GOURAUD;
- } else if ((b0 != b1) || (b1 != b2)) {
- INTERPOLATE_RGB = true;
- m_drawFlags|=R_GOURAUD;
- } else {
- //m_fill = parent.filli;
- m_drawFlags&=~R_GOURAUD;
- }
-
- // push values to arrays.. some extra scaling is added
- // to prevent possible color "overflood" due to rounding errors
- r_array[0] = (r0 * 253f + 1.0f) * 65536f;
- r_array[1] = (r1 * 253f + 1.0f) * 65536f;
- r_array[2] = (r2 * 253f + 1.0f) * 65536f;
-
- g_array[0] = (g0 * 253f + 1.0f) * 65536f;
- g_array[1] = (g1 * 253f + 1.0f) * 65536f;
- g_array[2] = (g2 * 253f + 1.0f) * 65536f;
-
- b_array[0] = (b0 * 253f + 1.0f) * 65536f;
- b_array[1] = (b1 * 253f + 1.0f) * 65536f;
- b_array[2] = (b2 * 253f + 1.0f) * 65536f;
-
- // for plain triangles
- m_fill = ((int)(255*r0) << 16) | ((int)(255*g0) << 8) | (int)(255*b0);
- }
-
-
- /**
- * Sets texture image used for the polygon
- */
- public void setTexture(PImage image) {
- //m_tImage = image;
- m_texture = image.pixels;
- TEX_WIDTH = image.width;
- TEX_HEIGHT = image.height;
- F_TEX_WIDTH = TEX_WIDTH-1;
- F_TEX_HEIGHT = TEX_HEIGHT-1;
- INTERPOLATE_UV = true;
-
- if (image.format == ARGB) {
- m_drawFlags|=R_TEXTURE32;
- } else if (image.format == RGB) {
- m_drawFlags|=R_TEXTURE24;
- } else if (image.format == ALPHA) {
- m_drawFlags|=R_TEXTURE8;
- }
-
- //if (parent.hints[SMOOTH_IMAGES]) {
- if (parent.smooth) {
- m_bilinear = true;
- } else {
- m_bilinear = false;
- }
- }
-
- /**
- *
- */
- public void setUV(float[] u, float[] v) {
- if (m_bilinear) {
- // sets & scales uv texture coordinates to edges of pixels
- u_array[0] = (u[0] * F_TEX_WIDTH) * 65500f;
- u_array[1] = (u[1] * F_TEX_WIDTH) * 65500f;
- u_array[2] = (u[2] * F_TEX_WIDTH) * 65500f;
- v_array[0] = (v[0] * F_TEX_HEIGHT) * 65500f;
- v_array[1] = (v[1] * F_TEX_HEIGHT) * 65500f;
- v_array[2] = (v[2] * F_TEX_HEIGHT) * 65500f;
- } else {
- // sets & scales uv texture coordinates to center of the pixel
- u_array[0] = (u[0] * TEX_WIDTH) * 65500f;
- u_array[1] = (u[1] * TEX_WIDTH) * 65500f;
- u_array[2] = (u[2] * TEX_WIDTH) * 65500f;
- v_array[0] = (v[0] * TEX_HEIGHT) * 65500f;
- v_array[1] = (v[1] * TEX_HEIGHT) * 65500f;
- v_array[2] = (v[2] * TEX_HEIGHT) * 65500f;
- }
- }
-
- public void setIndex(int index) {
- m_index = index;
- }
-
- /**
- * Renders the polygon
- */
- public void render() {
- // removed. done in PGraphics [rocha]
- // increase polygon offset
- //m_index = (m_index + 1) & 0xFFFFFFF;
-
- // draw the polygon
- draw();
-
- // removed. replaced by external antialiasing [rocha]
- // smooth edges?
- //if (parent.smooth )
- //{
- // drawline_blender(x_array[0], y_array[0], x_array[1], y_array[1]);
- // drawline_blender(x_array[1], y_array[1], x_array[2], y_array[2]);
- // drawline_blender(x_array[2], y_array[2], x_array[0], y_array[0]);
- //}
- }
-
- private void draw() {
- // y-coordinates
- float x0;
- float x1;
- float x2;
-
- //
- float z0;
- float z1;
- float z2;
-
- //
- float y0 = y_array[0];
- float y1 = y_array[1];
- float y2 = y_array[2];
-
- // do backface culling?
- if (m_culling) {
- x0 = x_array[0];
- if ((x_array[2]-x0)*(y1-y0) < (x_array[1]-x0)*(y2-y0))
- return;
- }
-
- /* get vertex order from top -> down */
- if (y0
+ * Written by sami www.sumea.com
+ */
+public class PTriangle implements PConstants
+{
+ static final int R_GOURAUD = 0x1;
+ static final int R_TEXTURE8 = 0x2;
+ static final int R_TEXTURE24 = 0x4;
+ static final int R_TEXTURE32 = 0x8;
+ static final int R_ALPHA = 0x10;
+
+ private int[] m_pixels;
+ private int[] m_texture;
+ private int[] m_stencil;
+ private float[] m_zbuffer;
+
+ private int SCREEN_WIDTH;
+ private int SCREEN_HEIGHT;
+ //private int SCREEN_WIDTH1;
+ //private int SCREEN_HEIGHT1;
+
+ private int TEX_WIDTH;
+ private int TEX_HEIGHT;
+ private float F_TEX_WIDTH;
+ private float F_TEX_HEIGHT;
+
+ public boolean INTERPOLATE_UV;
+ public boolean INTERPOLATE_RGB;
+ public boolean INTERPOLATE_ALPHA;
+
+ // Vertex coordinates
+ private float[] x_array;
+ private float[] y_array;
+ private float[] z_array;
+
+ // U,V coordinates
+ private float[] u_array;
+ private float[] v_array;
+
+ // Vertex Intensity
+ private float[] r_array;
+ private float[] g_array;
+ private float[] b_array;
+ private float[] a_array;
+
+ // vertex offsets
+ private int o0;
+ private int o1;
+ private int o2;
+
+ /* rgb & a */
+ private float r0;
+ private float r1;
+ private float r2;
+ private float g0;
+ private float g1;
+ private float g2;
+ private float b0;
+ private float b1;
+ private float b2;
+ private float a0;
+ private float a1;
+ private float a2;
+
+ /* accurate texture uv coordinates */
+ private float u0;
+ private float u1;
+ private float u2;
+ private float v0;
+ private float v1;
+ private float v2;
+
+ /* deltas */
+ //private float dx0;
+ //private float dx1;
+ private float dx2;
+ private float dy0;
+ private float dy1;
+ private float dy2;
+ private float dz0;
+ //private float dz1;
+ private float dz2;
+
+ /* texture deltas */
+ private float du0;
+ //private float du1;
+ private float du2;
+ private float dv0;
+ //private float dv1;
+ private float dv2;
+
+ /* rgba deltas */
+ private float dr0;
+ //private float dr1;
+ private float dr2;
+ private float dg0;
+ //private float dg1;
+ private float dg2;
+ private float db0;
+ //private float db1;
+ private float db2;
+ private float da0;
+ //private float da1;
+ private float da2;
+
+ /* */
+ private float uleft;
+ private float vleft;
+ private float uleftadd;
+ private float vleftadd;
+
+ /* polyedge positions & adds */
+ private float xleft;
+ private float xrght;
+ private float xadd1;
+ private float xadd2;
+ private float zleft;
+ private float zleftadd;
+
+ /* rgba positions & adds */
+ private float rleft;
+ private float gleft;
+ private float bleft;
+ private float aleft;
+ private float rleftadd;
+ private float gleftadd;
+ private float bleftadd;
+ private float aleftadd;
+
+ /* other somewhat useful variables :) */
+ private float dta;
+ //private float dta2;
+ private float temp;
+ private float width;
+
+ /* integer poly UV adds */
+ private int iuadd;
+ private int ivadd;
+ private int iradd;
+ private int igadd;
+ private int ibadd;
+ private int iaadd;
+ private float izadd;
+
+ /* fill color */
+ private int m_fill;
+
+ /* draw flags */
+ public int m_drawFlags;
+
+ /* current poly number */
+ private int m_index;
+
+ /** */
+ private PGraphics3 parent;
+
+ private boolean noDepthTest;
+
+ /** */
+ private boolean m_culling;
+
+ /** */
+ private boolean m_singleRight;
+
+ /** */
+ private boolean m_bilinear;
+
+ public PTriangle(PGraphics3 g) {
+ //SCREEN_WIDTH = g.width;
+ //SCREEN_HEIGHT = g.height;
+ //SCREEN_WIDTH1 = SCREEN_WIDTH-1;
+ //SCREEN_HEIGHT1 = SCREEN_HEIGHT-1;
+
+ //m_pixels = g.pixels;
+ //m_stencil = g.stencil;
+ //m_zbuffer = g.zbuffer;
+
+ x_array = new float[3];
+ y_array = new float[3];
+ z_array = new float[3];
+ u_array = new float[3];
+ v_array = new float[3];
+ r_array = new float[3];
+ g_array = new float[3];
+ b_array = new float[3];
+ a_array = new float[3];
+
+ this.parent = g;
+ reset();
+ }
+
+ /**
+ * Resets polygon attributes
+ */
+ public void reset() {
+ // reset these in case PGraphics was resized
+
+ SCREEN_WIDTH = parent.width;
+ SCREEN_HEIGHT = parent.height;
+ //SCREEN_WIDTH1 = SCREEN_WIDTH-1;
+ //SCREEN_HEIGHT1 = SCREEN_HEIGHT-1;
+
+ m_pixels = parent.pixels;
+ m_stencil = parent.stencil;
+ m_zbuffer = parent.zbuffer;
+
+ noDepthTest = parent.hints[DISABLE_DEPTH_TEST];
+
+ // other things to reset
+
+ INTERPOLATE_UV = false;
+ INTERPOLATE_RGB = false;
+ INTERPOLATE_ALPHA = false;
+ //m_tImage = null;
+ m_texture = null;
+ m_drawFlags = 0;
+ }
+
+ /**
+ * Sets backface culling on/off
+ */
+ public void setCulling(boolean tf) {
+ m_culling = tf;
+ }
+
+
+ /**
+ * Sets vertex coordinates for the triangle
+ */
+ public void setVertices(float x0, float y0, float z0,
+ float x1, float y1, float z1,
+ float x2, float y2, float z2) {
+ x_array[0] = x0;
+ x_array[1] = x1;
+ x_array[2] = x2;
+
+ y_array[0] = y0;
+ y_array[1] = y1;
+ y_array[2] = y2;
+
+ z_array[0] = z0;
+ z_array[1] = z1;
+ z_array[2] = z2;
+ }
+
+ /**
+ * Sets the UV coordinates of the texture
+ */
+ public void setUV(float u0, float v0,
+ float u1, float v1,
+ float u2, float v2) {
+ // sets & scales uv texture coordinates to center of the pixel
+ u_array[0] = (u0 * F_TEX_WIDTH + 0.5f) * 65536f;
+ u_array[1] = (u1 * F_TEX_WIDTH + 0.5f) * 65536f;
+ u_array[2] = (u2 * F_TEX_WIDTH + 0.5f) * 65536f;
+ v_array[0] = (v0 * F_TEX_HEIGHT + 0.5f) * 65536f;
+ v_array[1] = (v1 * F_TEX_HEIGHT + 0.5f) * 65536f;
+ v_array[2] = (v2 * F_TEX_HEIGHT + 0.5f) * 65536f;
+ }
+
+ /**
+ * Sets vertex intensities in 0xRRGGBBAA format
+ */
+ public void setIntensities( float r0, float g0, float b0, float a0,
+ float r1, float g1, float b1, float a1,
+ float r2, float g2, float b2, float a2) {
+ // Check if we need alpha or not?
+ if ((a0 != 1.0f) || (a1 != 1.0f) || (a2 != 1.0f)) {
+ INTERPOLATE_ALPHA = true;
+ a_array[0] = (a0 * 253f + 1.0f) * 65536f;
+ a_array[1] = (a1 * 253f + 1.0f) * 65536f;
+ a_array[2] = (a2 * 253f + 1.0f) * 65536f;
+ m_drawFlags|=R_ALPHA;
+ } else {
+ INTERPOLATE_ALPHA = false;
+ m_drawFlags&=~R_ALPHA;
+ }
+
+ // Check if we need to interpolate the intensity values
+ if ((r0 != r1) || (r1 != r2)) {
+ INTERPOLATE_RGB = true;
+ m_drawFlags|=R_GOURAUD;
+ } else if ((g0 != g1) || (g1 != g2)) {
+ INTERPOLATE_RGB = true;
+ m_drawFlags|=R_GOURAUD;
+ } else if ((b0 != b1) || (b1 != b2)) {
+ INTERPOLATE_RGB = true;
+ m_drawFlags|=R_GOURAUD;
+ } else {
+ //m_fill = parent.filli;
+ m_drawFlags&=~R_GOURAUD;
+ }
+
+ // push values to arrays.. some extra scaling is added
+ // to prevent possible color "overflood" due to rounding errors
+ r_array[0] = (r0 * 253f + 1.0f) * 65536f;
+ r_array[1] = (r1 * 253f + 1.0f) * 65536f;
+ r_array[2] = (r2 * 253f + 1.0f) * 65536f;
+
+ g_array[0] = (g0 * 253f + 1.0f) * 65536f;
+ g_array[1] = (g1 * 253f + 1.0f) * 65536f;
+ g_array[2] = (g2 * 253f + 1.0f) * 65536f;
+
+ b_array[0] = (b0 * 253f + 1.0f) * 65536f;
+ b_array[1] = (b1 * 253f + 1.0f) * 65536f;
+ b_array[2] = (b2 * 253f + 1.0f) * 65536f;
+
+ // for plain triangles
+ m_fill = ((int)(255*r0) << 16) | ((int)(255*g0) << 8) | (int)(255*b0);
+ }
+
+
+ /**
+ * Sets texture image used for the polygon
+ */
+ public void setTexture(PImage image) {
+ //m_tImage = image;
+ m_texture = image.pixels;
+ TEX_WIDTH = image.width;
+ TEX_HEIGHT = image.height;
+ F_TEX_WIDTH = TEX_WIDTH-1;
+ F_TEX_HEIGHT = TEX_HEIGHT-1;
+ INTERPOLATE_UV = true;
+
+ if (image.format == ARGB) {
+ m_drawFlags|=R_TEXTURE32;
+ } else if (image.format == RGB) {
+ m_drawFlags|=R_TEXTURE24;
+ } else if (image.format == ALPHA) {
+ m_drawFlags|=R_TEXTURE8;
+ }
+
+ //if (parent.hints[SMOOTH_IMAGES]) {
+ if (parent.smooth) {
+ m_bilinear = true;
+ } else {
+ m_bilinear = false;
+ }
+ }
+
+ /**
+ *
+ */
+ public void setUV(float[] u, float[] v) {
+ if (m_bilinear) {
+ // sets & scales uv texture coordinates to edges of pixels
+ u_array[0] = (u[0] * F_TEX_WIDTH) * 65500f;
+ u_array[1] = (u[1] * F_TEX_WIDTH) * 65500f;
+ u_array[2] = (u[2] * F_TEX_WIDTH) * 65500f;
+ v_array[0] = (v[0] * F_TEX_HEIGHT) * 65500f;
+ v_array[1] = (v[1] * F_TEX_HEIGHT) * 65500f;
+ v_array[2] = (v[2] * F_TEX_HEIGHT) * 65500f;
+ } else {
+ // sets & scales uv texture coordinates to center of the pixel
+ u_array[0] = (u[0] * TEX_WIDTH) * 65500f;
+ u_array[1] = (u[1] * TEX_WIDTH) * 65500f;
+ u_array[2] = (u[2] * TEX_WIDTH) * 65500f;
+ v_array[0] = (v[0] * TEX_HEIGHT) * 65500f;
+ v_array[1] = (v[1] * TEX_HEIGHT) * 65500f;
+ v_array[2] = (v[2] * TEX_HEIGHT) * 65500f;
+ }
+ }
+
+ public void setIndex(int index) {
+ m_index = index;
+ }
+
+ /**
+ * Renders the polygon
+ */
+ public void render() {
+ // removed. done in PGraphics [rocha]
+ // increase polygon offset
+ //m_index = (m_index + 1) & 0xFFFFFFF;
+
+ // draw the polygon
+ draw();
+
+ // removed. replaced by external antialiasing [rocha]
+ // smooth edges?
+ //if (parent.smooth )
+ //{
+ // drawline_blender(x_array[0], y_array[0], x_array[1], y_array[1]);
+ // drawline_blender(x_array[1], y_array[1], x_array[2], y_array[2]);
+ // drawline_blender(x_array[2], y_array[2], x_array[0], y_array[0]);
+ //}
+ }
+
+ private void draw() {
+ // y-coordinates
+ float x0;
+ float x1;
+ float x2;
+
+ //
+ float z0;
+ float z1;
+ float z2;
+
+ //
+ float y0 = y_array[0];
+ float y1 = y_array[1];
+ float y2 = y_array[2];
+
+ // do backface culling?
+ if (m_culling) {
+ x0 = x_array[0];
+ if ((x_array[2]-x0)*(y1-y0) < (x_array[1]-x0)*(y2-y0))
+ return;
+ }
+
+ /* get vertex order from top -> down */
+ if (y0