diff --git a/.classpath b/.classpath new file mode 100644 index 000000000..f713e0ef4 --- /dev/null +++ b/.classpath @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 000000000..59e917a11 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + processing-orig + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/core/PApplet.java b/core/src/processing/core/PApplet.java similarity index 96% rename from core/PApplet.java rename to core/src/processing/core/PApplet.java index 09b6ef0a3..09fdc0cf6 100644 --- a/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -1,7509 +1,7509 @@ -/* -*- 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.applet.*; -import java.awt.*; -import java.awt.event.*; -import java.awt.image.*; -import java.io.*; -import java.lang.reflect.*; -import java.net.*; -import java.text.*; -import java.util.*; -import java.util.zip.*; - - -/** - * Base class for all sketches that use processing.core. - */ -public class PApplet extends Applet - implements PConstants, Runnable, - MouseListener, MouseMotionListener, KeyListener, FocusListener -{ - /** - * "1.3" or "1.1" or whatever (just the first three chars) - */ - public static final String javaVersionName = - System.getProperty("java.version").substring(0,3); - - /** - * Version of Java that's in use, whether 1.1 or 1.3 or whatever, - * stored as a float. - *

- * Note that because this is stored as a float, the values may - * not be exactly 1.3 or 1.4. Instead, make sure you're - * comparing against 1.3f or 1.4f, which will have the same amount - * of error (i.e. 1.40000001). This could just be a double, but - * since Processing only uses floats, it's safer 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<>= 1; - } - - if (x<0) x=-x; - if (y<0) y=-y; - if (z<0) z=-z; - - int xi=(int)x, yi=(int)y, zi=(int)z; - float xf = (float)(x-xi); - float yf = (float)(y-yi); - float zf = (float)(z-zi); - float rxf, ryf; - - float r=0; - float ampl=0.5f; - - float n1,n2,n3; - - for (int i=0; i=1.0f) { xi++; xf--; } - if (yf>=1.0f) { yi++; yf--; } - if (zf>=1.0f) { zi++; zf--; } - } - return r; - } - - // [toxi 031112] - // now adjusts to the size of the cosLUT used via - // the new variables, defined above - private float noise_fsc(float i) { - // using bagel's cosine table instead - return 0.5f*(1.0f-perlin_cosTable[(int)(i*perlin_PI)%perlin_TWOPI]); - } - - // [toxi 040903] - // make perlin noise quality user controlled to allow - // for different levels of detail. lower values will produce - // smoother results as higher octaves are surpressed - - public void noiseDetail(int lod) { - if (lod>0) perlin_octaves=lod; - } - - public void noiseDetail(int lod, float falloff) { - if (lod>0) perlin_octaves=lod; - if (falloff>0) perlin_amp_falloff=falloff; - } - - public void noiseSeed(long what) { - if (perlinRandom == null) perlinRandom = new Random(); - perlinRandom.setSeed(what); - } - - - - ////////////////////////////////////////////////////////////// - - // SOUND I/O - - /* - public PSound loadSound(String filename) { - if (PApplet.javaVersion >= 1.3f) { - return new PSound2(this, openStream(filename)); - } - return new PSound(this, openStream(filename)); - } - */ - - - ////////////////////////////////////////////////////////////// - - // IMAGE I/O - - - Hashtable imageTable; - - /** - * Draw an image based on its filename. This is less efficient than - * using loadImage because there's no way to unload it from memory, - * but it's useful for beginners. - */ - public void image(String filename, float x, float y) { - image(tableImage(filename), x, y); - } - - /** - * Draw an image based on its filename. This is less than efficient - * than using loadImage because there's no way to unload it from memory, - * but it's useful for beginners. - */ - public void image(String filename, - float x, float y, float c, float d) { - image(tableImage(filename), x, y, c, d); - } - - /** - * Draw an image based on its filename. This is less than efficient - * than using loadImage because there's no way to unload it from memory, - * but it's useful for beginners. - */ - public void image(String filename, - float a, float b, float c, float d, - int u1, int v1, int u2, int v2) { - image(tableImage(filename), a, b, c, d, u1, v1, u2, v2); - } - - - /** - * Load an image and store it in a table based on its name. - */ - protected PImage tableImage(String filename) { - if (imageTable == null) imageTable = new Hashtable(); - - PImage image = (PImage) imageTable.get(filename); - if (image != null) return image; - - image = loadImage(filename); - return image; - } - - - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - - - protected String[] loadImageFormats; - - - /** - * Load an image from the data folder or a local directory. - * Supports .gif (including transparency), .tga, and .jpg images. - * In Java 1.3 or later, .png images are - * - * also supported. - *

- * 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. - *

- * Only works with Java 1.3 or later. Many .otf fonts don't seem - * to be supported by Java, perhaps because they're CFF based? - *

- * Font names are inconsistent across platforms and Java versions. - * On Mac OS X, Java 1.3 uses the font menu name of the font, - * whereas Java 1.4 uses the PostScript name of the font. Java 1.4 - * on OS X will also accept the font menu name as well. On Windows, - * it appears that only the menu names are used, no matter what - * Java version is in use. Naming system unknown/untested for 1.5. - *

- * Use 'null' for the charset if you want to use any of the 65,536 - * unicode characters that exist in the font. Note that this can - * produce an enormous file or may cause an OutOfMemoryError. - */ - public PFont createFont(String name, float size, - boolean smooth, char charset[]) { - if (PApplet.javaVersion < 1.3f) { - throw new RuntimeException("Can only create fonts with " + - "Java 1.3 or higher"); - } - - String lowerName = name.toLowerCase(); - Font font = null; - - try { - Method deriveFontMethod = - Font.class.getMethod("deriveFont", - new Class[] { Float.TYPE }); - Float floatSize = new Float(size); - - if (lowerName.endsWith(".otf") || lowerName.endsWith(".ttf")) { - //font = Font.createFont(Font.TRUETYPE_FONT, openStream(name)); - Method createFontMethod = - Font.class.getMethod("createFont", - new Class[] { Integer.TYPE, - InputStream.class }); - Field ttf = Font.class.getField("TRUETYPE_FONT"); - Integer ttfInteger = new Integer(ttf.getInt(ttf)); - Font baseFont = (Font) - createFontMethod.invoke(name, - new Object[] { ttfInteger, - openStream(name) }); - font = (Font) deriveFontMethod.invoke(baseFont, - new Object[] { floatSize }); - } else { - Font baseFont = new Font(name, Font.PLAIN, 1); - font = (Font) - deriveFontMethod.invoke(baseFont, new Object[] { floatSize }); - } - - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException("Problem using createFont() " + - "with the file " + name); - } - return new PFont(font, smooth, charset); - } - - - - ////////////////////////////////////////////////////////////// - - // FILE INPUT - - - public File inputFile() { - return inputFile("Select a file..."); - } - - - public File inputFile(String prompt) { - Frame parentFrame = null; - Component comp = getParent(); - while (comp != null) { - if (comp instanceof Frame) { - parentFrame = (Frame) comp; - break; - } - comp = comp.getParent(); - } - return inputFile(prompt, parentFrame); - } - - - static public File inputFile(Frame parent) { - return inputFile("Select a file...", parent); - } - - - /** - * static version of inputFile 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. - */ - // 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: - *

- */ - 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. - *

- * 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 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. - *

- * 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. - *

-   * 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. - *

- * 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). - *

-   * 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. - *

- * 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. - *

- * From a software engineering standpoint, using the stderr stream - * is highly problematic because of its tendency to die or act - * funny, especially on Windows. Threading issues can cause the - * buffers to get full or the applet to not run properly. - * Formerly known as the "code folder bug", this has been fixed - * through the use of this class, however it remains a tenuous - * situation that could perhaps break in a future JDK release. - */ - class Worker { - private Object value; - private WorkerVar workerVar; - - protected synchronized Object getValue() { - return value; - } - - private synchronized void setValue(Object x) { - value = x; - } - - public Object construct() { - try { - int anything = System.in.read(); - if (anything == EXTERNAL_STOP) { - - // adding this for 0073.. need to stop libraries - // when the stop button is hit. - PApplet.this.stop(); - finished = true; - } - } catch (IOException e) { - finished = true; - } - try { - Thread.sleep(250); - //Thread.sleep(100); // kick up latency for 0075? - } catch (InterruptedException e) { } - return null; - } - - // removing this from SwingWorker - //public void finished() { } - - public void interrupt() { - Thread t = workerVar.get(); - if (t != null) { - t.interrupt(); - } - workerVar.clear(); - } - - public Object get() { - while (true) { - Thread t = workerVar.get(); - if (t == null) { - return getValue(); - } - try { - t.join(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); // propagate - return null; - } - } - } - - public Worker() { - // removing this from SwingWorker - //final Runnable doFinished = new Runnable() { - // public void run() { finished(); } - // }; - - Runnable doConstruct = new Runnable() { - public void run() { - try { - setValue(construct()); - - } finally { - workerVar.clear(); - } - // removing this from SwingWorker to avoid swing - //javax.swing.SwingUtilities.invokeLater(doFinished); - } - }; - - Thread t = new Thread(doConstruct); - workerVar = new WorkerVar(t); - } - - public void start() { - Thread t = workerVar.get(); - if (t != null) t.start(); - } - } - - - /** - * Set this sketch to communicate its state back to the PDE. - *

- * This uses the stderr stream to write positions of the window - * (so that it will be saved by the PDE for the next run) and - * notify on quit. See more notes in the Worker class. - */ - public void setupExternalMessages() { //Frame parentFrame) { - //final Worker worker = new Worker(); - - //parentFrame.addComponentListener(new ComponentAdapter() { - frame.addComponentListener(new ComponentAdapter() { - public void componentMoved(ComponentEvent e) { - Point where = ((Frame) e.getSource()).getLocation(); - System.err.println(PApplet.EXTERNAL_MOVE + " " + - where.x + " " + where.y); - System.err.flush(); // doesn't seem to help or hurt - } - }); - - //parentFrame.addWindowListener(new WindowAdapter() { - frame.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - System.err.println(PApplet.EXTERNAL_QUIT); - System.err.flush(); // important - System.exit(0); - } - }); - } - - - /** - * Set up a listener that will fire proper component resize events - * in cases where frame.setResizable(true) is called. - */ - public void setupFrameResizeListener() { - frame.addComponentListener(new ComponentAdapter() { - - public void componentResized(ComponentEvent e) { - // might be multiple resize calls before visible (i.e. first - // when pack() is called, then when it's resized for use). - // ignore them because it's not the user resizing things. - Frame farm = (Frame) e.getComponent(); - if (farm.isVisible()) { - Insets insets = farm.getInsets(); - Dimension windowSize = farm.getSize(); - int usableW = windowSize.width - insets.left - insets.right; - int usableH = windowSize.height - insets.top - insets.bottom; - - // the ComponentListener in PApplet will handle calling size() - setBounds(insets.left, insets.top, usableW, usableH); - } - } - }); - } - - - /** - * main() method for running this class from the command line. - *

- * 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: - *

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 "); - System.err.println("For additional options, " + - "see the javadoc for PApplet"); - System.exit(1); - } - - try { - // true if this sketch is being run by the PDE - boolean external = false; - int location[] = null; - int editorLocation[] = null; - String folder = System.getProperty("user.dir"); - String name = null; - boolean present = false; - Color backgroundColor = Color.black; //BLACK; - Color stopColor = Color.gray; //GRAY; - GraphicsDevice displayDevice = null; - boolean hideStop = false; - - String param = null, value = null; - - int argIndex = 0; - while (argIndex < args.length) { - int equals = args[argIndex].indexOf('='); - if (equals != -1) { - param = args[argIndex].substring(0, equals); - value = args[argIndex].substring(equals + 1); - - if (param.equals(ARGS_EDITOR_LOCATION)) { - external = true; - editorLocation = toInt(split(value, ',')); - - } else if (param.equals(ARGS_DISPLAY)) { - int deviceIndex = Integer.parseInt(value) - 1; - - //DisplayMode dm = device.getDisplayMode(); - //if ((dm.getWidth() == 1024) && (dm.getHeight() == 768)) { - - GraphicsEnvironment environment = - GraphicsEnvironment.getLocalGraphicsEnvironment(); - GraphicsDevice devices[] = environment.getScreenDevices(); - if ((deviceIndex >= 0) && (deviceIndex < devices.length)) { - displayDevice = devices[deviceIndex]; - } else { - System.err.println("Display " + value + " does not exist, " + - "using the default display instead."); - } - - } else if (param.equals(ARGS_BGCOLOR)) { - if (value.charAt(0) == '#') value = value.substring(1); - backgroundColor = new Color(Integer.parseInt(value, 16)); - - } else if (param.equals(ARGS_STOP_COLOR)) { - if (value.charAt(0) == '#') value = value.substring(1); - stopColor = new Color(Integer.parseInt(value, 16)); - - } else if (param.equals(ARGS_SKETCH_FOLDER)) { - folder = value; - - } else if (param.equals(ARGS_LOCATION)) { - location = toInt(split(value, ',')); - } - - } else { - if (args[argIndex].equals(ARGS_PRESENT)) { - present = true; - - } else if (args[argIndex].equals(ARGS_HIDE_STOP)) { - hideStop = true; - - } else if (args[argIndex].equals(ARGS_EXTERNAL)) { - external = true; - - } else { - name = args[argIndex]; - break; - } - } - argIndex++; - } - - if (displayDevice == null) { - GraphicsEnvironment environment = - GraphicsEnvironment.getLocalGraphicsEnvironment(); - displayDevice = environment.getDefaultScreenDevice(); - } - - Frame frame = new Frame(displayDevice.getDefaultConfiguration()); - /* - Frame frame = null; - if (displayDevice != null) { - frame = new Frame(displayDevice.getDefaultConfiguration()); - } else { - frame = new Frame(); - } - */ - - Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); - - // remove the grow box by default - // users who want it back can call frame.setResizable(true) - frame.setResizable(false); - - Class c = Class.forName(name); - PApplet applet = (PApplet) c.newInstance(); - - // these are needed before init/start - applet.frame = frame; - applet.sketchPath = folder; - applet.args = PApplet.subset(args, 1); - - applet.init(); - - // wait until the applet has figured out its width - // hoping that this won't hang if the applet has an exception - while (applet.defaultSize && !applet.finished) { - try { - Thread.sleep(5); - - } catch (InterruptedException e) { } - } - - if (present) { - frame.setUndecorated(true); - frame.setBackground(backgroundColor); - displayDevice.setFullScreenWindow(frame); - - frame.add(applet); - Dimension fullscreen = frame.getSize(); - applet.setBounds((fullscreen.width - applet.width) / 2, - (fullscreen.height - applet.height) / 2, - applet.width, applet.height); - - if (!hideStop) { - Label label = new Label("stop"); - label.setForeground(stopColor); - label.addMouseListener(new MouseAdapter() { - public void mousePressed(MouseEvent e) { - System.exit(0); - } - }); - frame.add(label); - - Dimension labelSize = label.getPreferredSize(); - // sometimes shows up truncated on mac - //System.out.println("label width is " + labelSize.width); - labelSize = new Dimension(100, labelSize.height); - label.setSize(labelSize); - label.setLocation(20, fullscreen.height - labelSize.height - 20); - } - - // not always running externally when in present mode - if (external) { - applet.setupExternalMessages(); - } - - } else { // if not presenting - // can't do pack earlier cuz present mode don't like it - // (can't go full screen with a frame after calling pack) - frame.pack(); // get insets. get more. - Insets insets = frame.getInsets(); - - int windowW = Math.max(applet.width, MIN_WINDOW_WIDTH) + - insets.left + insets.right; - int windowH = Math.max(applet.height, MIN_WINDOW_HEIGHT) + - insets.top + insets.bottom; - - frame.setSize(windowW, windowH); - - if (location != null) { - // a specific location was received from PdeRuntime - // (applet has been run more than once, user placed window) - frame.setLocation(location[0], location[1]); - - } else if (external) { - int locationX = editorLocation[0] - 20; - int locationY = editorLocation[1]; - - if (locationX - windowW > 10) { - // if it fits to the left of the window - frame.setLocation(locationX - windowW, locationY); - - } else { // doesn't fit - // if it fits inside the editor window, - // offset slightly from upper lefthand corner - // so that it's plunked inside the text area - locationX = editorLocation[0] + 66; - locationY = editorLocation[1] + 66; - - if ((locationX + windowW > screen.width - 33) || - (locationY + windowH > screen.height - 33)) { - // otherwise center on screen - locationX = (screen.width - windowW) / 2; - locationY = (screen.height - windowH) / 2; - } - frame.setLocation(locationX, locationY); - } - } else { // just center on screen - frame.setLocation((screen.width - applet.width) / 2, - (screen.height - applet.height) / 2); - } - - frame.setLayout(null); - frame.add(applet); - - if (backgroundColor == Color.black) { //BLACK) { - // this means no bg color unless specified - backgroundColor = SystemColor.control; - } - frame.setBackground(backgroundColor); - - int usableWindowH = windowH - insets.top - insets.bottom; - applet.setBounds((windowW - applet.width)/2, - insets.top + (usableWindowH - applet.height)/2, - applet.width, applet.height); - - if (external) { - applet.setupExternalMessages(); - - } else { // !external - frame.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - System.exit(0); - } - }); - } - - // handle frame resizing events - applet.setupFrameResizeListener(); - - // all set for rockin - if (applet.displayable()) { - frame.setVisible(true); - } - } - - //System.out.println("showing frame"); - //System.out.println("applet requesting focus"); - applet.requestFocus(); // ask for keydowns - //System.out.println("exiting main()"); - - } catch (Exception e) { - e.printStackTrace(); - System.exit(1); - } - } - - - ////////////////////////////////////////////////////////////// - - - public PGraphics beginRecord(String renderer, String filename) { - filename = insertFrame(filename); - PGraphics rec = createGraphics(width, height, renderer, filename); - beginRecord(rec); - return rec; - } - - - public void beginRecord(PGraphics recorder) { - this.recorder = recorder; - recorder.beginDraw(); - } - - - public void endRecord() { - //println("endRecord()"); - //if (!recorderNull) { - if (recorder != null) { - //recorder.endRecord(); - recorder.endDraw(); - recorder.dispose(); - recorder = null; - } - } - - - public PGraphics beginRaw(String renderer, String filename) { - //filename = savePath(filename); // ensure an absolute path - filename = insertFrame(filename); - PGraphics rec = createGraphics(width, height, renderer, filename); - //g.recordRaw(rec); - g.beginRaw(rec); - return rec; - } - - - /** - * Check a string for #### signs to see if the frame number should be - * inserted. Used for functions like saveFrame() and beginRecord() to - * replace the # marks with the frame number. If only one # is used, - * it will be ignored, under the assumption that it's probably not - * intended to be the frame number. - */ - public String insertFrame(String what) { - int first = what.indexOf('#'); - int last = what.lastIndexOf('#'); - - if ((first != -1) && (last - first > 0)) { - String prefix = what.substring(0, first); - int count = last - first + 1; - String suffix = what.substring(last + 1); - return prefix + nf(frameCount, count) + suffix; - } - return what; // no change - } - - - ////////////////////////////////////////////////////////////// - - - public void loadPixels() { - System.err.println("Use beginPixels() instead of loadPixels() " + - "with release 0116 and later."); - } - - - public void beginPixels() { - g.beginPixels(); - pixels = g.pixels; - } - - - ////////////////////////////////////////////////////////////// - - // everything below this line is automatically generated. no touch. - // public functions for processing.core - - - public void imageMode(int mode) { - if (recorder != null) recorder.imageMode(mode); - g.imageMode(mode); - } - - - public void smooth() { - if (recorder != null) recorder.smooth(); - g.smooth(); - } - - - public void noSmooth() { - if (recorder != null) recorder.noSmooth(); - g.noSmooth(); - } - - - public void endPixels() { - if (recorder != null) recorder.endPixels(); - g.endPixels(); - } - - - public void endPixels(int x1, int y1, int x2, int y2) { - if (recorder != null) recorder.endPixels(x1, y1, x2, y2); - g.endPixels(x1, y1, x2, y2); - } - - - public int get(int x, int y) { - return g.get(x, y); - } - - - public PImage get(int x, int y, int w, int h) { - return g.get(x, y, w, h); - } - - - public PImage get() { - return g.get(); - } - - - public void set(int x, int y, int c) { - if (recorder != null) recorder.set(x, y, c); - g.set(x, y, c); - } - - - public void set(int dx, int dy, PImage src) { - if (recorder != null) recorder.set(dx, dy, src); - g.set(dx, dy, src); - } - - - public void mask(int alpha[]) { - if (recorder != null) recorder.mask(alpha); - g.mask(alpha); - } - - - public void mask(PImage alpha) { - if (recorder != null) recorder.mask(alpha); - g.mask(alpha); - } - - - public void filter(int kind) { - if (recorder != null) recorder.filter(kind); - g.filter(kind); - } - - - public void filter(int kind, float param) { - if (recorder != null) recorder.filter(kind, param); - g.filter(kind, param); - } - - - public void copy(int sx1, int sy1, int sx2, int sy2, - int dx1, int dy1, int dx2, int dy2) { - if (recorder != null) recorder.copy(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); - g.copy(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); - } - - - public void copy(PImage src, - int sx1, int sy1, int sx2, int sy2, - int dx1, int dy1, int dx2, int dy2) { - if (recorder != null) recorder.copy(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); - g.copy(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); - } - - - static public int blend(int c1, int c2, int mode) { - return PGraphics.blend(c1, c2, mode); - } - - - public void blend(int sx, int sy, int dx, int dy, int mode) { - if (recorder != null) recorder.blend(sx, sy, dx, dy, mode); - g.blend(sx, sy, dx, dy, mode); - } - - - public void blend(PImage src, - int sx, int sy, int dx, int dy, int mode) { - if (recorder != null) recorder.blend(src, sx, sy, dx, dy, mode); - g.blend(src, sx, sy, dx, dy, mode); - } - - - public void blend(int sx1, int sy1, int sx2, int sy2, - int dx1, int dy1, int dx2, int dy2, int mode) { - if (recorder != null) recorder.blend(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); - g.blend(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); - } - - - public void blend(PImage src, - int sx1, int sy1, int sx2, int sy2, - int dx1, int dy1, int dx2, int dy2, int mode) { - if (recorder != null) recorder.blend(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); - g.blend(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); - } - - - public void hint(int which) { - if (recorder != null) recorder.hint(which); - g.hint(which); - } - - - public void noHint(int which) { - if (recorder != null) recorder.noHint(which); - g.noHint(which); - } - - - public void beginShape() { - if (recorder != null) recorder.beginShape(); - g.beginShape(); - } - - - public void beginShape(int kind) { - if (recorder != null) recorder.beginShape(kind); - g.beginShape(kind); - } - - - public void normal(float nx, float ny, float nz) { - if (recorder != null) recorder.normal(nx, ny, nz); - g.normal(nx, ny, nz); - } - - - public void textureMode(int mode) { - if (recorder != null) recorder.textureMode(mode); - g.textureMode(mode); - } - - - public void texture(PImage image) { - if (recorder != null) recorder.texture(image); - g.texture(image); - } - - - public void vertex(float x, float y) { - if (recorder != null) recorder.vertex(x, y); - g.vertex(x, y); - } - - - public void vertex(float x, float y, float z) { - if (recorder != null) recorder.vertex(x, y, z); - g.vertex(x, y, z); - } - - - public void vertex(float x, float y, float u, float v) { - if (recorder != null) recorder.vertex(x, y, u, v); - g.vertex(x, y, u, v); - } - - - public void vertex(float x, float y, float z, float u, float v) { - if (recorder != null) recorder.vertex(x, y, z, u, v); - g.vertex(x, y, z, u, v); - } - - - public void bezierVertex(float x1, float y1, - float x2, float y2, - float x3, float y3) { - if (recorder != null) recorder.bezierVertex(x1, y1, x2, y2, x3, y3); - g.bezierVertex(x1, y1, x2, y2, x3, y3); - } - - - public void bezierVertex(float x1, float y1, float z1, - float x2, float y2, float z2, - float x3, float y3, float z3) { - if (recorder != null) recorder.bezierVertex(x1, y1, z1, x2, y2, z2, x3, y3, z3); - g.bezierVertex(x1, y1, z1, x2, y2, z2, x3, y3, z3); - } - - - public void curveVertex(float x, float y) { - if (recorder != null) recorder.curveVertex(x, y); - g.curveVertex(x, y); - } - - - public void curveVertex(float x, float y, float z) { - if (recorder != null) recorder.curveVertex(x, y, z); - g.curveVertex(x, y, z); - } - - - public void endShape() { - if (recorder != null) recorder.endShape(); - g.endShape(); - } - - - public void point(float x, float y) { - if (recorder != null) recorder.point(x, y); - g.point(x, y); - } - - - public void point(float x, float y, float z) { - if (recorder != null) recorder.point(x, y, z); - g.point(x, y, z); - } - - - public void line(float x1, float y1, float x2, float y2) { - if (recorder != null) recorder.line(x1, y1, x2, y2); - g.line(x1, y1, x2, y2); - } - - - public void line(float x1, float y1, float z1, - float x2, float y2, float z2) { - if (recorder != null) recorder.line(x1, y1, z1, x2, y2, z2); - g.line(x1, y1, z1, x2, y2, z2); - } - - - public void triangle(float x1, float y1, float x2, float y2, - float x3, float y3) { - if (recorder != null) recorder.triangle(x1, y1, x2, y2, x3, y3); - g.triangle(x1, y1, x2, y2, x3, y3); - } - - - public void quad(float x1, float y1, float x2, float y2, - float x3, float y3, float x4, float y4) { - if (recorder != null) recorder.quad(x1, y1, x2, y2, x3, y3, x4, y4); - g.quad(x1, y1, x2, y2, x3, y3, x4, y4); - } - - - public void rectMode(int mode) { - if (recorder != null) recorder.rectMode(mode); - g.rectMode(mode); - } - - - public void rect(float x1, float y1, float x2, float y2) { - if (recorder != null) recorder.rect(x1, y1, x2, y2); - g.rect(x1, y1, x2, y2); - } - - - public void ellipseMode(int mode) { - if (recorder != null) recorder.ellipseMode(mode); - g.ellipseMode(mode); - } - - - public void ellipse(float a, float b, float c, float d) { - if (recorder != null) recorder.ellipse(a, b, c, d); - g.ellipse(a, b, c, d); - } - - - public void arc(float a, float b, float c, float d, - float start, float stop) { - if (recorder != null) recorder.arc(a, b, c, d, start, stop); - g.arc(a, b, c, d, start, stop); - } - - - public void box(float size) { - if (recorder != null) recorder.box(size); - g.box(size); - } - - - public void box(float w, float h, float d) { - if (recorder != null) recorder.box(w, h, d); - g.box(w, h, d); - } - - - public void sphereDetail(int res) { - if (recorder != null) recorder.sphereDetail(res); - g.sphereDetail(res); - } - - - public void sphere(float r) { - if (recorder != null) recorder.sphere(r); - g.sphere(r); - } - - - public float bezierPoint(float a, float b, float c, float d, float t) { - return g.bezierPoint(a, b, c, d, t); - } - - - public float bezierTangent(float a, float b, float c, float d, float t) { - return g.bezierTangent(a, b, c, d, t); - } - - - public void bezierDetail(int detail) { - if (recorder != null) recorder.bezierDetail(detail); - g.bezierDetail(detail); - } - - - public void bezier(float x1, float y1, - float x2, float y2, - float x3, float y3, - float x4, float y4) { - if (recorder != null) recorder.bezier(x1, y1, x2, y2, x3, y3, x4, y4); - g.bezier(x1, y1, x2, y2, x3, y3, x4, y4); - } - - - 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) { - if (recorder != null) recorder.bezier(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4); - g.bezier(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4); - } - - - public float curvePoint(float a, float b, float c, float d, float t) { - return g.curvePoint(a, b, c, d, t); - } - - - public float curveTangent(float a, float b, float c, float d, - float t) { - return g.curveTangent(a, b, c, d, t); - } - - - public void curveDetail(int detail) { - if (recorder != null) recorder.curveDetail(detail); - g.curveDetail(detail); - } - - - public void curveTightness(float tightness) { - if (recorder != null) recorder.curveTightness(tightness); - g.curveTightness(tightness); - } - - - public void curve(float x1, float y1, - float x2, float y2, - float x3, float y3, - float x4, float y4) { - if (recorder != null) recorder.curve(x1, y1, x2, y2, x3, y3, x4, y4); - g.curve(x1, y1, x2, y2, x3, y3, x4, y4); - } - - - 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) { - if (recorder != null) recorder.curve(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4); - g.curve(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4); - } - - - public void image(PImage image, float x, float y) { - if (recorder != null) recorder.image(image, x, y); - g.image(image, x, y); - } - - - public void image(PImage image, - float x, float y, float c, float d) { - if (recorder != null) recorder.image(image, x, y, c, d); - g.image(image, x, y, c, d); - } - - - public void image(PImage image, - float a, float b, float c, float d, - int u1, int v1, int u2, int v2) { - if (recorder != null) recorder.image(image, a, b, c, d, u1, v1, u2, v2); - g.image(image, a, b, c, d, u1, v1, u2, v2); - } - - - public void textAlign(int align) { - if (recorder != null) recorder.textAlign(align); - g.textAlign(align); - } - - - public float textAscent() { - return g.textAscent(); - } - - - public float textDescent() { - return g.textDescent(); - } - - - public void textFont(PFont which) { - if (recorder != null) recorder.textFont(which); - g.textFont(which); - } - - - public void textFont(PFont which, float size) { - if (recorder != null) recorder.textFont(which, size); - g.textFont(which, size); - } - - - public void textLeading(float leading) { - if (recorder != null) recorder.textLeading(leading); - g.textLeading(leading); - } - - - public void textMode(int mode) { - if (recorder != null) recorder.textMode(mode); - g.textMode(mode); - } - - - public void textSize(float size) { - if (recorder != null) recorder.textSize(size); - g.textSize(size); - } - - - public float textWidth(char c) { - return g.textWidth(c); - } - - - public float textWidth(String str) { - return g.textWidth(str); - } - - - public void text(char c) { - if (recorder != null) recorder.text(c); - g.text(c); - } - - - public void text(char c, float x, float y) { - if (recorder != null) recorder.text(c, x, y); - g.text(c, x, y); - } - - - public void text(char c, float x, float y, float z) { - if (recorder != null) recorder.text(c, x, y, z); - g.text(c, x, y, z); - } - - - public void text(String str) { - if (recorder != null) recorder.text(str); - g.text(str); - } - - - public void text(String str, float x, float y) { - if (recorder != null) recorder.text(str, x, y); - g.text(str, x, y); - } - - - public void text(String str, float x, float y, float z) { - if (recorder != null) recorder.text(str, x, y, z); - g.text(str, x, y, z); - } - - - public void text(String str, float x1, float y1, float x2, float y2) { - if (recorder != null) recorder.text(str, x1, y1, x2, y2); - g.text(str, x1, y1, x2, y2); - } - - - public void text(String s, float x1, float y1, float x2, float y2, float z) { - if (recorder != null) recorder.text(s, x1, y1, x2, y2, z); - g.text(s, x1, y1, x2, y2, z); - } - - - public void text(int num, float x, float y) { - if (recorder != null) recorder.text(num, x, y); - g.text(num, x, y); - } - - - public void text(int num, float x, float y, float z) { - if (recorder != null) recorder.text(num, x, y, z); - g.text(num, x, y, z); - } - - - public void text(float num, float x, float y) { - if (recorder != null) recorder.text(num, x, y); - g.text(num, x, y); - } - - - public void text(float num, float x, float y, float z) { - if (recorder != null) recorder.text(num, x, y, z); - g.text(num, x, y, z); - } - - - public void translate(float tx, float ty) { - if (recorder != null) recorder.translate(tx, ty); - g.translate(tx, ty); - } - - - public void translate(float tx, float ty, float tz) { - if (recorder != null) recorder.translate(tx, ty, tz); - g.translate(tx, ty, tz); - } - - - public void rotate(float angle) { - if (recorder != null) recorder.rotate(angle); - g.rotate(angle); - } - - - public void rotateX(float angle) { - if (recorder != null) recorder.rotateX(angle); - g.rotateX(angle); - } - - - public void rotateY(float angle) { - if (recorder != null) recorder.rotateY(angle); - g.rotateY(angle); - } - - - public void rotateZ(float angle) { - if (recorder != null) recorder.rotateZ(angle); - g.rotateZ(angle); - } - - - public void rotate(float angle, float vx, float vy, float vz) { - if (recorder != null) recorder.rotate(angle, vx, vy, vz); - g.rotate(angle, vx, vy, vz); - } - - - public void scale(float s) { - if (recorder != null) recorder.scale(s); - g.scale(s); - } - - - public void scale(float sx, float sy) { - if (recorder != null) recorder.scale(sx, sy); - g.scale(sx, sy); - } - - - public void scale(float x, float y, float z) { - if (recorder != null) recorder.scale(x, y, z); - g.scale(x, y, z); - } - - - public void pushMatrix() { - if (recorder != null) recorder.pushMatrix(); - g.pushMatrix(); - } - - - public void popMatrix() { - if (recorder != null) recorder.popMatrix(); - g.popMatrix(); - } - - - public void resetMatrix() { - if (recorder != null) recorder.resetMatrix(); - g.resetMatrix(); - } - - - public void applyMatrix(float n00, float n01, float n02, - float n10, float n11, float n12) { - if (recorder != null) recorder.applyMatrix(n00, n01, n02, n10, n11, n12); - g.applyMatrix(n00, n01, n02, n10, n11, n12); - } - - - 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) { - if (recorder != null) recorder.applyMatrix(n00, n01, n02, n03, n10, n11, n12, n13, n20, n21, n22, n23, n30, n31, n32, n33); - g.applyMatrix(n00, n01, n02, n03, n10, n11, n12, n13, n20, n21, n22, n23, n30, n31, n32, n33); - } - - - public void loadMatrix() { - if (recorder != null) recorder.loadMatrix(); - g.loadMatrix(); - } - - - public void printMatrix() { - if (recorder != null) recorder.printMatrix(); - g.printMatrix(); - } - - - public void beginCamera() { - if (recorder != null) recorder.beginCamera(); - g.beginCamera(); - } - - - public void endCamera() { - if (recorder != null) recorder.endCamera(); - g.endCamera(); - } - - - public void camera() { - if (recorder != null) recorder.camera(); - g.camera(); - } - - - public void camera(float eyeX, float eyeY, float eyeZ, - float centerX, float centerY, float centerZ, - float upX, float upY, float upZ) { - if (recorder != null) recorder.camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); - g.camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); - } - - - public void printCamera() { - if (recorder != null) recorder.printCamera(); - g.printCamera(); - } - - - public void ortho() { - if (recorder != null) recorder.ortho(); - g.ortho(); - } - - - public void ortho(float left, float right, - float bottom, float top, - float near, float far) { - if (recorder != null) recorder.ortho(left, right, bottom, top, near, far); - g.ortho(left, right, bottom, top, near, far); - } - - - public void perspective() { - if (recorder != null) recorder.perspective(); - g.perspective(); - } - - - public void perspective(float fovy, float aspect, float zNear, float zFar) { - if (recorder != null) recorder.perspective(fovy, aspect, zNear, zFar); - g.perspective(fovy, aspect, zNear, zFar); - } - - - public void frustum(float left, float right, float bottom, - float top, float znear, float zfar) { - if (recorder != null) recorder.frustum(left, right, bottom, top, znear, zfar); - g.frustum(left, right, bottom, top, znear, zfar); - } - - - public void printProjection() { - if (recorder != null) recorder.printProjection(); - g.printProjection(); - } - - - public float screenX(float x, float y) { - return g.screenX(x, y); - } - - - public float screenY(float x, float y) { - return g.screenY(x, y); - } - - - public float screenX(float x, float y, float z) { - return g.screenX(x, y, z); - } - - - public float screenY(float x, float y, float z) { - return g.screenY(x, y, z); - } - - - public float screenZ(float x, float y, float z) { - return g.screenZ(x, y, z); - } - - - public float modelX(float x, float y, float z) { - return g.modelX(x, y, z); - } - - - public float modelY(float x, float y, float z) { - return g.modelY(x, y, z); - } - - - public float modelZ(float x, float y, float z) { - return g.modelZ(x, y, z); - } - - - public void colorMode(int mode) { - if (recorder != null) recorder.colorMode(mode); - g.colorMode(mode); - } - - - public void colorMode(int mode, float max) { - if (recorder != null) recorder.colorMode(mode, max); - g.colorMode(mode, max); - } - - - public void colorMode(int mode, - float maxX, float maxY, float maxZ) { - if (recorder != null) recorder.colorMode(mode, maxX, maxY, maxZ); - g.colorMode(mode, maxX, maxY, maxZ); - } - - - public void colorMode(int mode, - float maxX, float maxY, float maxZ, float maxA) { - if (recorder != null) recorder.colorMode(mode, maxX, maxY, maxZ, maxA); - g.colorMode(mode, maxX, maxY, maxZ, maxA); - } - - - public void strokeWeight(float weight) { - if (recorder != null) recorder.strokeWeight(weight); - g.strokeWeight(weight); - } - - - public void strokeJoin(int join) { - if (recorder != null) recorder.strokeJoin(join); - g.strokeJoin(join); - } - - - public void strokeCap(int cap) { - if (recorder != null) recorder.strokeCap(cap); - g.strokeCap(cap); - } - - - public void noStroke() { - if (recorder != null) recorder.noStroke(); - g.noStroke(); - } - - - public void stroke(int rgb) { - if (recorder != null) recorder.stroke(rgb); - g.stroke(rgb); - } - - - public void stroke(int rgb, float alpha) { - if (recorder != null) recorder.stroke(rgb, alpha); - g.stroke(rgb, alpha); - } - - - public void stroke(float gray) { - if (recorder != null) recorder.stroke(gray); - g.stroke(gray); - } - - - public void stroke(float gray, float alpha) { - if (recorder != null) recorder.stroke(gray, alpha); - g.stroke(gray, alpha); - } - - - public void stroke(float x, float y, float z) { - if (recorder != null) recorder.stroke(x, y, z); - g.stroke(x, y, z); - } - - - public void stroke(float x, float y, float z, float a) { - if (recorder != null) recorder.stroke(x, y, z, a); - g.stroke(x, y, z, a); - } - - - public void noTint() { - if (recorder != null) recorder.noTint(); - g.noTint(); - } - - - public void tint(int rgb) { - if (recorder != null) recorder.tint(rgb); - g.tint(rgb); - } - - - public void tint(int rgb, float alpha) { - if (recorder != null) recorder.tint(rgb, alpha); - g.tint(rgb, alpha); - } - - - public void tint(float gray) { - if (recorder != null) recorder.tint(gray); - g.tint(gray); - } - - - public void tint(float gray, float alpha) { - if (recorder != null) recorder.tint(gray, alpha); - g.tint(gray, alpha); - } - - - public void tint(float x, float y, float z) { - if (recorder != null) recorder.tint(x, y, z); - g.tint(x, y, z); - } - - - public void tint(float x, float y, float z, float a) { - if (recorder != null) recorder.tint(x, y, z, a); - g.tint(x, y, z, a); - } - - - public void noFill() { - if (recorder != null) recorder.noFill(); - g.noFill(); - } - - - public void fill(int rgb) { - if (recorder != null) recorder.fill(rgb); - g.fill(rgb); - } - - - public void fill(int rgb, float alpha) { - if (recorder != null) recorder.fill(rgb, alpha); - g.fill(rgb, alpha); - } - - - public void fill(float gray) { - if (recorder != null) recorder.fill(gray); - g.fill(gray); - } - - - public void fill(float gray, float alpha) { - if (recorder != null) recorder.fill(gray, alpha); - g.fill(gray, alpha); - } - - - public void fill(float x, float y, float z) { - if (recorder != null) recorder.fill(x, y, z); - g.fill(x, y, z); - } - - - public void fill(float x, float y, float z, float a) { - if (recorder != null) recorder.fill(x, y, z, a); - g.fill(x, y, z, a); - } - - - public void ambient(int rgb) { - if (recorder != null) recorder.ambient(rgb); - g.ambient(rgb); - } - - - public void ambient(float gray) { - if (recorder != null) recorder.ambient(gray); - g.ambient(gray); - } - - - public void ambient(float x, float y, float z) { - if (recorder != null) recorder.ambient(x, y, z); - g.ambient(x, y, z); - } - - - public void specular(int rgb) { - if (recorder != null) recorder.specular(rgb); - g.specular(rgb); - } - - - public void specular(float gray) { - if (recorder != null) recorder.specular(gray); - g.specular(gray); - } - - - public void specular(float gray, float alpha) { - if (recorder != null) recorder.specular(gray, alpha); - g.specular(gray, alpha); - } - - - public void specular(float x, float y, float z) { - if (recorder != null) recorder.specular(x, y, z); - g.specular(x, y, z); - } - - - public void specular(float x, float y, float z, float a) { - if (recorder != null) recorder.specular(x, y, z, a); - g.specular(x, y, z, a); - } - - - public void shininess(float shine) { - if (recorder != null) recorder.shininess(shine); - g.shininess(shine); - } - - - public void emissive(int rgb) { - if (recorder != null) recorder.emissive(rgb); - g.emissive(rgb); - } - - - public void emissive(float gray) { - if (recorder != null) recorder.emissive(gray); - g.emissive(gray); - } - - - public void emissive(float x, float y, float z ) { - if (recorder != null) recorder.emissive(x, y, z); - g.emissive(x, y, z); - } - - - public void lights() { - if (recorder != null) recorder.lights(); - g.lights(); - } - - - public void ambientLight(float red, float green, float blue) { - if (recorder != null) recorder.ambientLight(red, green, blue); - g.ambientLight(red, green, blue); - } - - - public void ambientLight(float red, float green, float blue, - float x, float y, float z) { - if (recorder != null) recorder.ambientLight(red, green, blue, x, y, z); - g.ambientLight(red, green, blue, x, y, z); - } - - - public void directionalLight(float red, float green, float blue, - float nx, float ny, float nz) { - if (recorder != null) recorder.directionalLight(red, green, blue, nx, ny, nz); - g.directionalLight(red, green, blue, nx, ny, nz); - } - - - public void pointLight(float red, float green, float blue, - float x, float y, float z) { - if (recorder != null) recorder.pointLight(red, green, blue, x, y, z); - g.pointLight(red, green, blue, x, y, z); - } - - - 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) { - if (recorder != null) recorder.spotLight(red, green, blue, x, y, z, nx, ny, nz, angle, concentration); - g.spotLight(red, green, blue, x, y, z, nx, ny, nz, angle, concentration); - } - - - public void lightFalloff(float constant, float linear, float quadratic) { - if (recorder != null) recorder.lightFalloff(constant, linear, quadratic); - g.lightFalloff(constant, linear, quadratic); - } - - - public void lightSpecular(float x, float y, float z) { - if (recorder != null) recorder.lightSpecular(x, y, z); - g.lightSpecular(x, y, z); - } - - - public void background(int rgb) { - if (recorder != null) recorder.background(rgb); - g.background(rgb); - } - - - public void background(float gray) { - if (recorder != null) recorder.background(gray); - g.background(gray); - } - - - public void background(float x, float y, float z) { - if (recorder != null) recorder.background(x, y, z); - g.background(x, y, z); - } - - - public void background(PImage image) { - if (recorder != null) recorder.background(image); - g.background(image); - } - - - public final float alpha(int what) { - return g.alpha(what); - } - - - public final float red(int what) { - return g.red(what); - } - - - public final float green(int what) { - return g.green(what); - } - - - public final float blue(int what) { - return g.blue(what); - } - - - public final float hue(int what) { - return g.hue(what); - } - - - public final float saturation(int what) { - return g.saturation(what); - } - - - public final float brightness(int what) { - return g.brightness(what); - } - - - public void beginRaw(PGraphics raw) { - if (recorder != null) recorder.beginRaw(raw); - g.beginRaw(raw); - } - - - public void endRaw() { - if (recorder != null) recorder.endRaw(); - g.endRaw(); - } - - - public boolean displayable() { - return g.displayable(); - } -} +/* -*- 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.applet.*; +import java.awt.*; +import java.awt.event.*; +import java.awt.image.*; +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.text.*; +import java.util.*; +import java.util.zip.*; + + +/** + * Base class for all sketches that use processing.core. + */ +public class PApplet extends Applet + implements PConstants, Runnable, + MouseListener, MouseMotionListener, KeyListener, FocusListener +{ + /** + * "1.3" or "1.1" or whatever (just the first three chars) + */ + public static final String javaVersionName = + System.getProperty("java.version").substring(0,3); + + /** + * Version of Java that's in use, whether 1.1 or 1.3 or whatever, + * stored as a float. + *

+ * Note that because this is stored as a float, the values may + * not be exactly 1.3 or 1.4. Instead, make sure you're + * comparing against 1.3f or 1.4f, which will have the same amount + * of error (i.e. 1.40000001). This could just be a double, but + * since Processing only uses floats, it's safer 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<>= 1; + } + + if (x<0) x=-x; + if (y<0) y=-y; + if (z<0) z=-z; + + int xi=(int)x, yi=(int)y, zi=(int)z; + float xf = (float)(x-xi); + float yf = (float)(y-yi); + float zf = (float)(z-zi); + float rxf, ryf; + + float r=0; + float ampl=0.5f; + + float n1,n2,n3; + + for (int i=0; i=1.0f) { xi++; xf--; } + if (yf>=1.0f) { yi++; yf--; } + if (zf>=1.0f) { zi++; zf--; } + } + return r; + } + + // [toxi 031112] + // now adjusts to the size of the cosLUT used via + // the new variables, defined above + private float noise_fsc(float i) { + // using bagel's cosine table instead + return 0.5f*(1.0f-perlin_cosTable[(int)(i*perlin_PI)%perlin_TWOPI]); + } + + // [toxi 040903] + // make perlin noise quality user controlled to allow + // for different levels of detail. lower values will produce + // smoother results as higher octaves are surpressed + + public void noiseDetail(int lod) { + if (lod>0) perlin_octaves=lod; + } + + public void noiseDetail(int lod, float falloff) { + if (lod>0) perlin_octaves=lod; + if (falloff>0) perlin_amp_falloff=falloff; + } + + public void noiseSeed(long what) { + if (perlinRandom == null) perlinRandom = new Random(); + perlinRandom.setSeed(what); + } + + + + ////////////////////////////////////////////////////////////// + + // SOUND I/O + + /* + public PSound loadSound(String filename) { + if (PApplet.javaVersion >= 1.3f) { + return new PSound2(this, openStream(filename)); + } + return new PSound(this, openStream(filename)); + } + */ + + + ////////////////////////////////////////////////////////////// + + // IMAGE I/O + + + Hashtable imageTable; + + /** + * Draw an image based on its filename. This is less efficient than + * using loadImage because there's no way to unload it from memory, + * but it's useful for beginners. + */ + public void image(String filename, float x, float y) { + image(tableImage(filename), x, y); + } + + /** + * Draw an image based on its filename. This is less than efficient + * than using loadImage because there's no way to unload it from memory, + * but it's useful for beginners. + */ + public void image(String filename, + float x, float y, float c, float d) { + image(tableImage(filename), x, y, c, d); + } + + /** + * Draw an image based on its filename. This is less than efficient + * than using loadImage because there's no way to unload it from memory, + * but it's useful for beginners. + */ + public void image(String filename, + float a, float b, float c, float d, + int u1, int v1, int u2, int v2) { + image(tableImage(filename), a, b, c, d, u1, v1, u2, v2); + } + + + /** + * Load an image and store it in a table based on its name. + */ + protected PImage tableImage(String filename) { + if (imageTable == null) imageTable = new Hashtable(); + + PImage image = (PImage) imageTable.get(filename); + if (image != null) return image; + + image = loadImage(filename); + return image; + } + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + + protected String[] loadImageFormats; + + + /** + * Load an image from the data folder or a local directory. + * Supports .gif (including transparency), .tga, and .jpg images. + * In Java 1.3 or later, .png images are + * + * also supported. + *

+ * 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. + *

+ * Only works with Java 1.3 or later. Many .otf fonts don't seem + * to be supported by Java, perhaps because they're CFF based? + *

+ * Font names are inconsistent across platforms and Java versions. + * On Mac OS X, Java 1.3 uses the font menu name of the font, + * whereas Java 1.4 uses the PostScript name of the font. Java 1.4 + * on OS X will also accept the font menu name as well. On Windows, + * it appears that only the menu names are used, no matter what + * Java version is in use. Naming system unknown/untested for 1.5. + *

+ * Use 'null' for the charset if you want to use any of the 65,536 + * unicode characters that exist in the font. Note that this can + * produce an enormous file or may cause an OutOfMemoryError. + */ + public PFont createFont(String name, float size, + boolean smooth, char charset[]) { + if (PApplet.javaVersion < 1.3f) { + throw new RuntimeException("Can only create fonts with " + + "Java 1.3 or higher"); + } + + String lowerName = name.toLowerCase(); + Font font = null; + + try { + Method deriveFontMethod = + Font.class.getMethod("deriveFont", + new Class[] { Float.TYPE }); + Float floatSize = new Float(size); + + if (lowerName.endsWith(".otf") || lowerName.endsWith(".ttf")) { + //font = Font.createFont(Font.TRUETYPE_FONT, openStream(name)); + Method createFontMethod = + Font.class.getMethod("createFont", + new Class[] { Integer.TYPE, + InputStream.class }); + Field ttf = Font.class.getField("TRUETYPE_FONT"); + Integer ttfInteger = new Integer(ttf.getInt(ttf)); + Font baseFont = (Font) + createFontMethod.invoke(name, + new Object[] { ttfInteger, + openStream(name) }); + font = (Font) deriveFontMethod.invoke(baseFont, + new Object[] { floatSize }); + } else { + Font baseFont = new Font(name, Font.PLAIN, 1); + font = (Font) + deriveFontMethod.invoke(baseFont, new Object[] { floatSize }); + } + + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Problem using createFont() " + + "with the file " + name); + } + return new PFont(font, smooth, charset); + } + + + + ////////////////////////////////////////////////////////////// + + // FILE INPUT + + + public File inputFile() { + return inputFile("Select a file..."); + } + + + public File inputFile(String prompt) { + Frame parentFrame = null; + Component comp = getParent(); + while (comp != null) { + if (comp instanceof Frame) { + parentFrame = (Frame) comp; + break; + } + comp = comp.getParent(); + } + return inputFile(prompt, parentFrame); + } + + + static public File inputFile(Frame parent) { + return inputFile("Select a file...", parent); + } + + + /** + * static version of inputFile 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. + */ + // 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: + *

+ */ + 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. + *

+ * 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 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. + *

+ * 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. + *

+   * 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. + *

+ * 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). + *

+   * 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. + *

+ * 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. + *

+ * From a software engineering standpoint, using the stderr stream + * is highly problematic because of its tendency to die or act + * funny, especially on Windows. Threading issues can cause the + * buffers to get full or the applet to not run properly. + * Formerly known as the "code folder bug", this has been fixed + * through the use of this class, however it remains a tenuous + * situation that could perhaps break in a future JDK release. + */ + class Worker { + private Object value; + private WorkerVar workerVar; + + protected synchronized Object getValue() { + return value; + } + + private synchronized void setValue(Object x) { + value = x; + } + + public Object construct() { + try { + int anything = System.in.read(); + if (anything == EXTERNAL_STOP) { + + // adding this for 0073.. need to stop libraries + // when the stop button is hit. + PApplet.this.stop(); + finished = true; + } + } catch (IOException e) { + finished = true; + } + try { + Thread.sleep(250); + //Thread.sleep(100); // kick up latency for 0075? + } catch (InterruptedException e) { } + return null; + } + + // removing this from SwingWorker + //public void finished() { } + + public void interrupt() { + Thread t = workerVar.get(); + if (t != null) { + t.interrupt(); + } + workerVar.clear(); + } + + public Object get() { + while (true) { + Thread t = workerVar.get(); + if (t == null) { + return getValue(); + } + try { + t.join(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // propagate + return null; + } + } + } + + public Worker() { + // removing this from SwingWorker + //final Runnable doFinished = new Runnable() { + // public void run() { finished(); } + // }; + + Runnable doConstruct = new Runnable() { + public void run() { + try { + setValue(construct()); + + } finally { + workerVar.clear(); + } + // removing this from SwingWorker to avoid swing + //javax.swing.SwingUtilities.invokeLater(doFinished); + } + }; + + Thread t = new Thread(doConstruct); + workerVar = new WorkerVar(t); + } + + public void start() { + Thread t = workerVar.get(); + if (t != null) t.start(); + } + } + + + /** + * Set this sketch to communicate its state back to the PDE. + *

+ * This uses the stderr stream to write positions of the window + * (so that it will be saved by the PDE for the next run) and + * notify on quit. See more notes in the Worker class. + */ + public void setupExternalMessages() { //Frame parentFrame) { + //final Worker worker = new Worker(); + + //parentFrame.addComponentListener(new ComponentAdapter() { + frame.addComponentListener(new ComponentAdapter() { + public void componentMoved(ComponentEvent e) { + Point where = ((Frame) e.getSource()).getLocation(); + System.err.println(PApplet.EXTERNAL_MOVE + " " + + where.x + " " + where.y); + System.err.flush(); // doesn't seem to help or hurt + } + }); + + //parentFrame.addWindowListener(new WindowAdapter() { + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + System.err.println(PApplet.EXTERNAL_QUIT); + System.err.flush(); // important + System.exit(0); + } + }); + } + + + /** + * Set up a listener that will fire proper component resize events + * in cases where frame.setResizable(true) is called. + */ + public void setupFrameResizeListener() { + frame.addComponentListener(new ComponentAdapter() { + + public void componentResized(ComponentEvent e) { + // might be multiple resize calls before visible (i.e. first + // when pack() is called, then when it's resized for use). + // ignore them because it's not the user resizing things. + Frame farm = (Frame) e.getComponent(); + if (farm.isVisible()) { + Insets insets = farm.getInsets(); + Dimension windowSize = farm.getSize(); + int usableW = windowSize.width - insets.left - insets.right; + int usableH = windowSize.height - insets.top - insets.bottom; + + // the ComponentListener in PApplet will handle calling size() + setBounds(insets.left, insets.top, usableW, usableH); + } + } + }); + } + + + /** + * main() method for running this class from the command line. + *

+ * 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: + *

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 "); + System.err.println("For additional options, " + + "see the javadoc for PApplet"); + System.exit(1); + } + + try { + // true if this sketch is being run by the PDE + boolean external = false; + int location[] = null; + int editorLocation[] = null; + String folder = System.getProperty("user.dir"); + String name = null; + boolean present = false; + Color backgroundColor = Color.black; //BLACK; + Color stopColor = Color.gray; //GRAY; + GraphicsDevice displayDevice = null; + boolean hideStop = false; + + String param = null, value = null; + + int argIndex = 0; + while (argIndex < args.length) { + int equals = args[argIndex].indexOf('='); + if (equals != -1) { + param = args[argIndex].substring(0, equals); + value = args[argIndex].substring(equals + 1); + + if (param.equals(ARGS_EDITOR_LOCATION)) { + external = true; + editorLocation = toInt(split(value, ',')); + + } else if (param.equals(ARGS_DISPLAY)) { + int deviceIndex = Integer.parseInt(value) - 1; + + //DisplayMode dm = device.getDisplayMode(); + //if ((dm.getWidth() == 1024) && (dm.getHeight() == 768)) { + + GraphicsEnvironment environment = + GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice devices[] = environment.getScreenDevices(); + if ((deviceIndex >= 0) && (deviceIndex < devices.length)) { + displayDevice = devices[deviceIndex]; + } else { + System.err.println("Display " + value + " does not exist, " + + "using the default display instead."); + } + + } else if (param.equals(ARGS_BGCOLOR)) { + if (value.charAt(0) == '#') value = value.substring(1); + backgroundColor = new Color(Integer.parseInt(value, 16)); + + } else if (param.equals(ARGS_STOP_COLOR)) { + if (value.charAt(0) == '#') value = value.substring(1); + stopColor = new Color(Integer.parseInt(value, 16)); + + } else if (param.equals(ARGS_SKETCH_FOLDER)) { + folder = value; + + } else if (param.equals(ARGS_LOCATION)) { + location = toInt(split(value, ',')); + } + + } else { + if (args[argIndex].equals(ARGS_PRESENT)) { + present = true; + + } else if (args[argIndex].equals(ARGS_HIDE_STOP)) { + hideStop = true; + + } else if (args[argIndex].equals(ARGS_EXTERNAL)) { + external = true; + + } else { + name = args[argIndex]; + break; + } + } + argIndex++; + } + + if (displayDevice == null) { + GraphicsEnvironment environment = + GraphicsEnvironment.getLocalGraphicsEnvironment(); + displayDevice = environment.getDefaultScreenDevice(); + } + + Frame frame = new Frame(displayDevice.getDefaultConfiguration()); + /* + Frame frame = null; + if (displayDevice != null) { + frame = new Frame(displayDevice.getDefaultConfiguration()); + } else { + frame = new Frame(); + } + */ + + Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + + // remove the grow box by default + // users who want it back can call frame.setResizable(true) + frame.setResizable(false); + + Class c = Class.forName(name); + PApplet applet = (PApplet) c.newInstance(); + + // these are needed before init/start + applet.frame = frame; + applet.sketchPath = folder; + applet.args = PApplet.subset(args, 1); + + applet.init(); + + // wait until the applet has figured out its width + // hoping that this won't hang if the applet has an exception + while (applet.defaultSize && !applet.finished) { + try { + Thread.sleep(5); + + } catch (InterruptedException e) { } + } + + if (present) { + frame.setUndecorated(true); + frame.setBackground(backgroundColor); + displayDevice.setFullScreenWindow(frame); + + frame.add(applet); + Dimension fullscreen = frame.getSize(); + applet.setBounds((fullscreen.width - applet.width) / 2, + (fullscreen.height - applet.height) / 2, + applet.width, applet.height); + + if (!hideStop) { + Label label = new Label("stop"); + label.setForeground(stopColor); + label.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + System.exit(0); + } + }); + frame.add(label); + + Dimension labelSize = label.getPreferredSize(); + // sometimes shows up truncated on mac + //System.out.println("label width is " + labelSize.width); + labelSize = new Dimension(100, labelSize.height); + label.setSize(labelSize); + label.setLocation(20, fullscreen.height - labelSize.height - 20); + } + + // not always running externally when in present mode + if (external) { + applet.setupExternalMessages(); + } + + } else { // if not presenting + // can't do pack earlier cuz present mode don't like it + // (can't go full screen with a frame after calling pack) + frame.pack(); // get insets. get more. + Insets insets = frame.getInsets(); + + int windowW = Math.max(applet.width, MIN_WINDOW_WIDTH) + + insets.left + insets.right; + int windowH = Math.max(applet.height, MIN_WINDOW_HEIGHT) + + insets.top + insets.bottom; + + frame.setSize(windowW, windowH); + + if (location != null) { + // a specific location was received from PdeRuntime + // (applet has been run more than once, user placed window) + frame.setLocation(location[0], location[1]); + + } else if (external) { + int locationX = editorLocation[0] - 20; + int locationY = editorLocation[1]; + + if (locationX - windowW > 10) { + // if it fits to the left of the window + frame.setLocation(locationX - windowW, locationY); + + } else { // doesn't fit + // if it fits inside the editor window, + // offset slightly from upper lefthand corner + // so that it's plunked inside the text area + locationX = editorLocation[0] + 66; + locationY = editorLocation[1] + 66; + + if ((locationX + windowW > screen.width - 33) || + (locationY + windowH > screen.height - 33)) { + // otherwise center on screen + locationX = (screen.width - windowW) / 2; + locationY = (screen.height - windowH) / 2; + } + frame.setLocation(locationX, locationY); + } + } else { // just center on screen + frame.setLocation((screen.width - applet.width) / 2, + (screen.height - applet.height) / 2); + } + + frame.setLayout(null); + frame.add(applet); + + if (backgroundColor == Color.black) { //BLACK) { + // this means no bg color unless specified + backgroundColor = SystemColor.control; + } + frame.setBackground(backgroundColor); + + int usableWindowH = windowH - insets.top - insets.bottom; + applet.setBounds((windowW - applet.width)/2, + insets.top + (usableWindowH - applet.height)/2, + applet.width, applet.height); + + if (external) { + applet.setupExternalMessages(); + + } else { // !external + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + } + + // handle frame resizing events + applet.setupFrameResizeListener(); + + // all set for rockin + if (applet.displayable()) { + frame.setVisible(true); + } + } + + //System.out.println("showing frame"); + //System.out.println("applet requesting focus"); + applet.requestFocus(); // ask for keydowns + //System.out.println("exiting main()"); + + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + + + ////////////////////////////////////////////////////////////// + + + public PGraphics beginRecord(String renderer, String filename) { + filename = insertFrame(filename); + PGraphics rec = createGraphics(width, height, renderer, filename); + beginRecord(rec); + return rec; + } + + + public void beginRecord(PGraphics recorder) { + this.recorder = recorder; + recorder.beginDraw(); + } + + + public void endRecord() { + //println("endRecord()"); + //if (!recorderNull) { + if (recorder != null) { + //recorder.endRecord(); + recorder.endDraw(); + recorder.dispose(); + recorder = null; + } + } + + + public PGraphics beginRaw(String renderer, String filename) { + //filename = savePath(filename); // ensure an absolute path + filename = insertFrame(filename); + PGraphics rec = createGraphics(width, height, renderer, filename); + //g.recordRaw(rec); + g.beginRaw(rec); + return rec; + } + + + /** + * Check a string for #### signs to see if the frame number should be + * inserted. Used for functions like saveFrame() and beginRecord() to + * replace the # marks with the frame number. If only one # is used, + * it will be ignored, under the assumption that it's probably not + * intended to be the frame number. + */ + public String insertFrame(String what) { + int first = what.indexOf('#'); + int last = what.lastIndexOf('#'); + + if ((first != -1) && (last - first > 0)) { + String prefix = what.substring(0, first); + int count = last - first + 1; + String suffix = what.substring(last + 1); + return prefix + nf(frameCount, count) + suffix; + } + return what; // no change + } + + + ////////////////////////////////////////////////////////////// + + + public void loadPixels() { + System.err.println("Use beginPixels() instead of loadPixels() " + + "with release 0116 and later."); + } + + + public void beginPixels() { + g.beginPixels(); + pixels = g.pixels; + } + + + ////////////////////////////////////////////////////////////// + + // everything below this line is automatically generated. no touch. + // public functions for processing.core + + + public void imageMode(int mode) { + if (recorder != null) recorder.imageMode(mode); + g.imageMode(mode); + } + + + public void smooth() { + if (recorder != null) recorder.smooth(); + g.smooth(); + } + + + public void noSmooth() { + if (recorder != null) recorder.noSmooth(); + g.noSmooth(); + } + + + public void endPixels() { + if (recorder != null) recorder.endPixels(); + g.endPixels(); + } + + + public void endPixels(int x1, int y1, int x2, int y2) { + if (recorder != null) recorder.endPixels(x1, y1, x2, y2); + g.endPixels(x1, y1, x2, y2); + } + + + public int get(int x, int y) { + return g.get(x, y); + } + + + public PImage get(int x, int y, int w, int h) { + return g.get(x, y, w, h); + } + + + public PImage get() { + return g.get(); + } + + + public void set(int x, int y, int c) { + if (recorder != null) recorder.set(x, y, c); + g.set(x, y, c); + } + + + public void set(int dx, int dy, PImage src) { + if (recorder != null) recorder.set(dx, dy, src); + g.set(dx, dy, src); + } + + + public void mask(int alpha[]) { + if (recorder != null) recorder.mask(alpha); + g.mask(alpha); + } + + + public void mask(PImage alpha) { + if (recorder != null) recorder.mask(alpha); + g.mask(alpha); + } + + + public void filter(int kind) { + if (recorder != null) recorder.filter(kind); + g.filter(kind); + } + + + public void filter(int kind, float param) { + if (recorder != null) recorder.filter(kind, param); + g.filter(kind, param); + } + + + public void copy(int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2) { + if (recorder != null) recorder.copy(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + g.copy(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + } + + + public void copy(PImage src, + int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2) { + if (recorder != null) recorder.copy(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + g.copy(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + } + + + static public int blend(int c1, int c2, int mode) { + return PGraphics.blend(c1, c2, mode); + } + + + public void blend(int sx, int sy, int dx, int dy, int mode) { + if (recorder != null) recorder.blend(sx, sy, dx, dy, mode); + g.blend(sx, sy, dx, dy, mode); + } + + + public void blend(PImage src, + int sx, int sy, int dx, int dy, int mode) { + if (recorder != null) recorder.blend(src, sx, sy, dx, dy, mode); + g.blend(src, sx, sy, dx, dy, mode); + } + + + public void blend(int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2, int mode) { + if (recorder != null) recorder.blend(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); + g.blend(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); + } + + + public void blend(PImage src, + int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2, int mode) { + if (recorder != null) recorder.blend(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); + g.blend(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); + } + + + public void hint(int which) { + if (recorder != null) recorder.hint(which); + g.hint(which); + } + + + public void noHint(int which) { + if (recorder != null) recorder.noHint(which); + g.noHint(which); + } + + + public void beginShape() { + if (recorder != null) recorder.beginShape(); + g.beginShape(); + } + + + public void beginShape(int kind) { + if (recorder != null) recorder.beginShape(kind); + g.beginShape(kind); + } + + + public void normal(float nx, float ny, float nz) { + if (recorder != null) recorder.normal(nx, ny, nz); + g.normal(nx, ny, nz); + } + + + public void textureMode(int mode) { + if (recorder != null) recorder.textureMode(mode); + g.textureMode(mode); + } + + + public void texture(PImage image) { + if (recorder != null) recorder.texture(image); + g.texture(image); + } + + + public void vertex(float x, float y) { + if (recorder != null) recorder.vertex(x, y); + g.vertex(x, y); + } + + + public void vertex(float x, float y, float z) { + if (recorder != null) recorder.vertex(x, y, z); + g.vertex(x, y, z); + } + + + public void vertex(float x, float y, float u, float v) { + if (recorder != null) recorder.vertex(x, y, u, v); + g.vertex(x, y, u, v); + } + + + public void vertex(float x, float y, float z, float u, float v) { + if (recorder != null) recorder.vertex(x, y, z, u, v); + g.vertex(x, y, z, u, v); + } + + + public void bezierVertex(float x1, float y1, + float x2, float y2, + float x3, float y3) { + if (recorder != null) recorder.bezierVertex(x1, y1, x2, y2, x3, y3); + g.bezierVertex(x1, y1, x2, y2, x3, y3); + } + + + public void bezierVertex(float x1, float y1, float z1, + float x2, float y2, float z2, + float x3, float y3, float z3) { + if (recorder != null) recorder.bezierVertex(x1, y1, z1, x2, y2, z2, x3, y3, z3); + g.bezierVertex(x1, y1, z1, x2, y2, z2, x3, y3, z3); + } + + + public void curveVertex(float x, float y) { + if (recorder != null) recorder.curveVertex(x, y); + g.curveVertex(x, y); + } + + + public void curveVertex(float x, float y, float z) { + if (recorder != null) recorder.curveVertex(x, y, z); + g.curveVertex(x, y, z); + } + + + public void endShape() { + if (recorder != null) recorder.endShape(); + g.endShape(); + } + + + public void point(float x, float y) { + if (recorder != null) recorder.point(x, y); + g.point(x, y); + } + + + public void point(float x, float y, float z) { + if (recorder != null) recorder.point(x, y, z); + g.point(x, y, z); + } + + + public void line(float x1, float y1, float x2, float y2) { + if (recorder != null) recorder.line(x1, y1, x2, y2); + g.line(x1, y1, x2, y2); + } + + + public void line(float x1, float y1, float z1, + float x2, float y2, float z2) { + if (recorder != null) recorder.line(x1, y1, z1, x2, y2, z2); + g.line(x1, y1, z1, x2, y2, z2); + } + + + public void triangle(float x1, float y1, float x2, float y2, + float x3, float y3) { + if (recorder != null) recorder.triangle(x1, y1, x2, y2, x3, y3); + g.triangle(x1, y1, x2, y2, x3, y3); + } + + + public void quad(float x1, float y1, float x2, float y2, + float x3, float y3, float x4, float y4) { + if (recorder != null) recorder.quad(x1, y1, x2, y2, x3, y3, x4, y4); + g.quad(x1, y1, x2, y2, x3, y3, x4, y4); + } + + + public void rectMode(int mode) { + if (recorder != null) recorder.rectMode(mode); + g.rectMode(mode); + } + + + public void rect(float x1, float y1, float x2, float y2) { + if (recorder != null) recorder.rect(x1, y1, x2, y2); + g.rect(x1, y1, x2, y2); + } + + + public void ellipseMode(int mode) { + if (recorder != null) recorder.ellipseMode(mode); + g.ellipseMode(mode); + } + + + public void ellipse(float a, float b, float c, float d) { + if (recorder != null) recorder.ellipse(a, b, c, d); + g.ellipse(a, b, c, d); + } + + + public void arc(float a, float b, float c, float d, + float start, float stop) { + if (recorder != null) recorder.arc(a, b, c, d, start, stop); + g.arc(a, b, c, d, start, stop); + } + + + public void box(float size) { + if (recorder != null) recorder.box(size); + g.box(size); + } + + + public void box(float w, float h, float d) { + if (recorder != null) recorder.box(w, h, d); + g.box(w, h, d); + } + + + public void sphereDetail(int res) { + if (recorder != null) recorder.sphereDetail(res); + g.sphereDetail(res); + } + + + public void sphere(float r) { + if (recorder != null) recorder.sphere(r); + g.sphere(r); + } + + + public float bezierPoint(float a, float b, float c, float d, float t) { + return g.bezierPoint(a, b, c, d, t); + } + + + public float bezierTangent(float a, float b, float c, float d, float t) { + return g.bezierTangent(a, b, c, d, t); + } + + + public void bezierDetail(int detail) { + if (recorder != null) recorder.bezierDetail(detail); + g.bezierDetail(detail); + } + + + public void bezier(float x1, float y1, + float x2, float y2, + float x3, float y3, + float x4, float y4) { + if (recorder != null) recorder.bezier(x1, y1, x2, y2, x3, y3, x4, y4); + g.bezier(x1, y1, x2, y2, x3, y3, x4, y4); + } + + + 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) { + if (recorder != null) recorder.bezier(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4); + g.bezier(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4); + } + + + public float curvePoint(float a, float b, float c, float d, float t) { + return g.curvePoint(a, b, c, d, t); + } + + + public float curveTangent(float a, float b, float c, float d, + float t) { + return g.curveTangent(a, b, c, d, t); + } + + + public void curveDetail(int detail) { + if (recorder != null) recorder.curveDetail(detail); + g.curveDetail(detail); + } + + + public void curveTightness(float tightness) { + if (recorder != null) recorder.curveTightness(tightness); + g.curveTightness(tightness); + } + + + public void curve(float x1, float y1, + float x2, float y2, + float x3, float y3, + float x4, float y4) { + if (recorder != null) recorder.curve(x1, y1, x2, y2, x3, y3, x4, y4); + g.curve(x1, y1, x2, y2, x3, y3, x4, y4); + } + + + 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) { + if (recorder != null) recorder.curve(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4); + g.curve(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4); + } + + + public void image(PImage image, float x, float y) { + if (recorder != null) recorder.image(image, x, y); + g.image(image, x, y); + } + + + public void image(PImage image, + float x, float y, float c, float d) { + if (recorder != null) recorder.image(image, x, y, c, d); + g.image(image, x, y, c, d); + } + + + public void image(PImage image, + float a, float b, float c, float d, + int u1, int v1, int u2, int v2) { + if (recorder != null) recorder.image(image, a, b, c, d, u1, v1, u2, v2); + g.image(image, a, b, c, d, u1, v1, u2, v2); + } + + + public void textAlign(int align) { + if (recorder != null) recorder.textAlign(align); + g.textAlign(align); + } + + + public float textAscent() { + return g.textAscent(); + } + + + public float textDescent() { + return g.textDescent(); + } + + + public void textFont(PFont which) { + if (recorder != null) recorder.textFont(which); + g.textFont(which); + } + + + public void textFont(PFont which, float size) { + if (recorder != null) recorder.textFont(which, size); + g.textFont(which, size); + } + + + public void textLeading(float leading) { + if (recorder != null) recorder.textLeading(leading); + g.textLeading(leading); + } + + + public void textMode(int mode) { + if (recorder != null) recorder.textMode(mode); + g.textMode(mode); + } + + + public void textSize(float size) { + if (recorder != null) recorder.textSize(size); + g.textSize(size); + } + + + public float textWidth(char c) { + return g.textWidth(c); + } + + + public float textWidth(String str) { + return g.textWidth(str); + } + + + public void text(char c) { + if (recorder != null) recorder.text(c); + g.text(c); + } + + + public void text(char c, float x, float y) { + if (recorder != null) recorder.text(c, x, y); + g.text(c, x, y); + } + + + public void text(char c, float x, float y, float z) { + if (recorder != null) recorder.text(c, x, y, z); + g.text(c, x, y, z); + } + + + public void text(String str) { + if (recorder != null) recorder.text(str); + g.text(str); + } + + + public void text(String str, float x, float y) { + if (recorder != null) recorder.text(str, x, y); + g.text(str, x, y); + } + + + public void text(String str, float x, float y, float z) { + if (recorder != null) recorder.text(str, x, y, z); + g.text(str, x, y, z); + } + + + public void text(String str, float x1, float y1, float x2, float y2) { + if (recorder != null) recorder.text(str, x1, y1, x2, y2); + g.text(str, x1, y1, x2, y2); + } + + + public void text(String s, float x1, float y1, float x2, float y2, float z) { + if (recorder != null) recorder.text(s, x1, y1, x2, y2, z); + g.text(s, x1, y1, x2, y2, z); + } + + + public void text(int num, float x, float y) { + if (recorder != null) recorder.text(num, x, y); + g.text(num, x, y); + } + + + public void text(int num, float x, float y, float z) { + if (recorder != null) recorder.text(num, x, y, z); + g.text(num, x, y, z); + } + + + public void text(float num, float x, float y) { + if (recorder != null) recorder.text(num, x, y); + g.text(num, x, y); + } + + + public void text(float num, float x, float y, float z) { + if (recorder != null) recorder.text(num, x, y, z); + g.text(num, x, y, z); + } + + + public void translate(float tx, float ty) { + if (recorder != null) recorder.translate(tx, ty); + g.translate(tx, ty); + } + + + public void translate(float tx, float ty, float tz) { + if (recorder != null) recorder.translate(tx, ty, tz); + g.translate(tx, ty, tz); + } + + + public void rotate(float angle) { + if (recorder != null) recorder.rotate(angle); + g.rotate(angle); + } + + + public void rotateX(float angle) { + if (recorder != null) recorder.rotateX(angle); + g.rotateX(angle); + } + + + public void rotateY(float angle) { + if (recorder != null) recorder.rotateY(angle); + g.rotateY(angle); + } + + + public void rotateZ(float angle) { + if (recorder != null) recorder.rotateZ(angle); + g.rotateZ(angle); + } + + + public void rotate(float angle, float vx, float vy, float vz) { + if (recorder != null) recorder.rotate(angle, vx, vy, vz); + g.rotate(angle, vx, vy, vz); + } + + + public void scale(float s) { + if (recorder != null) recorder.scale(s); + g.scale(s); + } + + + public void scale(float sx, float sy) { + if (recorder != null) recorder.scale(sx, sy); + g.scale(sx, sy); + } + + + public void scale(float x, float y, float z) { + if (recorder != null) recorder.scale(x, y, z); + g.scale(x, y, z); + } + + + public void pushMatrix() { + if (recorder != null) recorder.pushMatrix(); + g.pushMatrix(); + } + + + public void popMatrix() { + if (recorder != null) recorder.popMatrix(); + g.popMatrix(); + } + + + public void resetMatrix() { + if (recorder != null) recorder.resetMatrix(); + g.resetMatrix(); + } + + + public void applyMatrix(float n00, float n01, float n02, + float n10, float n11, float n12) { + if (recorder != null) recorder.applyMatrix(n00, n01, n02, n10, n11, n12); + g.applyMatrix(n00, n01, n02, n10, n11, n12); + } + + + 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) { + if (recorder != null) recorder.applyMatrix(n00, n01, n02, n03, n10, n11, n12, n13, n20, n21, n22, n23, n30, n31, n32, n33); + g.applyMatrix(n00, n01, n02, n03, n10, n11, n12, n13, n20, n21, n22, n23, n30, n31, n32, n33); + } + + + public void loadMatrix() { + if (recorder != null) recorder.loadMatrix(); + g.loadMatrix(); + } + + + public void printMatrix() { + if (recorder != null) recorder.printMatrix(); + g.printMatrix(); + } + + + public void beginCamera() { + if (recorder != null) recorder.beginCamera(); + g.beginCamera(); + } + + + public void endCamera() { + if (recorder != null) recorder.endCamera(); + g.endCamera(); + } + + + public void camera() { + if (recorder != null) recorder.camera(); + g.camera(); + } + + + public void camera(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) { + if (recorder != null) recorder.camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); + g.camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); + } + + + public void printCamera() { + if (recorder != null) recorder.printCamera(); + g.printCamera(); + } + + + public void ortho() { + if (recorder != null) recorder.ortho(); + g.ortho(); + } + + + public void ortho(float left, float right, + float bottom, float top, + float near, float far) { + if (recorder != null) recorder.ortho(left, right, bottom, top, near, far); + g.ortho(left, right, bottom, top, near, far); + } + + + public void perspective() { + if (recorder != null) recorder.perspective(); + g.perspective(); + } + + + public void perspective(float fovy, float aspect, float zNear, float zFar) { + if (recorder != null) recorder.perspective(fovy, aspect, zNear, zFar); + g.perspective(fovy, aspect, zNear, zFar); + } + + + public void frustum(float left, float right, float bottom, + float top, float znear, float zfar) { + if (recorder != null) recorder.frustum(left, right, bottom, top, znear, zfar); + g.frustum(left, right, bottom, top, znear, zfar); + } + + + public void printProjection() { + if (recorder != null) recorder.printProjection(); + g.printProjection(); + } + + + public float screenX(float x, float y) { + return g.screenX(x, y); + } + + + public float screenY(float x, float y) { + return g.screenY(x, y); + } + + + public float screenX(float x, float y, float z) { + return g.screenX(x, y, z); + } + + + public float screenY(float x, float y, float z) { + return g.screenY(x, y, z); + } + + + public float screenZ(float x, float y, float z) { + return g.screenZ(x, y, z); + } + + + public float modelX(float x, float y, float z) { + return g.modelX(x, y, z); + } + + + public float modelY(float x, float y, float z) { + return g.modelY(x, y, z); + } + + + public float modelZ(float x, float y, float z) { + return g.modelZ(x, y, z); + } + + + public void colorMode(int mode) { + if (recorder != null) recorder.colorMode(mode); + g.colorMode(mode); + } + + + public void colorMode(int mode, float max) { + if (recorder != null) recorder.colorMode(mode, max); + g.colorMode(mode, max); + } + + + public void colorMode(int mode, + float maxX, float maxY, float maxZ) { + if (recorder != null) recorder.colorMode(mode, maxX, maxY, maxZ); + g.colorMode(mode, maxX, maxY, maxZ); + } + + + public void colorMode(int mode, + float maxX, float maxY, float maxZ, float maxA) { + if (recorder != null) recorder.colorMode(mode, maxX, maxY, maxZ, maxA); + g.colorMode(mode, maxX, maxY, maxZ, maxA); + } + + + public void strokeWeight(float weight) { + if (recorder != null) recorder.strokeWeight(weight); + g.strokeWeight(weight); + } + + + public void strokeJoin(int join) { + if (recorder != null) recorder.strokeJoin(join); + g.strokeJoin(join); + } + + + public void strokeCap(int cap) { + if (recorder != null) recorder.strokeCap(cap); + g.strokeCap(cap); + } + + + public void noStroke() { + if (recorder != null) recorder.noStroke(); + g.noStroke(); + } + + + public void stroke(int rgb) { + if (recorder != null) recorder.stroke(rgb); + g.stroke(rgb); + } + + + public void stroke(int rgb, float alpha) { + if (recorder != null) recorder.stroke(rgb, alpha); + g.stroke(rgb, alpha); + } + + + public void stroke(float gray) { + if (recorder != null) recorder.stroke(gray); + g.stroke(gray); + } + + + public void stroke(float gray, float alpha) { + if (recorder != null) recorder.stroke(gray, alpha); + g.stroke(gray, alpha); + } + + + public void stroke(float x, float y, float z) { + if (recorder != null) recorder.stroke(x, y, z); + g.stroke(x, y, z); + } + + + public void stroke(float x, float y, float z, float a) { + if (recorder != null) recorder.stroke(x, y, z, a); + g.stroke(x, y, z, a); + } + + + public void noTint() { + if (recorder != null) recorder.noTint(); + g.noTint(); + } + + + public void tint(int rgb) { + if (recorder != null) recorder.tint(rgb); + g.tint(rgb); + } + + + public void tint(int rgb, float alpha) { + if (recorder != null) recorder.tint(rgb, alpha); + g.tint(rgb, alpha); + } + + + public void tint(float gray) { + if (recorder != null) recorder.tint(gray); + g.tint(gray); + } + + + public void tint(float gray, float alpha) { + if (recorder != null) recorder.tint(gray, alpha); + g.tint(gray, alpha); + } + + + public void tint(float x, float y, float z) { + if (recorder != null) recorder.tint(x, y, z); + g.tint(x, y, z); + } + + + public void tint(float x, float y, float z, float a) { + if (recorder != null) recorder.tint(x, y, z, a); + g.tint(x, y, z, a); + } + + + public void noFill() { + if (recorder != null) recorder.noFill(); + g.noFill(); + } + + + public void fill(int rgb) { + if (recorder != null) recorder.fill(rgb); + g.fill(rgb); + } + + + public void fill(int rgb, float alpha) { + if (recorder != null) recorder.fill(rgb, alpha); + g.fill(rgb, alpha); + } + + + public void fill(float gray) { + if (recorder != null) recorder.fill(gray); + g.fill(gray); + } + + + public void fill(float gray, float alpha) { + if (recorder != null) recorder.fill(gray, alpha); + g.fill(gray, alpha); + } + + + public void fill(float x, float y, float z) { + if (recorder != null) recorder.fill(x, y, z); + g.fill(x, y, z); + } + + + public void fill(float x, float y, float z, float a) { + if (recorder != null) recorder.fill(x, y, z, a); + g.fill(x, y, z, a); + } + + + public void ambient(int rgb) { + if (recorder != null) recorder.ambient(rgb); + g.ambient(rgb); + } + + + public void ambient(float gray) { + if (recorder != null) recorder.ambient(gray); + g.ambient(gray); + } + + + public void ambient(float x, float y, float z) { + if (recorder != null) recorder.ambient(x, y, z); + g.ambient(x, y, z); + } + + + public void specular(int rgb) { + if (recorder != null) recorder.specular(rgb); + g.specular(rgb); + } + + + public void specular(float gray) { + if (recorder != null) recorder.specular(gray); + g.specular(gray); + } + + + public void specular(float gray, float alpha) { + if (recorder != null) recorder.specular(gray, alpha); + g.specular(gray, alpha); + } + + + public void specular(float x, float y, float z) { + if (recorder != null) recorder.specular(x, y, z); + g.specular(x, y, z); + } + + + public void specular(float x, float y, float z, float a) { + if (recorder != null) recorder.specular(x, y, z, a); + g.specular(x, y, z, a); + } + + + public void shininess(float shine) { + if (recorder != null) recorder.shininess(shine); + g.shininess(shine); + } + + + public void emissive(int rgb) { + if (recorder != null) recorder.emissive(rgb); + g.emissive(rgb); + } + + + public void emissive(float gray) { + if (recorder != null) recorder.emissive(gray); + g.emissive(gray); + } + + + public void emissive(float x, float y, float z ) { + if (recorder != null) recorder.emissive(x, y, z); + g.emissive(x, y, z); + } + + + public void lights() { + if (recorder != null) recorder.lights(); + g.lights(); + } + + + public void ambientLight(float red, float green, float blue) { + if (recorder != null) recorder.ambientLight(red, green, blue); + g.ambientLight(red, green, blue); + } + + + public void ambientLight(float red, float green, float blue, + float x, float y, float z) { + if (recorder != null) recorder.ambientLight(red, green, blue, x, y, z); + g.ambientLight(red, green, blue, x, y, z); + } + + + public void directionalLight(float red, float green, float blue, + float nx, float ny, float nz) { + if (recorder != null) recorder.directionalLight(red, green, blue, nx, ny, nz); + g.directionalLight(red, green, blue, nx, ny, nz); + } + + + public void pointLight(float red, float green, float blue, + float x, float y, float z) { + if (recorder != null) recorder.pointLight(red, green, blue, x, y, z); + g.pointLight(red, green, blue, x, y, z); + } + + + 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) { + if (recorder != null) recorder.spotLight(red, green, blue, x, y, z, nx, ny, nz, angle, concentration); + g.spotLight(red, green, blue, x, y, z, nx, ny, nz, angle, concentration); + } + + + public void lightFalloff(float constant, float linear, float quadratic) { + if (recorder != null) recorder.lightFalloff(constant, linear, quadratic); + g.lightFalloff(constant, linear, quadratic); + } + + + public void lightSpecular(float x, float y, float z) { + if (recorder != null) recorder.lightSpecular(x, y, z); + g.lightSpecular(x, y, z); + } + + + public void background(int rgb) { + if (recorder != null) recorder.background(rgb); + g.background(rgb); + } + + + public void background(float gray) { + if (recorder != null) recorder.background(gray); + g.background(gray); + } + + + public void background(float x, float y, float z) { + if (recorder != null) recorder.background(x, y, z); + g.background(x, y, z); + } + + + public void background(PImage image) { + if (recorder != null) recorder.background(image); + g.background(image); + } + + + public final float alpha(int what) { + return g.alpha(what); + } + + + public final float red(int what) { + return g.red(what); + } + + + public final float green(int what) { + return g.green(what); + } + + + public final float blue(int what) { + return g.blue(what); + } + + + public final float hue(int what) { + return g.hue(what); + } + + + public final float saturation(int what) { + return g.saturation(what); + } + + + public final float brightness(int what) { + return g.brightness(what); + } + + + public void beginRaw(PGraphics raw) { + if (recorder != null) recorder.beginRaw(raw); + g.beginRaw(raw); + } + + + public void endRaw() { + if (recorder != null) recorder.endRaw(); + g.endRaw(); + } + + + public boolean displayable() { + return g.displayable(); + } +} diff --git a/core/PConstants.java b/core/src/processing/core/PConstants.java similarity index 96% rename from core/PConstants.java rename to core/src/processing/core/PConstants.java index 7350183e4..b493a3439 100644 --- a/core/PConstants.java +++ b/core/src/processing/core/PConstants.java @@ -1,413 +1,413 @@ -/* -*- 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.Cursor; -import java.awt.event.KeyEvent; - - -/** - * Numbers shared throughout processing.core. - *

- * 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. - *

- * Changed value in 0093 to not interfere with LEFT, CENTER, and RIGHT. - */ - static final int MODEL = 4; - - /** - * textMode(SHAPE) draws text using the the glyph outlines of - * individual characters rather than as textures. If the outlines are - * not available, then textMode(SHAPE) will be ignored and textMode(MODEL) - * will be used instead. For this reason, be sure to call textMode() - * after calling textFont(). - *

- * Currently, textMode(SHAPE) is only supported by OPENGL mode. - * It also requires Java 1.2 or higher (OPENGL requires 1.4 anyway) - */ - static final int SHAPE = 5; - - - // text alignment modes - // are inherited from LEFT, CENTER, RIGHT - - - // stroke modes - - static final int SQUARE = 1 << 0; - static final int ROUND = 1 << 1; - static final int PROJECT = 1 << 2; - static final int MITER = 1 << 3; - static final int BEVEL = 1 << 5; - - - // lighting - - static final int AMBIENT = 0; - static final int DIRECTIONAL = 1; - static final int POINT = 2; - static final int SPOT = 3; - - - // key constants - - // only including the most-used of these guys - // if people need more esoteric keys, they can learn about - // the esoteric java KeyEvent api and of virtual keys - - // both key and keyCode will equal these values - static final int BACKSPACE = 8; - static final int TAB = 9; - static final int ENTER = 10; - static final int RETURN = 13; - static final int ESC = 27; - static final int DELETE = 127; - - // i.e. if ((key == CODED) && (keyCode == UP)) - static final int CODED = 0xffff; - - // key will be CODED and keyCode will be this value - static final int UP = KeyEvent.VK_UP; - static final int DOWN = KeyEvent.VK_DOWN; - static final int LEFT = KeyEvent.VK_LEFT; - static final int RIGHT = KeyEvent.VK_RIGHT; - - // key will be CODED and keyCode will be this value - static final int ALT = KeyEvent.VK_ALT; - static final int CONTROL = KeyEvent.VK_CONTROL; - static final int SHIFT = KeyEvent.VK_SHIFT; - - - // cursor types - - static final int ARROW = Cursor.DEFAULT_CURSOR; - static final int CROSS = Cursor.CROSSHAIR_CURSOR; - static final int HAND = Cursor.HAND_CURSOR; - static final int MOVE = Cursor.MOVE_CURSOR; - static final int TEXT = Cursor.TEXT_CURSOR; - static final int WAIT = Cursor.WAIT_CURSOR; - - - // hints - - //static final int SCALE_STROKE_WIDTH = 0; - //static final int LIGHTING_AFFECTS_STROKE = 1; - static final int DISABLE_TEXT_SMOOTH = 3; - //static final int DISABLE_SMOOTH_HACK = 4; - static final int DISABLE_DEPTH_TEST = 5; - static final int NO_FLYING_POO = 6; - static final int ENABLE_DEPTH_SORT = 7; - - static final int HINT_COUNT = 8; - - - ////////////////////////////////////////////////////////////// - - // FIELDS - - - // transformed values - // (to be used in rendering) - - static final int X = 0; // transformed xyzw - static final int Y = 1; // formerly SX SY SZ - static final int Z = 2; - - static final int R = 3; // actual rgb, after lighting - static final int G = 4; // fill stored here, transform in place - static final int B = 5; - static final int A = 6; - - // values that need no transformation - // but will be used in rendering - - static final int U = 7; // texture - static final int V = 8; - - // incoming values, raw and untransformed - // (won't be used in rendering) - - static final int MX = 9; // model coords xyz - static final int MY = 10; - static final int MZ = 11; - - static final int SR = 12; // stroke colors - static final int SG = 13; - static final int SB = 14; - static final int SA = 15; - - static final int SW = 16; // stroke weight - - // not used in rendering - // only used for calculating colors - - static final int NX = 17; // normal - static final int NY = 18; - static final int NZ = 19; - - static final int VX = 20; // view space coords - static final int VY = 21; - static final int VZ = 22; - static final int VW = 23; - - // Ambient color (usually to be kept the same as diffuse) - // fill(_) sets both ambient and diffuse. - static final int AR = 24; - static final int AG = 25; - static final int AB = 26; - - // Diffuse is shared with fill. - static final int DR = 3; - static final int DG = 4; - static final int DB = 5; - static final int DA = 6; - - //specular (by default kept white) - static final int SPR = 27; - static final int SPG = 28; - static final int SPB = 29; - //GL doesn't use a separate specular alpha, but we do (we're better) - static final int SPA = 30; - - static final int SHINE = 31; - - //emissive (by default kept black) - static final int ER = 32; - static final int EG = 33; - static final int EB = 34; - - //has this vertex been lit yet - static final int BEEN_LIT = 35; - - static final int VERTEX_FIELD_COUNT = 36; - - // line & triangle fields (note how these overlap) - - static final int INDEX = 0; // shape index - static final int VERTEX1 = 1; - static final int VERTEX2 = 2; - static final int VERTEX3 = 3; // (triangles only) - static final int TEXTURE_INDEX = 4; // (triangles only) - static final int STROKE_MODE = 3; // (lines only) - static final int STROKE_WEIGHT = 4; // (lines only) - - static final int LINE_FIELD_COUNT = 5; - static final int TRIANGLE_FIELD_COUNT = 5; - - static final int TRI_DIFFUSE_R = 0; - static final int TRI_DIFFUSE_G = 1; - static final int TRI_DIFFUSE_B = 2; - static final int TRI_DIFFUSE_A = 3; - static final int TRI_SPECULAR_R = 4; - static final int TRI_SPECULAR_G = 5; - static final int TRI_SPECULAR_B = 6; - static final int TRI_SPECULAR_A = 7; - - static final int TRIANGLE_COLOR_COUNT = 8; - - - // normal modes for lighting, these have the uglier naming - // because the constants are never seen by users - - /// normal calculated per triangle - static final int AUTO_NORMAL = 0; - /// one normal manually specified per shape - static final int MANUAL_SHAPE_NORMAL = 1; - /// normals specified for each shape vertex - static final int MANUAL_VERTEX_NORMAL = 2; -} +/* -*- 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.Cursor; +import java.awt.event.KeyEvent; + + +/** + * Numbers shared throughout processing.core. + *

+ * 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. + *

+ * Changed value in 0093 to not interfere with LEFT, CENTER, and RIGHT. + */ + static final int MODEL = 4; + + /** + * textMode(SHAPE) draws text using the the glyph outlines of + * individual characters rather than as textures. If the outlines are + * not available, then textMode(SHAPE) will be ignored and textMode(MODEL) + * will be used instead. For this reason, be sure to call textMode() + * after calling textFont(). + *

+ * Currently, textMode(SHAPE) is only supported by OPENGL mode. + * It also requires Java 1.2 or higher (OPENGL requires 1.4 anyway) + */ + static final int SHAPE = 5; + + + // text alignment modes + // are inherited from LEFT, CENTER, RIGHT + + + // stroke modes + + static final int SQUARE = 1 << 0; + static final int ROUND = 1 << 1; + static final int PROJECT = 1 << 2; + static final int MITER = 1 << 3; + static final int BEVEL = 1 << 5; + + + // lighting + + static final int AMBIENT = 0; + static final int DIRECTIONAL = 1; + static final int POINT = 2; + static final int SPOT = 3; + + + // key constants + + // only including the most-used of these guys + // if people need more esoteric keys, they can learn about + // the esoteric java KeyEvent api and of virtual keys + + // both key and keyCode will equal these values + static final int BACKSPACE = 8; + static final int TAB = 9; + static final int ENTER = 10; + static final int RETURN = 13; + static final int ESC = 27; + static final int DELETE = 127; + + // i.e. if ((key == CODED) && (keyCode == UP)) + static final int CODED = 0xffff; + + // key will be CODED and keyCode will be this value + static final int UP = KeyEvent.VK_UP; + static final int DOWN = KeyEvent.VK_DOWN; + static final int LEFT = KeyEvent.VK_LEFT; + static final int RIGHT = KeyEvent.VK_RIGHT; + + // key will be CODED and keyCode will be this value + static final int ALT = KeyEvent.VK_ALT; + static final int CONTROL = KeyEvent.VK_CONTROL; + static final int SHIFT = KeyEvent.VK_SHIFT; + + + // cursor types + + static final int ARROW = Cursor.DEFAULT_CURSOR; + static final int CROSS = Cursor.CROSSHAIR_CURSOR; + static final int HAND = Cursor.HAND_CURSOR; + static final int MOVE = Cursor.MOVE_CURSOR; + static final int TEXT = Cursor.TEXT_CURSOR; + static final int WAIT = Cursor.WAIT_CURSOR; + + + // hints + + //static final int SCALE_STROKE_WIDTH = 0; + //static final int LIGHTING_AFFECTS_STROKE = 1; + static final int DISABLE_TEXT_SMOOTH = 3; + //static final int DISABLE_SMOOTH_HACK = 4; + static final int DISABLE_DEPTH_TEST = 5; + static final int NO_FLYING_POO = 6; + static final int ENABLE_DEPTH_SORT = 7; + + static final int HINT_COUNT = 8; + + + ////////////////////////////////////////////////////////////// + + // FIELDS + + + // transformed values + // (to be used in rendering) + + static final int X = 0; // transformed xyzw + static final int Y = 1; // formerly SX SY SZ + static final int Z = 2; + + static final int R = 3; // actual rgb, after lighting + static final int G = 4; // fill stored here, transform in place + static final int B = 5; + static final int A = 6; + + // values that need no transformation + // but will be used in rendering + + static final int U = 7; // texture + static final int V = 8; + + // incoming values, raw and untransformed + // (won't be used in rendering) + + static final int MX = 9; // model coords xyz + static final int MY = 10; + static final int MZ = 11; + + static final int SR = 12; // stroke colors + static final int SG = 13; + static final int SB = 14; + static final int SA = 15; + + static final int SW = 16; // stroke weight + + // not used in rendering + // only used for calculating colors + + static final int NX = 17; // normal + static final int NY = 18; + static final int NZ = 19; + + static final int VX = 20; // view space coords + static final int VY = 21; + static final int VZ = 22; + static final int VW = 23; + + // Ambient color (usually to be kept the same as diffuse) + // fill(_) sets both ambient and diffuse. + static final int AR = 24; + static final int AG = 25; + static final int AB = 26; + + // Diffuse is shared with fill. + static final int DR = 3; + static final int DG = 4; + static final int DB = 5; + static final int DA = 6; + + //specular (by default kept white) + static final int SPR = 27; + static final int SPG = 28; + static final int SPB = 29; + //GL doesn't use a separate specular alpha, but we do (we're better) + static final int SPA = 30; + + static final int SHINE = 31; + + //emissive (by default kept black) + static final int ER = 32; + static final int EG = 33; + static final int EB = 34; + + //has this vertex been lit yet + static final int BEEN_LIT = 35; + + static final int VERTEX_FIELD_COUNT = 36; + + // line & triangle fields (note how these overlap) + + static final int INDEX = 0; // shape index + static final int VERTEX1 = 1; + static final int VERTEX2 = 2; + static final int VERTEX3 = 3; // (triangles only) + static final int TEXTURE_INDEX = 4; // (triangles only) + static final int STROKE_MODE = 3; // (lines only) + static final int STROKE_WEIGHT = 4; // (lines only) + + static final int LINE_FIELD_COUNT = 5; + static final int TRIANGLE_FIELD_COUNT = 5; + + static final int TRI_DIFFUSE_R = 0; + static final int TRI_DIFFUSE_G = 1; + static final int TRI_DIFFUSE_B = 2; + static final int TRI_DIFFUSE_A = 3; + static final int TRI_SPECULAR_R = 4; + static final int TRI_SPECULAR_G = 5; + static final int TRI_SPECULAR_B = 6; + static final int TRI_SPECULAR_A = 7; + + static final int TRIANGLE_COLOR_COUNT = 8; + + + // normal modes for lighting, these have the uglier naming + // because the constants are never seen by users + + /// normal calculated per triangle + static final int AUTO_NORMAL = 0; + /// one normal manually specified per shape + static final int MANUAL_SHAPE_NORMAL = 1; + /// normals specified for each shape vertex + static final int MANUAL_VERTEX_NORMAL = 2; +} diff --git a/core/PFont.java b/core/src/processing/core/PFont.java similarity index 97% rename from core/PFont.java rename to core/src/processing/core/PFont.java index 81f40ba75..15da9d8cb 100644 --- a/core/PFont.java +++ b/core/src/processing/core/PFont.java @@ -1,1002 +1,1002 @@ -/* -*- 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: - *

- *   |
- *   |                   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. - *

- * 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: + *

+ *   |
+ *   |                   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. + *

+ * 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. - *

- * When creating your own PGraphics, you should call this before - * drawing anything. - */ - public void beginDraw() { // ignore - // need to call defaults(), but can only be done when it's ok - // to draw (i.e. for opengl, no drawing can be done outside - // beginDraw/endDraw). - if (!defaultsInited) defaults(); - - resetMatrix(); // reset model matrix - - // reset vertices - vertexCount = 0; - } - - - /** - * This will finalize rendering so that it can be shown on-screen. - *

- * When creating your own PGraphics, you should call this when - * you're finished drawing. - */ - public void endDraw() { // ignore - // moving this back here (post-68) because of macosx thread problem - if (mis != null) { - mis.newPixels(pixels, cm, 0, width); - } - // mark pixels as having been updated, so that they'll work properly - // when this PGraphics is drawn using image(). - endPixels(); - } - - - /** - * Set engine's default values. This has to be called by PApplet, - * somewhere inside setup() or draw() because it talks to the - * graphics buffer, meaning that for subclasses like OpenGL, there - * needs to be a valid graphics context to mess with otherwise - * you'll get some good crashing action. - */ - public void defaults() { // ignore - //System.out.println("PGraphics.defaults() " + width + " " + height); - - colorMode(RGB, TFF); - fill(TFF); - stroke(0); - - strokeWeight(ONE); - //try { - //strokeCap(ROUND); - //strokeJoin(MITER); - //} catch (RuntimeException e) { } // P3D will complain - - background(204); - - // init shape stuff - shape = 0; - - // init matrices (must do before lights) - matrixStackDepth = 0; - - rectMode(CORNER); - ellipseMode(CENTER); - //arcMode(CENTER); - //angleMode(RADIANS); - - // no current font - textFont = null; - textSize = 12; - textLeading = 14; - textAlign = LEFT; - textMode = MODEL; - - defaultsInited = true; - } - - - protected void flush() { - // no-op, mostly for P3D to write sorted stuff - } - - - ////////////////////////////////////////////////////////////// - - // HINTS - - /** - * Enable a hint option. - *

- * 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: - *

- */ - 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. - *

- * 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: - *

-   * 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. - *

- * For instance, to convert the following example:

-   * 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. - *

- * Identical to typing: - *

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. - *

- * (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:

-   * 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. - *

- * 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). - *

- * 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. - *

- * Newlines that are \n (Unix newline or linefeed char, ascii 10) - * are honored, and \r (carriage return, Windows and Mac OS) are - * ignored. - */ - public void text(String str, float x1, float y1, float x2, float y2) { - if (textFont == null) { - throw new RuntimeException("use textFont() before text()"); - } - - if (textMode == SCREEN) beginPixels(); - - float hradius, vradius; - switch (rectMode) { - 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 (x2 < x1) { - float temp = x1; x1 = x2; x2 = temp; - } - if (y2 < y1) { - float temp = y1; y1 = y2; y2 = temp; - } - - float spaceWidth = textWidth(' '); - float runningX = x1; //boxX1; - float currentY = y1; //boxY1; - float boxWidth = x2 - x1; //boxX2 - boxX1; - - // lineX is the position where the text starts, which is adjusted - // to left/center/right based on the current textAlign - float lineX = x1; //boxX1; - if (textAlign == CENTER) { - lineX = lineX + boxWidth/2f; - } else if (textAlign == RIGHT) { - lineX = x2; //boxX2; - } - - // ala illustrator, the text itself must fit inside the box - currentY += textAscent(); //ascent() * textSize; - // if the box is already too small, tell em to f off - if (currentY > y2) return; //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 = textWidthImpl(textBuffer, wordStart, index); - - if (runningX + wordWidth > x2) { //boxX2) { - if (runningX == x1) { //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 = textWidthImpl(textBuffer, wordStart, index); - } while (wordWidth > boxWidth); - textLineImpl(textBuffer, lineStart, index, lineX, currentY); - - } else { - // next word is too big, output current line - // and advance to the next line - textLineImpl(textBuffer, lineStart, wordStop, lineX, currentY); - // 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 = x1; //boxX1; - currentY += textLeading; - //if (currentY > boxY2) return; // box is now full - if (currentY > y2) 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 - textLineImpl(textBuffer, lineStart, index, lineX, currentY); - } - lineStart = index + 1; - wordStart = lineStart; - runningX = x1; // fix for bug 188 - currentY += textLeading; - //if (currentY > boxY2) return; // box is now full - if (currentY > y2) return; // box is now full - } - index++; - } - if ((lineStart < length) && (lineStart != index)) { - textLineImpl(textBuffer, lineStart, index, lineX, currentY); - } - - if (textMode == SCREEN) endPixels(); - } - - - public void text(String s, float x1, float y1, float x2, float y2, 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(s, x1, y1, x2, y2); - textZ = z; - - if (z != 0) translate(0, 0, -z); // TEMPORARY HACK! SLOW! - } - - - public void text(int num, float x, float y) { - text(String.valueOf(num), x, y); - } - - - public void text(int num, float x, float y, float z) { - text(String.valueOf(num), x, y, z); - } - - - /** - * This does a basic number formatting, to avoid the - * generally ugly appearance of printing floats. - * Users who want more control should use their own nf() cmmand, - * or if they want the long, ugly version of float, - * use String.valueOf() to convert the float to a String first. - */ - public void text(float num, float x, float y) { - text(PApplet.nfs(num, 0, 3), x, y); - } - - - public void text(float num, float x, float y, float z) { - text(PApplet.nfs(num, 0, 3), x, y, z); - } - - - // ........................................................ - - - //font.getStringBounds(text, g2.getFontRenderContext()).getWidth(); - - protected void textCharImpl(char ch, float x, float y) { //, float z) { - int index = textFont.index(ch); - if (index == -1) return; - - PImage glyph = textFont.images[index]; - - if (textMode == MODEL) { - float high = (float) textFont.height[index] / textFont.fheight; - float bwidth = (float) textFont.width[index] / textFont.fwidth; - float lextent = (float) textFont.leftExtent[index] / textFont.fwidth; - float textent = (float) textFont.topExtent[index] / textFont.fheight; - - float x1 = x + lextent * textSize; - float y1 = y - textent * textSize; - float x2 = x1 + bwidth * textSize; - float y2 = y1 + high * textSize; - - textCharModelImpl(glyph, - x1, y1, x2, y2, - //x1, y1, z, x2, y2, z, - textFont.width[index], textFont.height[index]); - - } else if (textMode == SCREEN) { - int xx = (int) x + textFont.leftExtent[index];; - int yy = (int) y - textFont.topExtent[index]; - - int w0 = textFont.width[index]; - int h0 = textFont.height[index]; - - textCharScreenImpl(glyph, xx, yy, w0, h0); - } - } - - - protected void textCharModelImpl(PImage glyph, - float x1, float y1, //float z1, - float x2, float y2, //float z2, - int u2, int v2) { - boolean savedTint = tint; - int savedTintColor = tintColor; - float savedTintR = tintR; - float savedTintG = tintG; - float savedTintB = tintB; - float savedTintA = tintA; - boolean savedTintAlpha = tintAlpha; - - tint = true; - tintColor = fillColor; - tintR = fillR; - tintG = fillG; - tintB = fillB; - tintA = fillA; - tintAlpha = fillAlpha; - - imageImpl(glyph, x1, y1, x2, y2, 0, 0, u2, v2); - - tint = savedTint; - tintColor = savedTintColor; - tintR = savedTintR; - tintG = savedTintG; - tintB = savedTintB; - tintA = savedTintA; - tintAlpha = savedTintAlpha; - } - - - // should take image, int x1, int y1, and x2, y2 - - protected void textCharScreenImpl(PImage glyph, - int xx, int yy, //int x2, int y2, - int w0, int h0) { - /* - System.out.println("textimplscreen"); - rectMode(CORNER); - stroke(255); - rect(xx, yy, w0, h0); - */ - - int x0 = 0; - int y0 = 0; - - if ((xx >= width) || (yy >= height) || - (xx + w0 < 0) || (yy + h0 < 0)) return; - - if (xx < 0) { - x0 -= xx; - w0 += xx; - xx = 0; - } - if (yy < 0) { - y0 -= yy; - h0 += yy; - yy = 0; - } - if (xx + w0 > width) { - w0 -= ((xx + w0) - width); - } - if (yy + h0 > height) { - h0 -= ((yy + h0) - height); - } - - int fr = fillRi; - int fg = fillGi; - int fb = fillBi; - int fa = fillAi; - - int pixels1[] = glyph.pixels; //images[glyph].pixels; - - // TODO this can be optimized a bit - for (int row = y0; row < y0 + h0; row++) { - for (int col = x0; col < x0 + w0; col++) { - int a1 = (fa * pixels1[row * textFont.twidth + col]) >> 8; - int a2 = a1 ^ 0xff; - //int p1 = pixels1[row * glyph.width + col]; - int p2 = pixels[(yy + row-y0)*width + (xx+col-x0)]; - - pixels[(yy + row-y0)*width + xx+col-x0] = - (0xff000000 | - (((a1 * fr + a2 * ((p2 >> 16) & 0xff)) & 0xff00) << 8) | - (( a1 * fg + a2 * ((p2 >> 8) & 0xff)) & 0xff00) | - (( a1 * fb + a2 * ( p2 & 0xff)) >> 8)); - } - } - } - - - - ////////////////////////////////////////////////////////////// - - // MATRIX TRANSFORMATIONS - - - public void translate(float tx, float ty) { - m02 += tx*m00 + ty*m01 + m02; - m12 += tx*m10 + ty*m11 + m12; - } - - - public void translate(float tx, float ty, float tz) { - depthErrorXYZ("translate"); - } - - - /** - * 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) { - float c = (float) Math.cos(angle); - float s = (float) Math.sin(angle); - - applyMatrix(c, -s, 0, s, c, 0); - } - - - public void rotateX(float angle) { - depthError("rotateX"); - } - - public void rotateY(float angle) { - depthError("rotateY"); - } - - - /** - * Rotate around the z axis. The functions rotate() and rotateZ() are - * the same, it's just that it make sense to have rotate() and then rotateX() - * and rotateY() when running in 3D; nor does it make sense to use - * a function called rotateZ() if you're only doing things in 2D. - * so we just decided to have them both be the same. - */ - public void rotateZ(float angle) { - depthError("rotateZ"); - } - - - /** - * Rotate about a vector in space. Same as the glRotatef() function. - */ - public void rotate(float angle, float vx, float vy, float vz) { - throw new RuntimeException("rotate(angle, x, y, z) " + - "can only be used with P3D or OPENGL"); - } - - - public void scale(float s) { - applyMatrix(s, 0, 0, - 0, s, 0); - } - - - public void scale(float sx, float sy) { - applyMatrix(sx, 0, 0, - 0, sy, 0); - } - - - public void scale(float x, float y, float z) { - depthErrorXYZ("scale"); - } - - - - ////////////////////////////////////////////////////////////// - - // TRANSFORMATION MATRIX - - - public void pushMatrix() { - if (matrixStackDepth+1 == MATRIX_STACK_DEPTH) { - throw new RuntimeException("too many calls to pushMatrix()"); - } - float mat[] = matrixStack[matrixStackDepth]; - mat[0] = m00; mat[1] = m01; mat[2] = m02; - mat[3] = m10; mat[4] = m11; mat[5] = m12; - matrixStackDepth++; - } - - - public void popMatrix() { - if (matrixStackDepth == 0) { - throw new RuntimeException("too many calls to popMatrix() " + - "(and not enough to pushMatrix)"); - } - matrixStackDepth--; - float mat[] = matrixStack[matrixStackDepth]; - m00 = mat[0]; m01 = mat[1]; m02 = mat[2]; - m10 = mat[3]; m11 = mat[4]; m12 = mat[5]; - } - - - /** - * Load identity as the transform/model matrix. - * Same as glLoadIdentity(). - */ - public void resetMatrix() { - m00 = 1; m01 = 0; m02 = 0; - m10 = 0; m11 = 1; m12 = 0; - } - - - /** - * Apply a 3x2 affine transformation matrix. - */ - public void applyMatrix(float n00, float n01, float n02, - float n10, float n11, float n12) { - - float r00 = m00*n00 + m01*n10; - float r01 = m00*n01 + m01*n11; - float r02 = m00*n02 + m01*n12 + m02; - - float r10 = m10*n00 + m11*n10; - float r11 = m10*n01 + m11*n11; - float r12 = m10*n02 + m11*n12 + m12; - - m00 = r00; m01 = r01; m02 = r02; - m10 = r10; m11 = r11; m12 = r12; - } - - - 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) { - throw new RuntimeException("applyMatrix() with a 4x4 matrix " + - "can only be used with OPENGL or P3D"); - } - - - /** - * Loads the current matrix into m00, m01 etc (or modelview and - * projection when using 3D) so that the values can be read. - *

- * Note that there is no "updateMatrix" because that gets too - * complicated (unnecessary) when considering the 3D matrices. - */ - public void loadMatrix() { - // no-op on base PGraphics because they're used directly - } - - - /** - * Print the current model (or "transformation") matrix. - */ - public void printMatrix() { - loadMatrix(); // just to make sure - - float big = Math.abs(m00); - if (Math.abs(m01) > big) big = Math.abs(m01); - if (Math.abs(m02) > big) big = Math.abs(m02); - if (Math.abs(m10) > big) big = Math.abs(m10); - if (Math.abs(m11) > big) big = Math.abs(m11); - if (Math.abs(m12) > big) big = Math.abs(m12); - - // avoid infinite loop - if (Float.isNaN(big) || Float.isInfinite(big)) { - big = 1000000; // set to something arbitrary - } - - int d = 1; - int bigi = (int) big; - while ((bigi /= 10) != 0) d++; // cheap log() - - System.out.println(PApplet.nfs(m00, d, 4) + " " + - PApplet.nfs(m01, d, 4) + " " + - PApplet.nfs(m02, d, 4)); - - System.out.println(PApplet.nfs(m10, d, 4) + " " + - PApplet.nfs(m11, d, 4) + " " + - PApplet.nfs(m12, d, 4)); - - System.out.println(); - } - - - - ////////////////////////////////////////////////////////////// - - // CAMERA (none are supported in 2D) - - - public void beginCamera() { - depthError("beginCamera"); - } - - public void endCamera() { - depthError("endCamera"); - } - - public void camera() { - depthError("camera"); - } - - public void camera(float eyeX, float eyeY, float eyeZ, - float centerX, float centerY, float centerZ, - float upX, float upY, float upZ) { - depthError("camera"); - } - - public void printCamera() { - depthError("printCamera"); - } - - - - ////////////////////////////////////////////////////////////// - - // PROJECTION (none are supported in 2D) - - - public void ortho() { - depthError("ortho"); - } - - public void ortho(float left, float right, - float bottom, float top, - float near, float far) { - depthError("ortho"); - } - - public void perspective() { - depthError("perspective"); - } - - public void perspective(float fovy, float aspect, float zNear, float zFar) { - depthError("perspective"); - } - - public void frustum(float left, float right, float bottom, - float top, float znear, float zfar) { - depthError("frustum"); - } - - public void printProjection() { - depthError("printCamera"); - } - - - - ////////////////////////////////////////////////////////////// - - // SCREEN TRANSFORMS - - - /** - * Given an x and y 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) { - return m00*x + m01*y + m02; - } - - - /** - * Given an x and y 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) { - return m10*x + m11*y + m12; - } - - - /** - * Maps a three dimensional point to its placement on-screen. - *

- * 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 - *

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. - *

- * 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: - *

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. - *

- * 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. + *

+ * When creating your own PGraphics, you should call this before + * drawing anything. + */ + public void beginDraw() { // ignore + // need to call defaults(), but can only be done when it's ok + // to draw (i.e. for opengl, no drawing can be done outside + // beginDraw/endDraw). + if (!defaultsInited) defaults(); + + resetMatrix(); // reset model matrix + + // reset vertices + vertexCount = 0; + } + + + /** + * This will finalize rendering so that it can be shown on-screen. + *

+ * When creating your own PGraphics, you should call this when + * you're finished drawing. + */ + public void endDraw() { // ignore + // moving this back here (post-68) because of macosx thread problem + if (mis != null) { + mis.newPixels(pixels, cm, 0, width); + } + // mark pixels as having been updated, so that they'll work properly + // when this PGraphics is drawn using image(). + endPixels(); + } + + + /** + * Set engine's default values. This has to be called by PApplet, + * somewhere inside setup() or draw() because it talks to the + * graphics buffer, meaning that for subclasses like OpenGL, there + * needs to be a valid graphics context to mess with otherwise + * you'll get some good crashing action. + */ + public void defaults() { // ignore + //System.out.println("PGraphics.defaults() " + width + " " + height); + + colorMode(RGB, TFF); + fill(TFF); + stroke(0); + + strokeWeight(ONE); + //try { + //strokeCap(ROUND); + //strokeJoin(MITER); + //} catch (RuntimeException e) { } // P3D will complain + + background(204); + + // init shape stuff + shape = 0; + + // init matrices (must do before lights) + matrixStackDepth = 0; + + rectMode(CORNER); + ellipseMode(CENTER); + //arcMode(CENTER); + //angleMode(RADIANS); + + // no current font + textFont = null; + textSize = 12; + textLeading = 14; + textAlign = LEFT; + textMode = MODEL; + + defaultsInited = true; + } + + + protected void flush() { + // no-op, mostly for P3D to write sorted stuff + } + + + ////////////////////////////////////////////////////////////// + + // HINTS + + /** + * Enable a hint option. + *

+ * 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: + *

+ */ + 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. + *

+ * 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: + *

+   * 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. + *

+ * For instance, to convert the following example:

+   * 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. + *

+ * Identical to typing: + *

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. + *

+ * (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:

+   * 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. + *

+ * 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). + *

+ * 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. + *

+ * Newlines that are \n (Unix newline or linefeed char, ascii 10) + * are honored, and \r (carriage return, Windows and Mac OS) are + * ignored. + */ + public void text(String str, float x1, float y1, float x2, float y2) { + if (textFont == null) { + throw new RuntimeException("use textFont() before text()"); + } + + if (textMode == SCREEN) beginPixels(); + + float hradius, vradius; + switch (rectMode) { + 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 (x2 < x1) { + float temp = x1; x1 = x2; x2 = temp; + } + if (y2 < y1) { + float temp = y1; y1 = y2; y2 = temp; + } + + float spaceWidth = textWidth(' '); + float runningX = x1; //boxX1; + float currentY = y1; //boxY1; + float boxWidth = x2 - x1; //boxX2 - boxX1; + + // lineX is the position where the text starts, which is adjusted + // to left/center/right based on the current textAlign + float lineX = x1; //boxX1; + if (textAlign == CENTER) { + lineX = lineX + boxWidth/2f; + } else if (textAlign == RIGHT) { + lineX = x2; //boxX2; + } + + // ala illustrator, the text itself must fit inside the box + currentY += textAscent(); //ascent() * textSize; + // if the box is already too small, tell em to f off + if (currentY > y2) return; //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 = textWidthImpl(textBuffer, wordStart, index); + + if (runningX + wordWidth > x2) { //boxX2) { + if (runningX == x1) { //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 = textWidthImpl(textBuffer, wordStart, index); + } while (wordWidth > boxWidth); + textLineImpl(textBuffer, lineStart, index, lineX, currentY); + + } else { + // next word is too big, output current line + // and advance to the next line + textLineImpl(textBuffer, lineStart, wordStop, lineX, currentY); + // 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 = x1; //boxX1; + currentY += textLeading; + //if (currentY > boxY2) return; // box is now full + if (currentY > y2) 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 + textLineImpl(textBuffer, lineStart, index, lineX, currentY); + } + lineStart = index + 1; + wordStart = lineStart; + runningX = x1; // fix for bug 188 + currentY += textLeading; + //if (currentY > boxY2) return; // box is now full + if (currentY > y2) return; // box is now full + } + index++; + } + if ((lineStart < length) && (lineStart != index)) { + textLineImpl(textBuffer, lineStart, index, lineX, currentY); + } + + if (textMode == SCREEN) endPixels(); + } + + + public void text(String s, float x1, float y1, float x2, float y2, 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(s, x1, y1, x2, y2); + textZ = z; + + if (z != 0) translate(0, 0, -z); // TEMPORARY HACK! SLOW! + } + + + public void text(int num, float x, float y) { + text(String.valueOf(num), x, y); + } + + + public void text(int num, float x, float y, float z) { + text(String.valueOf(num), x, y, z); + } + + + /** + * This does a basic number formatting, to avoid the + * generally ugly appearance of printing floats. + * Users who want more control should use their own nf() cmmand, + * or if they want the long, ugly version of float, + * use String.valueOf() to convert the float to a String first. + */ + public void text(float num, float x, float y) { + text(PApplet.nfs(num, 0, 3), x, y); + } + + + public void text(float num, float x, float y, float z) { + text(PApplet.nfs(num, 0, 3), x, y, z); + } + + + // ........................................................ + + + //font.getStringBounds(text, g2.getFontRenderContext()).getWidth(); + + protected void textCharImpl(char ch, float x, float y) { //, float z) { + int index = textFont.index(ch); + if (index == -1) return; + + PImage glyph = textFont.images[index]; + + if (textMode == MODEL) { + float high = (float) textFont.height[index] / textFont.fheight; + float bwidth = (float) textFont.width[index] / textFont.fwidth; + float lextent = (float) textFont.leftExtent[index] / textFont.fwidth; + float textent = (float) textFont.topExtent[index] / textFont.fheight; + + float x1 = x + lextent * textSize; + float y1 = y - textent * textSize; + float x2 = x1 + bwidth * textSize; + float y2 = y1 + high * textSize; + + textCharModelImpl(glyph, + x1, y1, x2, y2, + //x1, y1, z, x2, y2, z, + textFont.width[index], textFont.height[index]); + + } else if (textMode == SCREEN) { + int xx = (int) x + textFont.leftExtent[index];; + int yy = (int) y - textFont.topExtent[index]; + + int w0 = textFont.width[index]; + int h0 = textFont.height[index]; + + textCharScreenImpl(glyph, xx, yy, w0, h0); + } + } + + + protected void textCharModelImpl(PImage glyph, + float x1, float y1, //float z1, + float x2, float y2, //float z2, + int u2, int v2) { + boolean savedTint = tint; + int savedTintColor = tintColor; + float savedTintR = tintR; + float savedTintG = tintG; + float savedTintB = tintB; + float savedTintA = tintA; + boolean savedTintAlpha = tintAlpha; + + tint = true; + tintColor = fillColor; + tintR = fillR; + tintG = fillG; + tintB = fillB; + tintA = fillA; + tintAlpha = fillAlpha; + + imageImpl(glyph, x1, y1, x2, y2, 0, 0, u2, v2); + + tint = savedTint; + tintColor = savedTintColor; + tintR = savedTintR; + tintG = savedTintG; + tintB = savedTintB; + tintA = savedTintA; + tintAlpha = savedTintAlpha; + } + + + // should take image, int x1, int y1, and x2, y2 + + protected void textCharScreenImpl(PImage glyph, + int xx, int yy, //int x2, int y2, + int w0, int h0) { + /* + System.out.println("textimplscreen"); + rectMode(CORNER); + stroke(255); + rect(xx, yy, w0, h0); + */ + + int x0 = 0; + int y0 = 0; + + if ((xx >= width) || (yy >= height) || + (xx + w0 < 0) || (yy + h0 < 0)) return; + + if (xx < 0) { + x0 -= xx; + w0 += xx; + xx = 0; + } + if (yy < 0) { + y0 -= yy; + h0 += yy; + yy = 0; + } + if (xx + w0 > width) { + w0 -= ((xx + w0) - width); + } + if (yy + h0 > height) { + h0 -= ((yy + h0) - height); + } + + int fr = fillRi; + int fg = fillGi; + int fb = fillBi; + int fa = fillAi; + + int pixels1[] = glyph.pixels; //images[glyph].pixels; + + // TODO this can be optimized a bit + for (int row = y0; row < y0 + h0; row++) { + for (int col = x0; col < x0 + w0; col++) { + int a1 = (fa * pixels1[row * textFont.twidth + col]) >> 8; + int a2 = a1 ^ 0xff; + //int p1 = pixels1[row * glyph.width + col]; + int p2 = pixels[(yy + row-y0)*width + (xx+col-x0)]; + + pixels[(yy + row-y0)*width + xx+col-x0] = + (0xff000000 | + (((a1 * fr + a2 * ((p2 >> 16) & 0xff)) & 0xff00) << 8) | + (( a1 * fg + a2 * ((p2 >> 8) & 0xff)) & 0xff00) | + (( a1 * fb + a2 * ( p2 & 0xff)) >> 8)); + } + } + } + + + + ////////////////////////////////////////////////////////////// + + // MATRIX TRANSFORMATIONS + + + public void translate(float tx, float ty) { + m02 += tx*m00 + ty*m01 + m02; + m12 += tx*m10 + ty*m11 + m12; + } + + + public void translate(float tx, float ty, float tz) { + depthErrorXYZ("translate"); + } + + + /** + * 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) { + float c = (float) Math.cos(angle); + float s = (float) Math.sin(angle); + + applyMatrix(c, -s, 0, s, c, 0); + } + + + public void rotateX(float angle) { + depthError("rotateX"); + } + + public void rotateY(float angle) { + depthError("rotateY"); + } + + + /** + * Rotate around the z axis. The functions rotate() and rotateZ() are + * the same, it's just that it make sense to have rotate() and then rotateX() + * and rotateY() when running in 3D; nor does it make sense to use + * a function called rotateZ() if you're only doing things in 2D. + * so we just decided to have them both be the same. + */ + public void rotateZ(float angle) { + depthError("rotateZ"); + } + + + /** + * Rotate about a vector in space. Same as the glRotatef() function. + */ + public void rotate(float angle, float vx, float vy, float vz) { + throw new RuntimeException("rotate(angle, x, y, z) " + + "can only be used with P3D or OPENGL"); + } + + + public void scale(float s) { + applyMatrix(s, 0, 0, + 0, s, 0); + } + + + public void scale(float sx, float sy) { + applyMatrix(sx, 0, 0, + 0, sy, 0); + } + + + public void scale(float x, float y, float z) { + depthErrorXYZ("scale"); + } + + + + ////////////////////////////////////////////////////////////// + + // TRANSFORMATION MATRIX + + + public void pushMatrix() { + if (matrixStackDepth+1 == MATRIX_STACK_DEPTH) { + throw new RuntimeException("too many calls to pushMatrix()"); + } + float mat[] = matrixStack[matrixStackDepth]; + mat[0] = m00; mat[1] = m01; mat[2] = m02; + mat[3] = m10; mat[4] = m11; mat[5] = m12; + matrixStackDepth++; + } + + + public void popMatrix() { + if (matrixStackDepth == 0) { + throw new RuntimeException("too many calls to popMatrix() " + + "(and not enough to pushMatrix)"); + } + matrixStackDepth--; + float mat[] = matrixStack[matrixStackDepth]; + m00 = mat[0]; m01 = mat[1]; m02 = mat[2]; + m10 = mat[3]; m11 = mat[4]; m12 = mat[5]; + } + + + /** + * Load identity as the transform/model matrix. + * Same as glLoadIdentity(). + */ + public void resetMatrix() { + m00 = 1; m01 = 0; m02 = 0; + m10 = 0; m11 = 1; m12 = 0; + } + + + /** + * Apply a 3x2 affine transformation matrix. + */ + public void applyMatrix(float n00, float n01, float n02, + float n10, float n11, float n12) { + + float r00 = m00*n00 + m01*n10; + float r01 = m00*n01 + m01*n11; + float r02 = m00*n02 + m01*n12 + m02; + + float r10 = m10*n00 + m11*n10; + float r11 = m10*n01 + m11*n11; + float r12 = m10*n02 + m11*n12 + m12; + + m00 = r00; m01 = r01; m02 = r02; + m10 = r10; m11 = r11; m12 = r12; + } + + + 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) { + throw new RuntimeException("applyMatrix() with a 4x4 matrix " + + "can only be used with OPENGL or P3D"); + } + + + /** + * Loads the current matrix into m00, m01 etc (or modelview and + * projection when using 3D) so that the values can be read. + *

+ * Note that there is no "updateMatrix" because that gets too + * complicated (unnecessary) when considering the 3D matrices. + */ + public void loadMatrix() { + // no-op on base PGraphics because they're used directly + } + + + /** + * Print the current model (or "transformation") matrix. + */ + public void printMatrix() { + loadMatrix(); // just to make sure + + float big = Math.abs(m00); + if (Math.abs(m01) > big) big = Math.abs(m01); + if (Math.abs(m02) > big) big = Math.abs(m02); + if (Math.abs(m10) > big) big = Math.abs(m10); + if (Math.abs(m11) > big) big = Math.abs(m11); + if (Math.abs(m12) > big) big = Math.abs(m12); + + // avoid infinite loop + if (Float.isNaN(big) || Float.isInfinite(big)) { + big = 1000000; // set to something arbitrary + } + + int d = 1; + int bigi = (int) big; + while ((bigi /= 10) != 0) d++; // cheap log() + + System.out.println(PApplet.nfs(m00, d, 4) + " " + + PApplet.nfs(m01, d, 4) + " " + + PApplet.nfs(m02, d, 4)); + + System.out.println(PApplet.nfs(m10, d, 4) + " " + + PApplet.nfs(m11, d, 4) + " " + + PApplet.nfs(m12, d, 4)); + + System.out.println(); + } + + + + ////////////////////////////////////////////////////////////// + + // CAMERA (none are supported in 2D) + + + public void beginCamera() { + depthError("beginCamera"); + } + + public void endCamera() { + depthError("endCamera"); + } + + public void camera() { + depthError("camera"); + } + + public void camera(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) { + depthError("camera"); + } + + public void printCamera() { + depthError("printCamera"); + } + + + + ////////////////////////////////////////////////////////////// + + // PROJECTION (none are supported in 2D) + + + public void ortho() { + depthError("ortho"); + } + + public void ortho(float left, float right, + float bottom, float top, + float near, float far) { + depthError("ortho"); + } + + public void perspective() { + depthError("perspective"); + } + + public void perspective(float fovy, float aspect, float zNear, float zFar) { + depthError("perspective"); + } + + public void frustum(float left, float right, float bottom, + float top, float znear, float zfar) { + depthError("frustum"); + } + + public void printProjection() { + depthError("printCamera"); + } + + + + ////////////////////////////////////////////////////////////// + + // SCREEN TRANSFORMS + + + /** + * Given an x and y 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) { + return m00*x + m01*y + m02; + } + + + /** + * Given an x and y 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) { + return m10*x + m11*y + m12; + } + + + /** + * Maps a three dimensional point to its placement on-screen. + *

+ * 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 + *

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. + *

+ * 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: + *

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. + *

+ * 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. - *

- * Lighting and camera implementation by Simon Greenwold. - */ -public class PGraphics3 extends PGraphics { - - // ........................................................ - - // Lighting-related variables - - // Whether or not we have to worry about vertex position for lighting calcs - private boolean lightingDependsOnVertexPosition; - - static final int LIGHT_AMBIENT_R = 0; - static final int LIGHT_AMBIENT_G = 1; - static final int LIGHT_AMBIENT_B = 2; - static final int LIGHT_DIFFUSE_R = 3; - static final int LIGHT_DIFFUSE_G = 4; - static final int LIGHT_DIFFUSE_B = 5; - static final int LIGHT_SPECULAR_R = 6; - static final int LIGHT_SPECULAR_G = 7; - static final int LIGHT_SPECULAR_B = 8; - - static final int LIGHT_COLOR_COUNT = 9; - - // Used to shuttle lighting calcs around - // (no need to re-allocate all the time) - protected float[] tempLightingContribution = new float[LIGHT_COLOR_COUNT]; - protected float[] worldNormal = new float[4]; - - // Used in light_triangle(). Allocated here once to - // avoid re-allocating each time - protected float[] dv1 = new float[3]; - protected float[] dv2 = new float[3]; - protected float[] norm = new float[3]; - - // ........................................................ - - /** - * This is turned on at beginCamera, and off at endCamera - * Currently we don't support nested begin/end cameras. - * If we wanted to, this variable would have to become a stack. - */ - protected boolean manipulatingCamera; - - // These two matrices always point to either the modelview - // or the modelviewInv, but they are swapped during - // when in camera maniuplation mode. That way camera transforms - // are automatically accumulated in inverse on the modelview matrix. - protected PMatrix forwardTransform; - protected PMatrix reverseTransform; - - // ........................................................ - - // pos of first vertex of current shape in vertices array - protected int vertex_start; - - // i think vertex_end is actually the last vertex in the current shape - // and is separate from vertexCount for occasions where drawing happens - // on endDraw() with all the triangles being depth sorted - protected int vertex_end; - - // vertices may be added during clipping against the near plane. - protected int vertex_end_including_clip_verts; - - // used for sorting points when triangulating a polygon - // warning - maximum number of vertices for a polygon is DEFAULT_VERTICES - protected int vertex_order[] = new int[DEFAULT_VERTICES]; - - // ........................................................ - - protected int pathCount; - protected int pathOffset[] = new int[64]; - protected int pathLength[] = new int[64]; - - // ........................................................ - - // lines - static final int DEFAULT_LINES = 512; - public PLine line; // used for drawing - protected int lines[][] = new int[DEFAULT_LINES][LINE_FIELD_COUNT]; - protected int lineCount; - - // ........................................................ - - // triangles - static final int DEFAULT_TRIANGLES = 256; - public PTriangle triangle; - protected int triangles[][] = - new int[DEFAULT_TRIANGLES][TRIANGLE_FIELD_COUNT]; - protected float triangleColors[][][] = - new float[DEFAULT_TRIANGLES][3][TRIANGLE_COLOR_COUNT]; - protected int triangleCount; // total number of triangles - - // cheap picking someday - public int shape_index; - - // ........................................................ - - /** - * 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; - - //public PImage textureImage; - - static final int DEFAULT_TEXTURES = 3; - protected PImage textures[] = new PImage[DEFAULT_TEXTURES]; - int texture_index; - - // ........................................................ - - /** - * 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() - float sphereX[], sphereY[], sphereZ[]; - //public int sphereDetail = 0; - - // ........................................................ - - - /** - * Constructor for the PGraphics3 object. - * This prototype only exists because of annoying - * java compilers, and should not be used. - */ - public PGraphics3() { - forwardTransform = modelview; - reverseTransform = modelviewInv; - } - - - public PGraphics3(int iwidth, int iheight) { - this(iwidth, iheight, null); - } - - - /** - * Constructor for the PGraphics3 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 PGraphics3(int iwidth, int iheight, PApplet parent) { - // super will add the listeners to the applet, and call resize() - super(iwidth, iheight, parent); - forwardTransform = modelview; - reverseTransform = modelviewInv; - //resize(iwidth, iheight); - //projection = new PMatrix(); - } - - - /** - * 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("PGraphics3 resize"); - - width = iwidth; - height = iheight; - width1 = width - 1; - height1 = height - 1; - - allocate(); - - // clear the screen with the old background color - //background(backgroundColor); - - // init perspective projection based on new dimensions - cameraFOV = 60 * DEG_TO_RAD; // at least for now - cameraX = width / 2.0f; - cameraY = height / 2.0f; - //cameraZ = cameraY / ((float) tan(PI * cameraFOV / 360f)); - cameraZ = cameraY / ((float) tan(cameraFOV / 2.0f)); - cameraNear = cameraZ / 10.0f; - cameraFar = cameraZ * 10.0f; - - cameraAspect = (float)width / (float)height; - - // init lights (in resize() instead of allocate() b/c needed by opengl) - lightType = new int[MAX_LIGHTS]; - lightPosition = new float[MAX_LIGHTS][3]; - lightDiffuse = new float[MAX_LIGHTS][3]; - lightNormal = new float[MAX_LIGHTS][3]; - lightSpecular = new float[MAX_LIGHTS][3]; - lightFalloffConstant = new float[MAX_LIGHTS]; - lightFalloffLinear = new float[MAX_LIGHTS]; - lightFalloffQuadratic = new float[MAX_LIGHTS]; - lightSpotAngle = new float[MAX_LIGHTS]; - lightSpotAngleCos = new float[MAX_LIGHTS]; - lightSpotConcentration = new float[MAX_LIGHTS]; - currentLightSpecular = new float[3]; - - // reset the cameraMode if PERSPECTIVE or ORTHOGRAPHIC - // will just be ignored if CUSTOM, the user's hosed anyways - //System.out.println("setting cameraMode to " + cameraMode); - //if (this.cameraMode != CUSTOM) cameraMode(this.cameraMode); - - // making this again here because things are weird - projection = new PMatrix(); - - modelview = new PMatrix(MATRIX_STACK_DEPTH); - modelviewInv = new PMatrix(MATRIX_STACK_DEPTH); - forwardTransform = modelview; - reverseTransform = modelviewInv; - - camera = new PMatrix(); - cameraInv = new PMatrix(); - - // set up the default camera - camera(); - - // defaults to perspective, if the user has setup up their - // own projection, they'll need to fix it after resize anyway. - // this helps the people who haven't set up their own projection. - perspective(); - } - - - protected void allocate() { - //System.out.println("allocating for " + width + " " + height); - - 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; - - 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); - - zbuffer = new float[pixelCount]; - stencil = new int[pixelCount]; - - line = new PLine(this); - triangle = new PTriangle(this); - } - - - public void beginDraw() { - super.beginDraw(); - - modelview.set(camera); - modelviewInv.set(cameraInv); - - // clear out the lights, they'll have to be turned on again - lightCount = 0; - lightingDependsOnVertexPosition = false; - lightFalloff(1, 0, 0); - lightSpecular(0, 0, 0); - - // reset lines - lineCount = 0; - if (line != null) line.reset(); // is this necessary? - pathCount = 0; - - // reset triangles - triangleCount = 0; - if (triangle != null) triangle.reset(); // necessary? - - vertex_start = 0; - //vertex_end = 0; - - // reset textures - texture_index = 0; - - normal(0, 0, 1); - } - - - /** - * See notes in PGraphics. - * If z-sorting has been turned on, then the triangles will - * all be quicksorted here (to make alpha work more properly) - * and then blit to the screen. - */ - public void endDraw() { - // no need to z order and render - // shapes were already rendered in endShape(); - // (but can't return, since needs to update memimgsrc - if (hints[ENABLE_DEPTH_SORT]) { - flush(); - } - // blit to screen - super.endDraw(); - } - - - /** - * Emit any sorted geometry that's been collected on this frame. - */ - protected void flush() { - if (triangleCount > 0) { - if (hints[ENABLE_DEPTH_SORT]) { - depth_sort_triangles(); - } - render_triangles(); - } - if (lineCount > 0) { - if (hints[ENABLE_DEPTH_SORT]) { - depth_sort_lines(); - } - render_lines(); - } - } - - - public void defaults() { - super.defaults(); - - manipulatingCamera = false; - forwardTransform = modelview; - reverseTransform = modelviewInv; - - perspective(); - - // easiest for beginners - textureMode(IMAGE); - - emissive(0.0f); - specular(0.5f); - shininess(1.0f); - } - - - ////////////////////////////////////////////////////////////// - - - public void beginShape(int kind) { - shape = kind; - - shape_index = shape_index + 1; - if (shape_index == -1) { - shape_index = 0; - } - - if (hints[ENABLE_DEPTH_SORT]) { - // continue with previous vertex, line and triangle count - // all shapes are rendered at endDraw(); - vertex_start = vertexCount; - vertex_end = 0; - - } else { - // reset vertex, line and triangle information - // every shape is rendered at endShape(); - vertexCount = 0; - if (line != null) line.reset(); // necessary? - lineCount = 0; - pathCount = 0; - if (triangle != null) triangle.reset(); // necessary? - triangleCount = 0; - } - textureImage = null; - - splineVertexCount = 0; - normalMode = AUTO_NORMAL; - normalCount = 0; - } - - - /** - * Sets the current normal vector. - *

- * This is for drawing three dimensional shapes and surfaces, - * allowing you to specify a vector perpendicular to the surface - * of the shape, which determines how lighting affects it. - *

- * For the most part, PGraphics will attempt to automatically - * assign normals to shapes, but since that's imperfect, - * this is a better option when you want more control. - *

- * For people familiar with OpenGL, this function is basically - * identical to glNormal3f(). - *

- * Only applies inside a beginShape/endShape block. - */ - public void normal(float nx, float ny, float nz) { - normalX = nx; - normalY = ny; - normalZ = nz; - - // if drawing a shape and the normal hasn't been set yet, - // then we need to set the normals for each vertex so far - if (shape != 0) { - if (normalCount == 0) { - for (int i = vertex_start; i < vertexCount; i++) { - vertices[i][NX] = normalX; - vertices[i][NY] = normalY; - vertices[i][NZ] = normalZ; - } - } - - normalCount++; - if (normalCount == 1) { - // One normal per begin/end shape - normalMode = MANUAL_SHAPE_NORMAL; - } - else { - // a separate normal for each vertex - normalMode = MANUAL_VERTEX_NORMAL; - } - } - } - - - /** - * Set texture mode to either to use coordinates based on the IMAGE - * (more intuitive for new users) or NORMALIZED (better for advanced chaps) - */ - public void textureMode(int mode) { - this.textureMode = mode; - } - - - /** - * Set texture image for current shape - * needs to be called between @see beginShape and @see endShape - * - * @param image reference to a PImage object - */ - public void texture(PImage image) { - textureImage = image; - - if (texture_index == textures.length - 1) { - PImage temp[] = new PImage[texture_index<<1]; - System.arraycopy(textures, 0, temp, 0, texture_index); - textures = temp; - //message(CHATTER, "allocating more textures " + textures.length); - } - - if (textures[0] != null) { // wHY? - texture_index++; - } - - textures[texture_index] = image; - } - - - public void vertex(float x, float y) { - setup_vertex(x, y, 0); - } - - - public void vertex(float x, float y, float u, float v) { - texture_vertex(u, v); - setup_vertex(x, y, 0); - } - - - public void vertex(float x, float y, float z) { - setup_vertex(x, y, z); - } - - - public void vertex(float x, float y, float z, - float u, float v) { - texture_vertex(u, v); - setup_vertex(x, y, z); - } - - - protected void setup_vertex(float x, float y, float z) { - if (vertexCount == vertices.length) { - float temp[][] = new float[vertexCount << 1][VERTEX_FIELD_COUNT]; - System.arraycopy(vertices, 0, temp, 0, vertexCount); - vertices = temp; - int temp2[] = new int[vertexCount << 1]; - System.arraycopy(vertex_order, 0, temp2, 0, vertexCount); - vertex_order = temp2; - //message(CHATTER, "allocating more vertices " + vertices.length); - } - float vertex[] = vertices[vertexCount++]; - - //if (polygon.redundantVertex(x, y, z)) return; - - // user called vertex(), so that invalidates anything queued - // up for curve vertices. if this is internally called by - // spline_segment, then splineVertexCount will be saved and restored. - splineVertexCount = 0; - - vertex[MX] = x; - vertex[MY] = y; - vertex[MZ] = z; - - if (fill) { - vertex[R] = fillR; - vertex[G] = fillG; - vertex[B] = fillB; - vertex[A] = fillA; - - vertex[AR] = ambientR; - vertex[AG] = ambientG; - vertex[AB] = ambientB; - - vertex[SPR] = specularR; - vertex[SPG] = specularG; - vertex[SPB] = specularB; - vertex[SPA] = specularA; - - vertex[SHINE] = shininess; - - vertex[ER] = emissiveR; - vertex[EG] = emissiveG; - vertex[EB] = emissiveB; - } - - if (stroke) { - vertex[SR] = strokeR; - vertex[SG] = strokeG; - vertex[SB] = strokeB; - vertex[SA] = strokeA; - vertex[SW] = strokeWeight; - } - - if (textureImage != null) { - vertex[U] = textureU; - vertex[V] = textureV; - } - - vertex[NX] = normalX; - vertex[NY] = normalY; - vertex[NZ] = normalZ; - - vertex[BEEN_LIT] = 0; - } - - - /** - * set UV coords for the next vertex in the current shape. - * this is ugly as its own fxn, and will almost always be - * coincident with a call to vertex, so it's being moved - * to be an optional param of and overloaded vertex() - * - * @param u U coordinate (X coord in image 0<=X<=image width) - * @param v V coordinate (Y coord in image 0<=Y<=image height) - */ - protected void texture_vertex(float u, float v) { - if (textureImage == null) { - throw new RuntimeException("need to set an image with texture() " + - "before using u and v coordinates"); - } - if (textureMode == IMAGE) { - u /= (float) textureImage.width; - v /= (float) textureImage.height; - } - - textureU = u; - textureV = v; - - if (textureU < 0) textureU = 0; - else if (textureU > ONE) textureU = ONE; - - if (textureV < 0) textureV = 0; - else if (textureV > ONE) textureV = ONE; - } - - - protected void spline_vertex(float x, float y, float z, boolean bezier) { - // allocate space for the spline vertices - // to improve processing applet load times, don't allocate until actual use - 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; - } - - float vertex[] = splineVertices[splineVertexCount]; - - vertex[MX] = x; - vertex[MY] = y; - vertex[MZ] = z; - - if (fill) { - vertex[R] = fillR; - vertex[G] = fillG; - vertex[B] = fillB; - vertex[A] = fillA; - } - - if (stroke) { - vertex[SR] = strokeR; - vertex[SG] = strokeG; - vertex[SB] = strokeB; - vertex[SA] = strokeA; - vertex[SW] = strokeWeight; - } - - if (textureImage != null) { - vertex[U] = textureU; - vertex[V] = textureV; - } - - vertex[NX] = normalX; - vertex[NY] = normalY; - vertex[NZ] = normalZ; - - splineVertexCount++; - - // draw a segment if there are enough points - if (splineVertexCount > 3) { - if (bezier) { - if ((splineVertexCount % 4) == 0) { - if (!bezierInited) bezier_init(); - spline3_segment(splineVertexCount-4, - splineVertexCount-4, - bezier_draw, - bezierDetail); - } - } else { // catmull-rom curve (!bezier) - if (!curve_inited) curve_init(); - spline3_segment(splineVertexCount-4, - splineVertexCount-3, - curve_draw, - curveDetail); - } - } - } - - - /** - * See notes with the bezier() function. - */ - public void bezierVertex(float x2, float y2, - float x3, float y3, - float x4, float y4) { - bezierVertex(x2, y2, 0, x3, y3, 0, x4, y4, 0); - } - - - /** - * See notes with the bezier() function. - */ - public void bezierVertex(float x2, float y2, float z2, - float x3, float y3, float z3, - float x4, float y4, float z4) { - if (splineVertexCount > 0) { - float vertex[] = splineVertices[splineVertexCount-1]; - spline_vertex(vertex[MX], vertex[MY], vertex[MZ], true); - - } else if (vertexCount > 0) { - // make sure there's at least a call to vertex() - float vertex[] = vertices[vertexCount-1]; - spline_vertex(vertex[MX], vertex[MY], vertex[MZ], true); - - } else { - throw new RuntimeException("A call to vertex() must be used " + - "before bezierVertex()"); - } - spline_vertex(x2, y2, z2, true); - spline_vertex(x3, y3, z3, true); - spline_vertex(x4, y4, z4, true); - } - - - /** - * See notes with the curve() function. - */ - public void curveVertex(float x, float y) { - spline_vertex(x, y, 0, false); - } - - /** - * See notes with the curve() function. - */ - public void curveVertex(float x, float y, float z) { - spline_vertex(x, y, z, false); - } - - - public void endShape() { - vertex_end = vertexCount; - vertex_end_including_clip_verts = vertex_end; - - // don't try to draw if there are no vertices - // (fixes a bug in LINE_LOOP that re-adds a nonexistent vertex) - if (vertexCount == 0) { - shape = 0; - return; - } - - - // ------------------------------------------------------------------ - // 2D or 3D POINTS FROM MODEL (MX, MY, MZ) TO CAMERA SPACE (VX, VY, VZ) - // It is necessary to do this now because we will be clipping them on - // add_triangle. - - for (int i = vertex_start; i < vertex_end; i++) { - float vertex[] = vertices[i]; - - vertex[VX] = - modelview.m00*vertex[MX] + modelview.m01*vertex[MY] + - modelview.m02*vertex[MZ] + modelview.m03; - vertex[VY] = - modelview.m10*vertex[MX] + modelview.m11*vertex[MY] + - modelview.m12*vertex[MZ] + modelview.m13; - vertex[VZ] = - modelview.m20*vertex[MX] + modelview.m21*vertex[MY] + - modelview.m22*vertex[MZ] + modelview.m23; - vertex[VW] = - modelview.m30*vertex[MX] + modelview.m31*vertex[MY] + - modelview.m32*vertex[MZ] + modelview.m33; - - // normalize - if (vertex[VW] != 0 && vertex[VW] != ONE) { - vertex[VX] /= vertex[VW]; - vertex[VY] /= vertex[VW]; - vertex[VZ] /= vertex[VW]; - } - vertex[VW] = ONE; - } - - // ------------------------------------------------------------------ - // CREATE LINES - - int increment = 1; - int stop = 0; - //int counter = 0; - - if (stroke) { - switch (shape) { - - case POINTS: - { - stop = vertex_end; - for (int i = vertex_start; i < stop; i++) { - add_path(); // total overkill for points - add_line(i, i); - } - } - break; - - case LINES: - case LINE_STRIP: - case LINE_LOOP: - { - // store index of first vertex - int first = lineCount; - stop = vertex_end - 1; - increment = (shape == LINES) ? 2 : 1; - - // for LINE_STRIP and LINE_LOOP, make this all one path - if (shape != LINES) add_path(); - - for (int i = vertex_start; i < stop; i+=increment) { - // for LINES, make a new path for each segment - if (shape == LINES) add_path(); - add_line(i, i+1); - } - - // for LINE_LOOP, close the loop with a final segment - if (shape == LINE_LOOP) { - add_line(stop, lines[first][VERTEX1]); - } - } - break; - - case TRIANGLES: - { - for (int i = vertex_start; i < vertex_end-2; i += 3) { - add_path(); - //counter = i - vertex_start; - add_line(i+0, i+1); - add_line(i+1, i+2); - add_line(i+2, i+0); - } - } - break; - - case TRIANGLE_STRIP: - { - // first draw all vertices as a line strip - stop = vertex_end-1; - - add_path(); - for (int i = vertex_start; i < stop; i++) { - //counter = i - vertex_start; - add_line(i,i+1); - } - - // then draw from vertex (n) to (n+2) - stop = vertex_end-2; - for (int i = vertex_start; i < stop; i++) { - add_path(); - add_line(i,i+2); - } - } - break; - - case TRIANGLE_FAN: - { - // this just draws a series of line segments - // from the center to each exterior point - for (int i = vertex_start + 1; i < vertex_end; i++) { - add_path(); - add_line(vertex_start, i); - } - - // then a single line loop around the outside. - add_path(); - for (int i = vertex_start + 1; i < vertex_end-1; i++) { - add_line(i, i+1); - } - // closing the loop - add_line(vertex_end-1, vertex_start + 1); - } - break; - - case QUADS: - { - for (int i = vertex_start; i < vertex_end; i += 4) { - add_path(); - //counter = i - vertex_start; - add_line(i+0, i+1); - add_line(i+1, i+2); - add_line(i+2, i+3); - add_line(i+3, i+0); - } - } - break; - - case QUAD_STRIP: - { - for (int i = vertex_start; i < vertex_end - 3; i += 2) { - add_path(); - add_line(i+0, i+2); - add_line(i+2, i+3); - add_line(i+3, i+1); - add_line(i+1, i+0); - } - /* - // first draw all vertices as a line strip - stop = vertex_end - 1; - - add_path(); - for (int i = vertex_start; i < stop; i++) { - counter = i - vertex_start; - add_line(i, i+1); - } - - // then draw from vertex (n) to (n+3) - stop = vertex_end-2; - increment = 2; - - add_path(); - for (int i = vertex_start; i < stop; i += increment) { - add_line(i, i+3); - } - */ - } - break; - - case POLYGON: - //case CONCAVE_POLYGON: - //case CONVEX_POLYGON: - { - // store index of first vertex - int first = lineCount; - stop = vertex_end - 1; - - add_path(); - for (int i = vertex_start; i < stop; i++) { - add_line(i, i+1); - } - // draw the last line connecting back to the first point in poly - add_line(stop, lines[first][VERTEX1]); - } - break; - } - } - - // ------------------------------------------------------------------ - // CREATE TRIANGLES - - if (fill) { - switch (shape) { - case TRIANGLE_FAN: - { - stop = vertex_end - 1; - for (int i = vertex_start + 1; i < stop; i++) { - add_triangle(vertex_start, i, i+1); - } - } - break; - - case TRIANGLES: - case TRIANGLE_STRIP: - { - stop = vertex_end - 2; - increment = (shape == TRIANGLES) ? 3 : 1; - for (int i = vertex_start; i < stop; i += increment) { - // have to switch between clockwise/counter-clockwise - // otherwise the feller is backwards and renderer won't draw - if ((i % 2) == 0) { - add_triangle(i, i+2, i+1); - } else { - add_triangle(i, i+1, i+2); - } - } - } - break; - - case QUADS: - { - stop = vertexCount-3; - for (int i = vertex_start; i < stop; i += 4) { - // first triangle - add_triangle(i, i+1, i+2); - // second triangle - add_triangle(i, i+2, i+3); - } - } - break; - - case QUAD_STRIP: - { - stop = vertexCount-3; - for (int i = vertex_start; i < stop; i += 2) { - // first triangle - add_triangle(i+0, i+2, i+1); - // second triangle - add_triangle(i+2, i+3, i+1); - } - } - break; - - case POLYGON: - //case CONCAVE_POLYGON: - //case CONVEX_POLYGON: - { - triangulate_polygon(); - } - break; - } - } - - - // ------------------------------------------------------------------ - // TRANSFORM / LIGHT / CLIP - - if (lightCount > 0 && fill) { - handle_lighting(); - } - else { - handle_no_lighting(); - } - - - - // ------------------------------------------------------------------ - // POINTS FROM CAMERA SPACE (VX, VY, VZ) TO SCREEN SPACE (X, Y, Z) - // this appears to be wasted time with the opengl renderer - - for (int i = vertex_start; i < vertex_end_including_clip_verts; i++) { - float vx[] = vertices[i]; - - float ox = - projection.m00*vx[VX] + projection.m01*vx[VY] + - projection.m02*vx[VZ] + projection.m03*vx[VW]; - float oy = - projection.m10*vx[VX] + projection.m11*vx[VY] + - projection.m12*vx[VZ] + projection.m13*vx[VW]; - float oz = - projection.m20*vx[VX] + projection.m21*vx[VY] + - projection.m22*vx[VZ] + projection.m23*vx[VW]; - float ow = - projection.m30*vx[VX] + projection.m31*vx[VY] + - projection.m32*vx[VZ] + projection.m33*vx[VW]; - - if (ow != 0 && ow != ONE) { - ox /= ow; oy /= ow; oz /= ow; - } - - vx[X] = width * (ONE + ox) / 2.0f; - vx[Y] = height * (ONE + oy) / 2.0f; - vx[Z] = (oz + ONE) / 2.0f; - } - - - // ------------------------------------------------------------------ - // RENDER SHAPES FILLS HERE WHEN NOT DEPTH SORTING - - // if true, the shapes will be rendered on endDraw - if (!hints[ENABLE_DEPTH_SORT]) { - if (fill) render_triangles(); - if (stroke) render_lines(); - } - - shape = 0; - } - - - protected final void add_path() { - if (pathCount == pathOffset.length) { - int temp1[] = new int[pathCount << 1]; - System.arraycopy(pathOffset, 0, temp1, 0, pathCount); - pathOffset = temp1; - int temp2[] = new int[pathCount << 1]; - System.arraycopy(pathLength, 0, temp2, 0, pathCount); - pathLength = temp2; - } - pathOffset[pathCount] = lineCount; - pathLength[pathCount] = 0; - pathCount++; - } - - - protected void add_line(int a, int b) { - add_line_with_clip(a, b); - } - - protected final void add_line_with_clip(int a, int b) { - float az = vertices[a][VZ]; - float bz = vertices[b][VZ]; - if (az > cameraNear) { - if (bz > cameraNear) { - return; - } - int cb = interpolate_clip_vertex(a, b); - add_line_no_clip(cb, b); - return; - } - else { - if (bz <= cameraNear) { - add_line_no_clip(a, b); - return; - } - int cb = interpolate_clip_vertex(a, b); - add_line_no_clip(a, cb); - return; - } - } - - protected final void add_line_no_clip(int a, int b) { - if (lineCount == lines.length) { - int temp[][] = new int[lineCount<<1][LINE_FIELD_COUNT]; - System.arraycopy(lines, 0, temp, 0, lineCount); - lines = temp; - //message(CHATTER, "allocating more lines " + lines.length); - } - lines[lineCount][VERTEX1] = a; - lines[lineCount][VERTEX2] = b; - lines[lineCount][INDEX] = -1; - - lines[lineCount][STROKE_MODE] = strokeCap | strokeJoin; - lines[lineCount][STROKE_WEIGHT] = (int) (strokeWeight + 0.5f); // hmm - lineCount++; - - // mark this piece as being part of the current path - pathLength[pathCount-1]++; - } - - - protected void add_triangle(int a, int b, int c) { - add_triangle_with_clip(a, b, c); - //add_triangle_no_clip(a, b, c); - } - - protected final void add_triangle_with_clip(int a, int b, int c) { - boolean aClipped = false; - boolean bClipped = false; - //boolean cClipped = false; - int clippedCount = 0; - - cameraNear = -8; - if (vertices[a][VZ] > cameraNear) { - aClipped = true; - clippedCount++; - } - if (vertices[b][VZ] > cameraNear) { - bClipped = true; - clippedCount++; - } - if (vertices[c][VZ] > cameraNear) { - //cClipped = true; - clippedCount++; - } - if (clippedCount == 0) { - add_triangle_no_clip(a, b, c); - return; - } - else if (clippedCount == 3) { - return; - } - // | . - // In this case there is only one visible point. |/| - // So we'll have to make two new points on the clip line <| | - // and add that triangle instead. |\| - // | . - else if (clippedCount == 2) { - //System.out.println("Clipped two"); - - int ca, cb, cc, cd, ce; - if (!aClipped) { - ca = a; - cb = b; - cc = c; - } - else if (!bClipped) { - ca = b; - cb = a; - cc = c; - } - else { //if (!cClipped) { - ca = c; - cb = b; - cc = a; - } - - cd = interpolate_clip_vertex(ca, cb); - ce = interpolate_clip_vertex(ca, cc); - add_triangle_no_clip(ca, cd, ce); - return; - } - - // . | - // In this case there are two visible points. |\| - // So we'll have to make two new points on the clip line | |> - // and then add two new triangles. |/| - // . | - else { // (clippedCount == 1) { - //System.out.println("Clipped one"); - int ca, cb, cc, cd, ce; - if (aClipped) { - //System.out.println("aClipped"); - ca = c; - cb = b; - cc = a; - } - else if (bClipped) { - //System.out.println("bClipped"); - ca = a; - cb = c; - cc = b; - } - else { //if (cClipped) { - //System.out.println("cClipped"); - ca = a; - cb = b; - cc = c; - } - - cd = interpolate_clip_vertex(ca, cc); - ce = interpolate_clip_vertex(cb, cc); - add_triangle_no_clip(ca, cd, cb); - //System.out.println("ca: " + ca + ", " + vertices[ca][VX] + ", " + vertices[ca][VY] + ", " + vertices[ca][VZ]); - //System.out.println("cd: " + cd + ", " + vertices[cd][VX] + ", " + vertices[cd][VY] + ", " + vertices[cd][VZ]); - //System.out.println("cb: " + cb + ", " + vertices[cb][VX] + ", " + vertices[cb][VY] + ", " + vertices[cb][VZ]); - add_triangle_no_clip(cb, cd, ce); - return; - } - } - - private final int interpolate_clip_vertex(int a, int b) { - float[] va; - float[] vb; - // Set up va, vb such that va[VZ] >= vb[VZ] - if (vertices[a][VZ] < vertices[b][VZ]) { - va = vertices[b]; - vb = vertices[a]; - } - else { - va = vertices[a]; - vb = vertices[b]; - } - float az = va[VZ]; - float bz = vb[VZ]; - - float dz = az - bz; - // If they have the same z, just use pt. a. - if (dz == 0) { - return a; - } - //float pa = (az - cameraNear) / dz; - //float pb = (cameraNear - bz) / dz; - float pa = (cameraNear - bz) / dz; - float pb = 1 - pa; - - vertex(pa * va[MX] + pb * vb[MX], - pa * va[MY] + pb * vb[MY], - pa * va[MZ] + pb * vb[MZ]); - int irv = vertexCount - 1; - vertex_end_including_clip_verts++; - float[] rv = vertices[irv]; - - rv[X] = pa * va[X] + pb * vb[X]; - rv[Y] = pa * va[Y] + pb * vb[Y]; - rv[Z] = pa * va[Z] + pb * vb[Z]; - - rv[VX] = pa * va[VX] + pb * vb[VX]; - rv[VY] = pa * va[VY] + pb * vb[VY]; - rv[VZ] = pa * va[VZ] + pb * vb[VZ]; - rv[VW] = pa * va[VW] + pb * vb[VW]; - - rv[R] = pa * va[R] + pb * vb[R]; - rv[G] = pa * va[G] + pb * vb[G]; - rv[B] = pa * va[B] + pb * vb[B]; - rv[A] = pa * va[A] + pb * vb[A]; - - rv[U] = pa * va[U] + pb * vb[U]; - rv[V] = pa * va[V] + pb * vb[V]; - - rv[SR] = pa * va[SR] + pb * vb[SR]; - rv[SG] = pa * va[SG] + pb * vb[SG]; - rv[SB] = pa * va[SB] + pb * vb[SB]; - rv[SA] = pa * va[SA] + pb * vb[SA]; - - rv[NX] = pa * va[NX] + pb * vb[NX]; - rv[NY] = pa * va[NY] + pb * vb[NY]; - rv[NZ] = pa * va[NZ] + pb * vb[NZ]; - - rv[SW] = pa * va[SW] + pb * vb[SW]; - - rv[AR] = pa * va[AR] + pb * vb[AR]; - rv[AG] = pa * va[AG] + pb * vb[AG]; - rv[AB] = pa * va[AB] + pb * vb[AB]; - - rv[SPR] = pa * va[SPR] + pb * vb[SPR]; - rv[SPG] = pa * va[SPG] + pb * vb[SPG]; - rv[SPB] = pa * va[SPB] + pb * vb[SPB]; - rv[SPA] = pa * va[SPA] + pb * vb[SPA]; - - rv[ER] = pa * va[ER] + pb * vb[ER]; - rv[EG] = pa * va[EG] + pb * vb[EG]; - rv[EB] = pa * va[EB] + pb * vb[EB]; - - rv[SHINE] = pa * va[SHINE] + pb * vb[SHINE]; - - rv[BEEN_LIT] = 0; - - return irv; - } - - - protected final void add_triangle_no_clip(int a, int b, int c) { - //System.out.println("adding triangle " + triangleCount); - if (triangleCount == triangles.length) { - int temp[][] = new int[triangleCount<<1][TRIANGLE_FIELD_COUNT]; - System.arraycopy(triangles, 0, temp, 0, triangleCount); - triangles = temp; - //message(CHATTER, "allocating more triangles " + triangles.length); - float ftemp[][][] = new float[triangleCount<<1][3][TRIANGLE_COLOR_COUNT]; - System.arraycopy(triangleColors, 0, ftemp, 0, triangleCount); - triangleColors = ftemp; - } - triangles[triangleCount][VERTEX1] = a; - triangles[triangleCount][VERTEX2] = b; - triangles[triangleCount][VERTEX3] = c; - - if (textureImage == null) { - triangles[triangleCount][TEXTURE_INDEX] = -1; - } else { - triangles[triangleCount][TEXTURE_INDEX] = texture_index; - } - - triangles[triangleCount][INDEX] = shape_index; - triangleCount++; - } - - - protected void depth_sort_triangles() { - depth_sort_triangles_internal(0, triangleCount-1); - } - - - protected void depth_sort_triangles_internal(int i, int j) { - int pivotIndex = (i+j)/2; - depth_sort_triangles_swap(pivotIndex, j); - int k = depth_sort_triangles_partition(i-1, j); - depth_sort_triangles_swap(k, j); - if ((k-i) > 1) depth_sort_triangles_internal(i, k-1); - if ((j-k) > 1) depth_sort_triangles_internal(k+1, j); - } - - - protected int depth_sort_triangles_partition(int left, int right) { - int pivot = right; - do { - while (depth_sort_triangles_compare(++left, pivot) < 0) { } - while ((right != 0) && - (depth_sort_triangles_compare(--right, pivot) > 0)) { } - depth_sort_triangles_swap(left, right); - } while (left < right); - depth_sort_triangles_swap(left, right); - return left; - } - - - protected void depth_sort_triangles_swap(int a, int b) { - int tempi[] = triangles[a]; - triangles[a] = triangles[b]; - triangles[b] = tempi; - float tempf[][] = triangleColors[a]; - triangleColors[a] = triangleColors[b]; - triangleColors[b] = tempf; - } - - - protected float depth_sort_triangles_compare(int a, int b) { - return - (vertices[triangles[b][VERTEX1]][Z] + - vertices[triangles[b][VERTEX2]][Z] + - vertices[triangles[b][VERTEX3]][Z]) - - (vertices[triangles[a][VERTEX1]][Z] + - vertices[triangles[a][VERTEX2]][Z] + - vertices[triangles[a][VERTEX3]][Z]); - } - - - protected void render_triangles() { - //System.out.println("rendering " + triangleCount + " triangles"); - - if (raw != null) { - raw.colorMode(RGB, 1); - raw.noStroke(); - raw.beginShape(TRIANGLES); - } - - for (int i = 0; i < triangleCount; i ++) { - float a[] = vertices[triangles[i][VERTEX1]]; - float b[] = vertices[triangles[i][VERTEX2]]; - float c[] = vertices[triangles[i][VERTEX3]]; - int tex = triangles[i][TEXTURE_INDEX]; - int index = triangles[i][INDEX]; - - triangle.reset(); - - // This is only true when not textured. We really should pass SPECULAR - // straight through to triangle rendering. - float ar = min(1, triangleColors[i][0][TRI_DIFFUSE_R] + - triangleColors[i][0][TRI_SPECULAR_R]); - float ag = min(1, triangleColors[i][0][TRI_DIFFUSE_G] + - triangleColors[i][0][TRI_SPECULAR_G]); - float ab = min(1, triangleColors[i][0][TRI_DIFFUSE_B] + - triangleColors[i][0][TRI_SPECULAR_B]); - float br = min(1, triangleColors[i][1][TRI_DIFFUSE_R] + - triangleColors[i][1][TRI_SPECULAR_R]); - float bg = min(1, triangleColors[i][1][TRI_DIFFUSE_G] + - triangleColors[i][1][TRI_SPECULAR_G]); - float bb = min(1, triangleColors[i][1][TRI_DIFFUSE_B] + - triangleColors[i][1][TRI_SPECULAR_B]); - float cr = min(1, triangleColors[i][2][TRI_DIFFUSE_R] + - triangleColors[i][2][TRI_SPECULAR_R]); - float cg = min(1, triangleColors[i][2][TRI_DIFFUSE_G] + - triangleColors[i][2][TRI_SPECULAR_G]); - float cb = min(1, triangleColors[i][2][TRI_DIFFUSE_B] + - triangleColors[i][2][TRI_SPECULAR_B]); - - if (tex > -1 && textures[tex] != null) { - triangle.setTexture(textures[tex]); - triangle.setUV(a[U], a[V], b[U], b[V], c[U], c[V]); - } - - triangle.setIntensities(ar, ag, ab, a[A], - br, bg, bb, b[A], - cr, cg, cb, c[A]); - - triangle.setVertices(a[X], a[Y], a[Z], - b[X], b[Y], b[Z], - c[X], c[Y], c[Z]); - - triangle.setIndex(index); - triangle.render(); - - //System.out.println(i + " " + a[Z] + " " + b[Z] + " " + c[Z]); - - if (raw != null) { - if (raw instanceof PGraphics3) { - if ((a[VW] != 0) && (b[VW] != 0) && (c[VW] != 0)) { - raw.fill(ar, ag, ab, a[A]); - raw.vertex(a[VX] / a[VW], a[VY] / a[VW], a[VZ] / a[VW]); - raw.fill(br, bg, bb, b[A]); - raw.vertex(b[VX] / b[VW], b[VY] / b[VW], b[VZ] / b[VW]); - raw.fill(cr, cg, cb, c[A]); - raw.vertex(c[VX] / c[VW], c[VY] / c[VW], c[VZ] / c[VW]); - } - } else { - raw.fill(ar, ag, ab, a[A]); - raw.vertex(a[X], a[Y]); - raw.fill(br, bg, bb, b[A]); - raw.vertex(b[X], b[Y]); - raw.fill(cr, cg, cb, c[A]); - raw.vertex(c[X], c[Y]); - } - } - } - - if (raw != null) { - raw.endShape(); - } - } - - - /* - public void triangleCallback(float x1, float y1, float z1, - float r1, float g1, float b1, float a1, - float u1, float v1, boolean e1, - float x2, float y2, float z2, - float r2, float g2, float b2, float a2, - float u2, float v2, boolean e2, - float x3, float y3, float z3, - float r3, float g3, float b3, float a3, - float u3, float v3, boolean e3, - PImage texture) { - } - - - public void lineCallback(float x1, float y1, float z1, - float r1, float g1, float b1, float a1, - float x2, float y2, float z2, - float r2, float g2, float b2, float a2, - float weight, int cap, int join) { - } - */ - - - protected void depth_sort_lines() { - } - - - protected void render_lines() { - if (raw != null) { - raw.colorMode(RGB, 1); - raw.noFill(); - raw.beginShape(LINES); - } - - for (int i = 0; i < lineCount; i ++) { - float a[] = vertices[lines[i][VERTEX1]]; - float b[] = vertices[lines[i][VERTEX2]]; - int index = lines[i][INDEX]; - - line.reset(); - - line.setIntensities(a[SR], a[SG], a[SB], a[SA], - b[SR], b[SG], b[SB], b[SA]); - - line.setVertices(a[X], a[Y], a[Z], - b[X], b[Y], b[Z]); - - if (raw != null) { - if (raw instanceof PGraphics3) { - if ((a[VW] != 0) && (b[VW] != 0)) { - raw.stroke(a[SR], a[SG], a[SB], a[SA]); - raw.vertex(a[VX] / a[VW], a[VY] / a[VW], a[VZ] / a[VW]); - raw.stroke(b[SR], b[SG], b[SB], b[SA]); - raw.vertex(b[VX] / b[VW], b[VY] / b[VW], b[VZ] / b[VW]); - } - } else { - raw.stroke(a[SR], a[SG], a[SB], a[SA]); - raw.vertex(a[X], a[Y]); - raw.stroke(b[SR], b[SG], b[SB], b[SA]); - raw.vertex(b[X], b[Y]); - } - } - - line.setIndex(index); - line.draw(); - } - - if (raw != null) { - raw.endShape(); - } - } - - - /** - * Triangulate the current polygon. - *

- * 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. - *

- * 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 - *

-   * [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. - *

- * 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 - *

-   * 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. - *

- * 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: - *

-   * 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. - *

- * 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: - *

-   * 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. - *

- * 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: - *

- * 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. - *

- * 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. - *

-   * 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. - *

- * 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. + *

+ * Lighting and camera implementation by Simon Greenwold. + */ +public class PGraphics3 extends PGraphics { + + // ........................................................ + + // Lighting-related variables + + // Whether or not we have to worry about vertex position for lighting calcs + private boolean lightingDependsOnVertexPosition; + + static final int LIGHT_AMBIENT_R = 0; + static final int LIGHT_AMBIENT_G = 1; + static final int LIGHT_AMBIENT_B = 2; + static final int LIGHT_DIFFUSE_R = 3; + static final int LIGHT_DIFFUSE_G = 4; + static final int LIGHT_DIFFUSE_B = 5; + static final int LIGHT_SPECULAR_R = 6; + static final int LIGHT_SPECULAR_G = 7; + static final int LIGHT_SPECULAR_B = 8; + + static final int LIGHT_COLOR_COUNT = 9; + + // Used to shuttle lighting calcs around + // (no need to re-allocate all the time) + protected float[] tempLightingContribution = new float[LIGHT_COLOR_COUNT]; + protected float[] worldNormal = new float[4]; + + // Used in light_triangle(). Allocated here once to + // avoid re-allocating each time + protected float[] dv1 = new float[3]; + protected float[] dv2 = new float[3]; + protected float[] norm = new float[3]; + + // ........................................................ + + /** + * This is turned on at beginCamera, and off at endCamera + * Currently we don't support nested begin/end cameras. + * If we wanted to, this variable would have to become a stack. + */ + protected boolean manipulatingCamera; + + // These two matrices always point to either the modelview + // or the modelviewInv, but they are swapped during + // when in camera maniuplation mode. That way camera transforms + // are automatically accumulated in inverse on the modelview matrix. + protected PMatrix forwardTransform; + protected PMatrix reverseTransform; + + // ........................................................ + + // pos of first vertex of current shape in vertices array + protected int vertex_start; + + // i think vertex_end is actually the last vertex in the current shape + // and is separate from vertexCount for occasions where drawing happens + // on endDraw() with all the triangles being depth sorted + protected int vertex_end; + + // vertices may be added during clipping against the near plane. + protected int vertex_end_including_clip_verts; + + // used for sorting points when triangulating a polygon + // warning - maximum number of vertices for a polygon is DEFAULT_VERTICES + protected int vertex_order[] = new int[DEFAULT_VERTICES]; + + // ........................................................ + + protected int pathCount; + protected int pathOffset[] = new int[64]; + protected int pathLength[] = new int[64]; + + // ........................................................ + + // lines + static final int DEFAULT_LINES = 512; + public PLine line; // used for drawing + protected int lines[][] = new int[DEFAULT_LINES][LINE_FIELD_COUNT]; + protected int lineCount; + + // ........................................................ + + // triangles + static final int DEFAULT_TRIANGLES = 256; + public PTriangle triangle; + protected int triangles[][] = + new int[DEFAULT_TRIANGLES][TRIANGLE_FIELD_COUNT]; + protected float triangleColors[][][] = + new float[DEFAULT_TRIANGLES][3][TRIANGLE_COLOR_COUNT]; + protected int triangleCount; // total number of triangles + + // cheap picking someday + public int shape_index; + + // ........................................................ + + /** + * 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; + + //public PImage textureImage; + + static final int DEFAULT_TEXTURES = 3; + protected PImage textures[] = new PImage[DEFAULT_TEXTURES]; + int texture_index; + + // ........................................................ + + /** + * 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() + float sphereX[], sphereY[], sphereZ[]; + //public int sphereDetail = 0; + + // ........................................................ + + + /** + * Constructor for the PGraphics3 object. + * This prototype only exists because of annoying + * java compilers, and should not be used. + */ + public PGraphics3() { + forwardTransform = modelview; + reverseTransform = modelviewInv; + } + + + public PGraphics3(int iwidth, int iheight) { + this(iwidth, iheight, null); + } + + + /** + * Constructor for the PGraphics3 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 PGraphics3(int iwidth, int iheight, PApplet parent) { + // super will add the listeners to the applet, and call resize() + super(iwidth, iheight, parent); + forwardTransform = modelview; + reverseTransform = modelviewInv; + //resize(iwidth, iheight); + //projection = new PMatrix(); + } + + + /** + * 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("PGraphics3 resize"); + + width = iwidth; + height = iheight; + width1 = width - 1; + height1 = height - 1; + + allocate(); + + // clear the screen with the old background color + //background(backgroundColor); + + // init perspective projection based on new dimensions + cameraFOV = 60 * DEG_TO_RAD; // at least for now + cameraX = width / 2.0f; + cameraY = height / 2.0f; + //cameraZ = cameraY / ((float) tan(PI * cameraFOV / 360f)); + cameraZ = cameraY / ((float) tan(cameraFOV / 2.0f)); + cameraNear = cameraZ / 10.0f; + cameraFar = cameraZ * 10.0f; + + cameraAspect = (float)width / (float)height; + + // init lights (in resize() instead of allocate() b/c needed by opengl) + lightType = new int[MAX_LIGHTS]; + lightPosition = new float[MAX_LIGHTS][3]; + lightDiffuse = new float[MAX_LIGHTS][3]; + lightNormal = new float[MAX_LIGHTS][3]; + lightSpecular = new float[MAX_LIGHTS][3]; + lightFalloffConstant = new float[MAX_LIGHTS]; + lightFalloffLinear = new float[MAX_LIGHTS]; + lightFalloffQuadratic = new float[MAX_LIGHTS]; + lightSpotAngle = new float[MAX_LIGHTS]; + lightSpotAngleCos = new float[MAX_LIGHTS]; + lightSpotConcentration = new float[MAX_LIGHTS]; + currentLightSpecular = new float[3]; + + // reset the cameraMode if PERSPECTIVE or ORTHOGRAPHIC + // will just be ignored if CUSTOM, the user's hosed anyways + //System.out.println("setting cameraMode to " + cameraMode); + //if (this.cameraMode != CUSTOM) cameraMode(this.cameraMode); + + // making this again here because things are weird + projection = new PMatrix(); + + modelview = new PMatrix(MATRIX_STACK_DEPTH); + modelviewInv = new PMatrix(MATRIX_STACK_DEPTH); + forwardTransform = modelview; + reverseTransform = modelviewInv; + + camera = new PMatrix(); + cameraInv = new PMatrix(); + + // set up the default camera + camera(); + + // defaults to perspective, if the user has setup up their + // own projection, they'll need to fix it after resize anyway. + // this helps the people who haven't set up their own projection. + perspective(); + } + + + protected void allocate() { + //System.out.println("allocating for " + width + " " + height); + + 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; + + 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); + + zbuffer = new float[pixelCount]; + stencil = new int[pixelCount]; + + line = new PLine(this); + triangle = new PTriangle(this); + } + + + public void beginDraw() { + super.beginDraw(); + + modelview.set(camera); + modelviewInv.set(cameraInv); + + // clear out the lights, they'll have to be turned on again + lightCount = 0; + lightingDependsOnVertexPosition = false; + lightFalloff(1, 0, 0); + lightSpecular(0, 0, 0); + + // reset lines + lineCount = 0; + if (line != null) line.reset(); // is this necessary? + pathCount = 0; + + // reset triangles + triangleCount = 0; + if (triangle != null) triangle.reset(); // necessary? + + vertex_start = 0; + //vertex_end = 0; + + // reset textures + texture_index = 0; + + normal(0, 0, 1); + } + + + /** + * See notes in PGraphics. + * If z-sorting has been turned on, then the triangles will + * all be quicksorted here (to make alpha work more properly) + * and then blit to the screen. + */ + public void endDraw() { + // no need to z order and render + // shapes were already rendered in endShape(); + // (but can't return, since needs to update memimgsrc + if (hints[ENABLE_DEPTH_SORT]) { + flush(); + } + // blit to screen + super.endDraw(); + } + + + /** + * Emit any sorted geometry that's been collected on this frame. + */ + protected void flush() { + if (triangleCount > 0) { + if (hints[ENABLE_DEPTH_SORT]) { + depth_sort_triangles(); + } + render_triangles(); + } + if (lineCount > 0) { + if (hints[ENABLE_DEPTH_SORT]) { + depth_sort_lines(); + } + render_lines(); + } + } + + + public void defaults() { + super.defaults(); + + manipulatingCamera = false; + forwardTransform = modelview; + reverseTransform = modelviewInv; + + perspective(); + + // easiest for beginners + textureMode(IMAGE); + + emissive(0.0f); + specular(0.5f); + shininess(1.0f); + } + + + ////////////////////////////////////////////////////////////// + + + public void beginShape(int kind) { + shape = kind; + + shape_index = shape_index + 1; + if (shape_index == -1) { + shape_index = 0; + } + + if (hints[ENABLE_DEPTH_SORT]) { + // continue with previous vertex, line and triangle count + // all shapes are rendered at endDraw(); + vertex_start = vertexCount; + vertex_end = 0; + + } else { + // reset vertex, line and triangle information + // every shape is rendered at endShape(); + vertexCount = 0; + if (line != null) line.reset(); // necessary? + lineCount = 0; + pathCount = 0; + if (triangle != null) triangle.reset(); // necessary? + triangleCount = 0; + } + textureImage = null; + + splineVertexCount = 0; + normalMode = AUTO_NORMAL; + normalCount = 0; + } + + + /** + * Sets the current normal vector. + *

+ * This is for drawing three dimensional shapes and surfaces, + * allowing you to specify a vector perpendicular to the surface + * of the shape, which determines how lighting affects it. + *

+ * For the most part, PGraphics will attempt to automatically + * assign normals to shapes, but since that's imperfect, + * this is a better option when you want more control. + *

+ * For people familiar with OpenGL, this function is basically + * identical to glNormal3f(). + *

+ * Only applies inside a beginShape/endShape block. + */ + public void normal(float nx, float ny, float nz) { + normalX = nx; + normalY = ny; + normalZ = nz; + + // if drawing a shape and the normal hasn't been set yet, + // then we need to set the normals for each vertex so far + if (shape != 0) { + if (normalCount == 0) { + for (int i = vertex_start; i < vertexCount; i++) { + vertices[i][NX] = normalX; + vertices[i][NY] = normalY; + vertices[i][NZ] = normalZ; + } + } + + normalCount++; + if (normalCount == 1) { + // One normal per begin/end shape + normalMode = MANUAL_SHAPE_NORMAL; + } + else { + // a separate normal for each vertex + normalMode = MANUAL_VERTEX_NORMAL; + } + } + } + + + /** + * Set texture mode to either to use coordinates based on the IMAGE + * (more intuitive for new users) or NORMALIZED (better for advanced chaps) + */ + public void textureMode(int mode) { + this.textureMode = mode; + } + + + /** + * Set texture image for current shape + * needs to be called between @see beginShape and @see endShape + * + * @param image reference to a PImage object + */ + public void texture(PImage image) { + textureImage = image; + + if (texture_index == textures.length - 1) { + PImage temp[] = new PImage[texture_index<<1]; + System.arraycopy(textures, 0, temp, 0, texture_index); + textures = temp; + //message(CHATTER, "allocating more textures " + textures.length); + } + + if (textures[0] != null) { // wHY? + texture_index++; + } + + textures[texture_index] = image; + } + + + public void vertex(float x, float y) { + setup_vertex(x, y, 0); + } + + + public void vertex(float x, float y, float u, float v) { + texture_vertex(u, v); + setup_vertex(x, y, 0); + } + + + public void vertex(float x, float y, float z) { + setup_vertex(x, y, z); + } + + + public void vertex(float x, float y, float z, + float u, float v) { + texture_vertex(u, v); + setup_vertex(x, y, z); + } + + + protected void setup_vertex(float x, float y, float z) { + if (vertexCount == vertices.length) { + float temp[][] = new float[vertexCount << 1][VERTEX_FIELD_COUNT]; + System.arraycopy(vertices, 0, temp, 0, vertexCount); + vertices = temp; + int temp2[] = new int[vertexCount << 1]; + System.arraycopy(vertex_order, 0, temp2, 0, vertexCount); + vertex_order = temp2; + //message(CHATTER, "allocating more vertices " + vertices.length); + } + float vertex[] = vertices[vertexCount++]; + + //if (polygon.redundantVertex(x, y, z)) return; + + // user called vertex(), so that invalidates anything queued + // up for curve vertices. if this is internally called by + // spline_segment, then splineVertexCount will be saved and restored. + splineVertexCount = 0; + + vertex[MX] = x; + vertex[MY] = y; + vertex[MZ] = z; + + if (fill) { + vertex[R] = fillR; + vertex[G] = fillG; + vertex[B] = fillB; + vertex[A] = fillA; + + vertex[AR] = ambientR; + vertex[AG] = ambientG; + vertex[AB] = ambientB; + + vertex[SPR] = specularR; + vertex[SPG] = specularG; + vertex[SPB] = specularB; + vertex[SPA] = specularA; + + vertex[SHINE] = shininess; + + vertex[ER] = emissiveR; + vertex[EG] = emissiveG; + vertex[EB] = emissiveB; + } + + if (stroke) { + vertex[SR] = strokeR; + vertex[SG] = strokeG; + vertex[SB] = strokeB; + vertex[SA] = strokeA; + vertex[SW] = strokeWeight; + } + + if (textureImage != null) { + vertex[U] = textureU; + vertex[V] = textureV; + } + + vertex[NX] = normalX; + vertex[NY] = normalY; + vertex[NZ] = normalZ; + + vertex[BEEN_LIT] = 0; + } + + + /** + * set UV coords for the next vertex in the current shape. + * this is ugly as its own fxn, and will almost always be + * coincident with a call to vertex, so it's being moved + * to be an optional param of and overloaded vertex() + * + * @param u U coordinate (X coord in image 0<=X<=image width) + * @param v V coordinate (Y coord in image 0<=Y<=image height) + */ + protected void texture_vertex(float u, float v) { + if (textureImage == null) { + throw new RuntimeException("need to set an image with texture() " + + "before using u and v coordinates"); + } + if (textureMode == IMAGE) { + u /= (float) textureImage.width; + v /= (float) textureImage.height; + } + + textureU = u; + textureV = v; + + if (textureU < 0) textureU = 0; + else if (textureU > ONE) textureU = ONE; + + if (textureV < 0) textureV = 0; + else if (textureV > ONE) textureV = ONE; + } + + + protected void spline_vertex(float x, float y, float z, boolean bezier) { + // allocate space for the spline vertices + // to improve processing applet load times, don't allocate until actual use + 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; + } + + float vertex[] = splineVertices[splineVertexCount]; + + vertex[MX] = x; + vertex[MY] = y; + vertex[MZ] = z; + + if (fill) { + vertex[R] = fillR; + vertex[G] = fillG; + vertex[B] = fillB; + vertex[A] = fillA; + } + + if (stroke) { + vertex[SR] = strokeR; + vertex[SG] = strokeG; + vertex[SB] = strokeB; + vertex[SA] = strokeA; + vertex[SW] = strokeWeight; + } + + if (textureImage != null) { + vertex[U] = textureU; + vertex[V] = textureV; + } + + vertex[NX] = normalX; + vertex[NY] = normalY; + vertex[NZ] = normalZ; + + splineVertexCount++; + + // draw a segment if there are enough points + if (splineVertexCount > 3) { + if (bezier) { + if ((splineVertexCount % 4) == 0) { + if (!bezierInited) bezier_init(); + spline3_segment(splineVertexCount-4, + splineVertexCount-4, + bezier_draw, + bezierDetail); + } + } else { // catmull-rom curve (!bezier) + if (!curve_inited) curve_init(); + spline3_segment(splineVertexCount-4, + splineVertexCount-3, + curve_draw, + curveDetail); + } + } + } + + + /** + * See notes with the bezier() function. + */ + public void bezierVertex(float x2, float y2, + float x3, float y3, + float x4, float y4) { + bezierVertex(x2, y2, 0, x3, y3, 0, x4, y4, 0); + } + + + /** + * See notes with the bezier() function. + */ + public void bezierVertex(float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4) { + if (splineVertexCount > 0) { + float vertex[] = splineVertices[splineVertexCount-1]; + spline_vertex(vertex[MX], vertex[MY], vertex[MZ], true); + + } else if (vertexCount > 0) { + // make sure there's at least a call to vertex() + float vertex[] = vertices[vertexCount-1]; + spline_vertex(vertex[MX], vertex[MY], vertex[MZ], true); + + } else { + throw new RuntimeException("A call to vertex() must be used " + + "before bezierVertex()"); + } + spline_vertex(x2, y2, z2, true); + spline_vertex(x3, y3, z3, true); + spline_vertex(x4, y4, z4, true); + } + + + /** + * See notes with the curve() function. + */ + public void curveVertex(float x, float y) { + spline_vertex(x, y, 0, false); + } + + /** + * See notes with the curve() function. + */ + public void curveVertex(float x, float y, float z) { + spline_vertex(x, y, z, false); + } + + + public void endShape() { + vertex_end = vertexCount; + vertex_end_including_clip_verts = vertex_end; + + // don't try to draw if there are no vertices + // (fixes a bug in LINE_LOOP that re-adds a nonexistent vertex) + if (vertexCount == 0) { + shape = 0; + return; + } + + + // ------------------------------------------------------------------ + // 2D or 3D POINTS FROM MODEL (MX, MY, MZ) TO CAMERA SPACE (VX, VY, VZ) + // It is necessary to do this now because we will be clipping them on + // add_triangle. + + for (int i = vertex_start; i < vertex_end; i++) { + float vertex[] = vertices[i]; + + vertex[VX] = + modelview.m00*vertex[MX] + modelview.m01*vertex[MY] + + modelview.m02*vertex[MZ] + modelview.m03; + vertex[VY] = + modelview.m10*vertex[MX] + modelview.m11*vertex[MY] + + modelview.m12*vertex[MZ] + modelview.m13; + vertex[VZ] = + modelview.m20*vertex[MX] + modelview.m21*vertex[MY] + + modelview.m22*vertex[MZ] + modelview.m23; + vertex[VW] = + modelview.m30*vertex[MX] + modelview.m31*vertex[MY] + + modelview.m32*vertex[MZ] + modelview.m33; + + // normalize + if (vertex[VW] != 0 && vertex[VW] != ONE) { + vertex[VX] /= vertex[VW]; + vertex[VY] /= vertex[VW]; + vertex[VZ] /= vertex[VW]; + } + vertex[VW] = ONE; + } + + // ------------------------------------------------------------------ + // CREATE LINES + + int increment = 1; + int stop = 0; + //int counter = 0; + + if (stroke) { + switch (shape) { + + case POINTS: + { + stop = vertex_end; + for (int i = vertex_start; i < stop; i++) { + add_path(); // total overkill for points + add_line(i, i); + } + } + break; + + case LINES: + case LINE_STRIP: + case LINE_LOOP: + { + // store index of first vertex + int first = lineCount; + stop = vertex_end - 1; + increment = (shape == LINES) ? 2 : 1; + + // for LINE_STRIP and LINE_LOOP, make this all one path + if (shape != LINES) add_path(); + + for (int i = vertex_start; i < stop; i+=increment) { + // for LINES, make a new path for each segment + if (shape == LINES) add_path(); + add_line(i, i+1); + } + + // for LINE_LOOP, close the loop with a final segment + if (shape == LINE_LOOP) { + add_line(stop, lines[first][VERTEX1]); + } + } + break; + + case TRIANGLES: + { + for (int i = vertex_start; i < vertex_end-2; i += 3) { + add_path(); + //counter = i - vertex_start; + add_line(i+0, i+1); + add_line(i+1, i+2); + add_line(i+2, i+0); + } + } + break; + + case TRIANGLE_STRIP: + { + // first draw all vertices as a line strip + stop = vertex_end-1; + + add_path(); + for (int i = vertex_start; i < stop; i++) { + //counter = i - vertex_start; + add_line(i,i+1); + } + + // then draw from vertex (n) to (n+2) + stop = vertex_end-2; + for (int i = vertex_start; i < stop; i++) { + add_path(); + add_line(i,i+2); + } + } + break; + + case TRIANGLE_FAN: + { + // this just draws a series of line segments + // from the center to each exterior point + for (int i = vertex_start + 1; i < vertex_end; i++) { + add_path(); + add_line(vertex_start, i); + } + + // then a single line loop around the outside. + add_path(); + for (int i = vertex_start + 1; i < vertex_end-1; i++) { + add_line(i, i+1); + } + // closing the loop + add_line(vertex_end-1, vertex_start + 1); + } + break; + + case QUADS: + { + for (int i = vertex_start; i < vertex_end; i += 4) { + add_path(); + //counter = i - vertex_start; + add_line(i+0, i+1); + add_line(i+1, i+2); + add_line(i+2, i+3); + add_line(i+3, i+0); + } + } + break; + + case QUAD_STRIP: + { + for (int i = vertex_start; i < vertex_end - 3; i += 2) { + add_path(); + add_line(i+0, i+2); + add_line(i+2, i+3); + add_line(i+3, i+1); + add_line(i+1, i+0); + } + /* + // first draw all vertices as a line strip + stop = vertex_end - 1; + + add_path(); + for (int i = vertex_start; i < stop; i++) { + counter = i - vertex_start; + add_line(i, i+1); + } + + // then draw from vertex (n) to (n+3) + stop = vertex_end-2; + increment = 2; + + add_path(); + for (int i = vertex_start; i < stop; i += increment) { + add_line(i, i+3); + } + */ + } + break; + + case POLYGON: + //case CONCAVE_POLYGON: + //case CONVEX_POLYGON: + { + // store index of first vertex + int first = lineCount; + stop = vertex_end - 1; + + add_path(); + for (int i = vertex_start; i < stop; i++) { + add_line(i, i+1); + } + // draw the last line connecting back to the first point in poly + add_line(stop, lines[first][VERTEX1]); + } + break; + } + } + + // ------------------------------------------------------------------ + // CREATE TRIANGLES + + if (fill) { + switch (shape) { + case TRIANGLE_FAN: + { + stop = vertex_end - 1; + for (int i = vertex_start + 1; i < stop; i++) { + add_triangle(vertex_start, i, i+1); + } + } + break; + + case TRIANGLES: + case TRIANGLE_STRIP: + { + stop = vertex_end - 2; + increment = (shape == TRIANGLES) ? 3 : 1; + for (int i = vertex_start; i < stop; i += increment) { + // have to switch between clockwise/counter-clockwise + // otherwise the feller is backwards and renderer won't draw + if ((i % 2) == 0) { + add_triangle(i, i+2, i+1); + } else { + add_triangle(i, i+1, i+2); + } + } + } + break; + + case QUADS: + { + stop = vertexCount-3; + for (int i = vertex_start; i < stop; i += 4) { + // first triangle + add_triangle(i, i+1, i+2); + // second triangle + add_triangle(i, i+2, i+3); + } + } + break; + + case QUAD_STRIP: + { + stop = vertexCount-3; + for (int i = vertex_start; i < stop; i += 2) { + // first triangle + add_triangle(i+0, i+2, i+1); + // second triangle + add_triangle(i+2, i+3, i+1); + } + } + break; + + case POLYGON: + //case CONCAVE_POLYGON: + //case CONVEX_POLYGON: + { + triangulate_polygon(); + } + break; + } + } + + + // ------------------------------------------------------------------ + // TRANSFORM / LIGHT / CLIP + + if (lightCount > 0 && fill) { + handle_lighting(); + } + else { + handle_no_lighting(); + } + + + + // ------------------------------------------------------------------ + // POINTS FROM CAMERA SPACE (VX, VY, VZ) TO SCREEN SPACE (X, Y, Z) + // this appears to be wasted time with the opengl renderer + + for (int i = vertex_start; i < vertex_end_including_clip_verts; i++) { + float vx[] = vertices[i]; + + float ox = + projection.m00*vx[VX] + projection.m01*vx[VY] + + projection.m02*vx[VZ] + projection.m03*vx[VW]; + float oy = + projection.m10*vx[VX] + projection.m11*vx[VY] + + projection.m12*vx[VZ] + projection.m13*vx[VW]; + float oz = + projection.m20*vx[VX] + projection.m21*vx[VY] + + projection.m22*vx[VZ] + projection.m23*vx[VW]; + float ow = + projection.m30*vx[VX] + projection.m31*vx[VY] + + projection.m32*vx[VZ] + projection.m33*vx[VW]; + + if (ow != 0 && ow != ONE) { + ox /= ow; oy /= ow; oz /= ow; + } + + vx[X] = width * (ONE + ox) / 2.0f; + vx[Y] = height * (ONE + oy) / 2.0f; + vx[Z] = (oz + ONE) / 2.0f; + } + + + // ------------------------------------------------------------------ + // RENDER SHAPES FILLS HERE WHEN NOT DEPTH SORTING + + // if true, the shapes will be rendered on endDraw + if (!hints[ENABLE_DEPTH_SORT]) { + if (fill) render_triangles(); + if (stroke) render_lines(); + } + + shape = 0; + } + + + protected final void add_path() { + if (pathCount == pathOffset.length) { + int temp1[] = new int[pathCount << 1]; + System.arraycopy(pathOffset, 0, temp1, 0, pathCount); + pathOffset = temp1; + int temp2[] = new int[pathCount << 1]; + System.arraycopy(pathLength, 0, temp2, 0, pathCount); + pathLength = temp2; + } + pathOffset[pathCount] = lineCount; + pathLength[pathCount] = 0; + pathCount++; + } + + + protected void add_line(int a, int b) { + add_line_with_clip(a, b); + } + + protected final void add_line_with_clip(int a, int b) { + float az = vertices[a][VZ]; + float bz = vertices[b][VZ]; + if (az > cameraNear) { + if (bz > cameraNear) { + return; + } + int cb = interpolate_clip_vertex(a, b); + add_line_no_clip(cb, b); + return; + } + else { + if (bz <= cameraNear) { + add_line_no_clip(a, b); + return; + } + int cb = interpolate_clip_vertex(a, b); + add_line_no_clip(a, cb); + return; + } + } + + protected final void add_line_no_clip(int a, int b) { + if (lineCount == lines.length) { + int temp[][] = new int[lineCount<<1][LINE_FIELD_COUNT]; + System.arraycopy(lines, 0, temp, 0, lineCount); + lines = temp; + //message(CHATTER, "allocating more lines " + lines.length); + } + lines[lineCount][VERTEX1] = a; + lines[lineCount][VERTEX2] = b; + lines[lineCount][INDEX] = -1; + + lines[lineCount][STROKE_MODE] = strokeCap | strokeJoin; + lines[lineCount][STROKE_WEIGHT] = (int) (strokeWeight + 0.5f); // hmm + lineCount++; + + // mark this piece as being part of the current path + pathLength[pathCount-1]++; + } + + + protected void add_triangle(int a, int b, int c) { + add_triangle_with_clip(a, b, c); + //add_triangle_no_clip(a, b, c); + } + + protected final void add_triangle_with_clip(int a, int b, int c) { + boolean aClipped = false; + boolean bClipped = false; + //boolean cClipped = false; + int clippedCount = 0; + + cameraNear = -8; + if (vertices[a][VZ] > cameraNear) { + aClipped = true; + clippedCount++; + } + if (vertices[b][VZ] > cameraNear) { + bClipped = true; + clippedCount++; + } + if (vertices[c][VZ] > cameraNear) { + //cClipped = true; + clippedCount++; + } + if (clippedCount == 0) { + add_triangle_no_clip(a, b, c); + return; + } + else if (clippedCount == 3) { + return; + } + // | . + // In this case there is only one visible point. |/| + // So we'll have to make two new points on the clip line <| | + // and add that triangle instead. |\| + // | . + else if (clippedCount == 2) { + //System.out.println("Clipped two"); + + int ca, cb, cc, cd, ce; + if (!aClipped) { + ca = a; + cb = b; + cc = c; + } + else if (!bClipped) { + ca = b; + cb = a; + cc = c; + } + else { //if (!cClipped) { + ca = c; + cb = b; + cc = a; + } + + cd = interpolate_clip_vertex(ca, cb); + ce = interpolate_clip_vertex(ca, cc); + add_triangle_no_clip(ca, cd, ce); + return; + } + + // . | + // In this case there are two visible points. |\| + // So we'll have to make two new points on the clip line | |> + // and then add two new triangles. |/| + // . | + else { // (clippedCount == 1) { + //System.out.println("Clipped one"); + int ca, cb, cc, cd, ce; + if (aClipped) { + //System.out.println("aClipped"); + ca = c; + cb = b; + cc = a; + } + else if (bClipped) { + //System.out.println("bClipped"); + ca = a; + cb = c; + cc = b; + } + else { //if (cClipped) { + //System.out.println("cClipped"); + ca = a; + cb = b; + cc = c; + } + + cd = interpolate_clip_vertex(ca, cc); + ce = interpolate_clip_vertex(cb, cc); + add_triangle_no_clip(ca, cd, cb); + //System.out.println("ca: " + ca + ", " + vertices[ca][VX] + ", " + vertices[ca][VY] + ", " + vertices[ca][VZ]); + //System.out.println("cd: " + cd + ", " + vertices[cd][VX] + ", " + vertices[cd][VY] + ", " + vertices[cd][VZ]); + //System.out.println("cb: " + cb + ", " + vertices[cb][VX] + ", " + vertices[cb][VY] + ", " + vertices[cb][VZ]); + add_triangle_no_clip(cb, cd, ce); + return; + } + } + + private final int interpolate_clip_vertex(int a, int b) { + float[] va; + float[] vb; + // Set up va, vb such that va[VZ] >= vb[VZ] + if (vertices[a][VZ] < vertices[b][VZ]) { + va = vertices[b]; + vb = vertices[a]; + } + else { + va = vertices[a]; + vb = vertices[b]; + } + float az = va[VZ]; + float bz = vb[VZ]; + + float dz = az - bz; + // If they have the same z, just use pt. a. + if (dz == 0) { + return a; + } + //float pa = (az - cameraNear) / dz; + //float pb = (cameraNear - bz) / dz; + float pa = (cameraNear - bz) / dz; + float pb = 1 - pa; + + vertex(pa * va[MX] + pb * vb[MX], + pa * va[MY] + pb * vb[MY], + pa * va[MZ] + pb * vb[MZ]); + int irv = vertexCount - 1; + vertex_end_including_clip_verts++; + float[] rv = vertices[irv]; + + rv[X] = pa * va[X] + pb * vb[X]; + rv[Y] = pa * va[Y] + pb * vb[Y]; + rv[Z] = pa * va[Z] + pb * vb[Z]; + + rv[VX] = pa * va[VX] + pb * vb[VX]; + rv[VY] = pa * va[VY] + pb * vb[VY]; + rv[VZ] = pa * va[VZ] + pb * vb[VZ]; + rv[VW] = pa * va[VW] + pb * vb[VW]; + + rv[R] = pa * va[R] + pb * vb[R]; + rv[G] = pa * va[G] + pb * vb[G]; + rv[B] = pa * va[B] + pb * vb[B]; + rv[A] = pa * va[A] + pb * vb[A]; + + rv[U] = pa * va[U] + pb * vb[U]; + rv[V] = pa * va[V] + pb * vb[V]; + + rv[SR] = pa * va[SR] + pb * vb[SR]; + rv[SG] = pa * va[SG] + pb * vb[SG]; + rv[SB] = pa * va[SB] + pb * vb[SB]; + rv[SA] = pa * va[SA] + pb * vb[SA]; + + rv[NX] = pa * va[NX] + pb * vb[NX]; + rv[NY] = pa * va[NY] + pb * vb[NY]; + rv[NZ] = pa * va[NZ] + pb * vb[NZ]; + + rv[SW] = pa * va[SW] + pb * vb[SW]; + + rv[AR] = pa * va[AR] + pb * vb[AR]; + rv[AG] = pa * va[AG] + pb * vb[AG]; + rv[AB] = pa * va[AB] + pb * vb[AB]; + + rv[SPR] = pa * va[SPR] + pb * vb[SPR]; + rv[SPG] = pa * va[SPG] + pb * vb[SPG]; + rv[SPB] = pa * va[SPB] + pb * vb[SPB]; + rv[SPA] = pa * va[SPA] + pb * vb[SPA]; + + rv[ER] = pa * va[ER] + pb * vb[ER]; + rv[EG] = pa * va[EG] + pb * vb[EG]; + rv[EB] = pa * va[EB] + pb * vb[EB]; + + rv[SHINE] = pa * va[SHINE] + pb * vb[SHINE]; + + rv[BEEN_LIT] = 0; + + return irv; + } + + + protected final void add_triangle_no_clip(int a, int b, int c) { + //System.out.println("adding triangle " + triangleCount); + if (triangleCount == triangles.length) { + int temp[][] = new int[triangleCount<<1][TRIANGLE_FIELD_COUNT]; + System.arraycopy(triangles, 0, temp, 0, triangleCount); + triangles = temp; + //message(CHATTER, "allocating more triangles " + triangles.length); + float ftemp[][][] = new float[triangleCount<<1][3][TRIANGLE_COLOR_COUNT]; + System.arraycopy(triangleColors, 0, ftemp, 0, triangleCount); + triangleColors = ftemp; + } + triangles[triangleCount][VERTEX1] = a; + triangles[triangleCount][VERTEX2] = b; + triangles[triangleCount][VERTEX3] = c; + + if (textureImage == null) { + triangles[triangleCount][TEXTURE_INDEX] = -1; + } else { + triangles[triangleCount][TEXTURE_INDEX] = texture_index; + } + + triangles[triangleCount][INDEX] = shape_index; + triangleCount++; + } + + + protected void depth_sort_triangles() { + depth_sort_triangles_internal(0, triangleCount-1); + } + + + protected void depth_sort_triangles_internal(int i, int j) { + int pivotIndex = (i+j)/2; + depth_sort_triangles_swap(pivotIndex, j); + int k = depth_sort_triangles_partition(i-1, j); + depth_sort_triangles_swap(k, j); + if ((k-i) > 1) depth_sort_triangles_internal(i, k-1); + if ((j-k) > 1) depth_sort_triangles_internal(k+1, j); + } + + + protected int depth_sort_triangles_partition(int left, int right) { + int pivot = right; + do { + while (depth_sort_triangles_compare(++left, pivot) < 0) { } + while ((right != 0) && + (depth_sort_triangles_compare(--right, pivot) > 0)) { } + depth_sort_triangles_swap(left, right); + } while (left < right); + depth_sort_triangles_swap(left, right); + return left; + } + + + protected void depth_sort_triangles_swap(int a, int b) { + int tempi[] = triangles[a]; + triangles[a] = triangles[b]; + triangles[b] = tempi; + float tempf[][] = triangleColors[a]; + triangleColors[a] = triangleColors[b]; + triangleColors[b] = tempf; + } + + + protected float depth_sort_triangles_compare(int a, int b) { + return + (vertices[triangles[b][VERTEX1]][Z] + + vertices[triangles[b][VERTEX2]][Z] + + vertices[triangles[b][VERTEX3]][Z]) - + (vertices[triangles[a][VERTEX1]][Z] + + vertices[triangles[a][VERTEX2]][Z] + + vertices[triangles[a][VERTEX3]][Z]); + } + + + protected void render_triangles() { + //System.out.println("rendering " + triangleCount + " triangles"); + + if (raw != null) { + raw.colorMode(RGB, 1); + raw.noStroke(); + raw.beginShape(TRIANGLES); + } + + for (int i = 0; i < triangleCount; i ++) { + float a[] = vertices[triangles[i][VERTEX1]]; + float b[] = vertices[triangles[i][VERTEX2]]; + float c[] = vertices[triangles[i][VERTEX3]]; + int tex = triangles[i][TEXTURE_INDEX]; + int index = triangles[i][INDEX]; + + triangle.reset(); + + // This is only true when not textured. We really should pass SPECULAR + // straight through to triangle rendering. + float ar = min(1, triangleColors[i][0][TRI_DIFFUSE_R] + + triangleColors[i][0][TRI_SPECULAR_R]); + float ag = min(1, triangleColors[i][0][TRI_DIFFUSE_G] + + triangleColors[i][0][TRI_SPECULAR_G]); + float ab = min(1, triangleColors[i][0][TRI_DIFFUSE_B] + + triangleColors[i][0][TRI_SPECULAR_B]); + float br = min(1, triangleColors[i][1][TRI_DIFFUSE_R] + + triangleColors[i][1][TRI_SPECULAR_R]); + float bg = min(1, triangleColors[i][1][TRI_DIFFUSE_G] + + triangleColors[i][1][TRI_SPECULAR_G]); + float bb = min(1, triangleColors[i][1][TRI_DIFFUSE_B] + + triangleColors[i][1][TRI_SPECULAR_B]); + float cr = min(1, triangleColors[i][2][TRI_DIFFUSE_R] + + triangleColors[i][2][TRI_SPECULAR_R]); + float cg = min(1, triangleColors[i][2][TRI_DIFFUSE_G] + + triangleColors[i][2][TRI_SPECULAR_G]); + float cb = min(1, triangleColors[i][2][TRI_DIFFUSE_B] + + triangleColors[i][2][TRI_SPECULAR_B]); + + if (tex > -1 && textures[tex] != null) { + triangle.setTexture(textures[tex]); + triangle.setUV(a[U], a[V], b[U], b[V], c[U], c[V]); + } + + triangle.setIntensities(ar, ag, ab, a[A], + br, bg, bb, b[A], + cr, cg, cb, c[A]); + + triangle.setVertices(a[X], a[Y], a[Z], + b[X], b[Y], b[Z], + c[X], c[Y], c[Z]); + + triangle.setIndex(index); + triangle.render(); + + //System.out.println(i + " " + a[Z] + " " + b[Z] + " " + c[Z]); + + if (raw != null) { + if (raw instanceof PGraphics3) { + if ((a[VW] != 0) && (b[VW] != 0) && (c[VW] != 0)) { + raw.fill(ar, ag, ab, a[A]); + raw.vertex(a[VX] / a[VW], a[VY] / a[VW], a[VZ] / a[VW]); + raw.fill(br, bg, bb, b[A]); + raw.vertex(b[VX] / b[VW], b[VY] / b[VW], b[VZ] / b[VW]); + raw.fill(cr, cg, cb, c[A]); + raw.vertex(c[VX] / c[VW], c[VY] / c[VW], c[VZ] / c[VW]); + } + } else { + raw.fill(ar, ag, ab, a[A]); + raw.vertex(a[X], a[Y]); + raw.fill(br, bg, bb, b[A]); + raw.vertex(b[X], b[Y]); + raw.fill(cr, cg, cb, c[A]); + raw.vertex(c[X], c[Y]); + } + } + } + + if (raw != null) { + raw.endShape(); + } + } + + + /* + public void triangleCallback(float x1, float y1, float z1, + float r1, float g1, float b1, float a1, + float u1, float v1, boolean e1, + float x2, float y2, float z2, + float r2, float g2, float b2, float a2, + float u2, float v2, boolean e2, + float x3, float y3, float z3, + float r3, float g3, float b3, float a3, + float u3, float v3, boolean e3, + PImage texture) { + } + + + public void lineCallback(float x1, float y1, float z1, + float r1, float g1, float b1, float a1, + float x2, float y2, float z2, + float r2, float g2, float b2, float a2, + float weight, int cap, int join) { + } + */ + + + protected void depth_sort_lines() { + } + + + protected void render_lines() { + if (raw != null) { + raw.colorMode(RGB, 1); + raw.noFill(); + raw.beginShape(LINES); + } + + for (int i = 0; i < lineCount; i ++) { + float a[] = vertices[lines[i][VERTEX1]]; + float b[] = vertices[lines[i][VERTEX2]]; + int index = lines[i][INDEX]; + + line.reset(); + + line.setIntensities(a[SR], a[SG], a[SB], a[SA], + b[SR], b[SG], b[SB], b[SA]); + + line.setVertices(a[X], a[Y], a[Z], + b[X], b[Y], b[Z]); + + if (raw != null) { + if (raw instanceof PGraphics3) { + if ((a[VW] != 0) && (b[VW] != 0)) { + raw.stroke(a[SR], a[SG], a[SB], a[SA]); + raw.vertex(a[VX] / a[VW], a[VY] / a[VW], a[VZ] / a[VW]); + raw.stroke(b[SR], b[SG], b[SB], b[SA]); + raw.vertex(b[VX] / b[VW], b[VY] / b[VW], b[VZ] / b[VW]); + } + } else { + raw.stroke(a[SR], a[SG], a[SB], a[SA]); + raw.vertex(a[X], a[Y]); + raw.stroke(b[SR], b[SG], b[SB], b[SA]); + raw.vertex(b[X], b[Y]); + } + } + + line.setIndex(index); + line.draw(); + } + + if (raw != null) { + raw.endShape(); + } + } + + + /** + * Triangulate the current polygon. + *

+ * 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. + *

+ * 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 + *

+   * [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. + *

+ * 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 + *

+   * 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. + *

+ * 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: + *

+   * 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. + *

+ * 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: + *

+   * 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. + *

+ * 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: + *

+ * 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. + *

+ * 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. + *

+   * 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. + *

+ * 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. - *

- * Also gets called by textFont, so the metrics - * will get recorded properly. - */ - public void textSize(float size) { - // if a native version available, subset this font - if (textFontNative != null) { - textFontNative = textFontNative.deriveFont(size); - g2.setFont(textFontNative); - textFontNativeMetrics = g2.getFontMetrics(textFontNative); - } - - // take care of setting the textSize and textLeading vars - // this has to happen second, because it calls textAscent() - // (which requires the native font metrics to be set) - super.textSize(size); - } - - - protected float textWidthImpl(char buffer[], int start, int stop) { - if (textFontNative == null) { - //System.out.println("native is null"); - return super.textWidthImpl(buffer, start, stop); - } - // maybe should use one of the newer/fancier functions for this? - int length = stop - start; - return textFontNativeMetrics.charsWidth(buffer, start, length); - } - - - protected void textLinePlacedImpl(char buffer[], int start, int stop, - float x, float y) { - if (textFontNative == null) { - super.textLinePlacedImpl(buffer, start, stop, x, y); - return; - } - - // check to see if this font wants to be smoothed but smoothing - // is currently shut off for the graphics context overall - boolean savedSmooth = smooth; - if (textFont.smooth != savedSmooth) { - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - textFont.smooth ? - RenderingHints.VALUE_ANTIALIAS_ON : - RenderingHints.VALUE_ANTIALIAS_OFF); - } - - g2.setColor(fillColorObject); - // better to use drawString(float, float)? - int length = stop - start; - g2.drawChars(buffer, start, length, (int) (x + 0.5f), (int) (y + 0.5f)); - - // this didn't seem to help the scaling issue - // and creates garbage because of the new temporary object - //java.awt.font.GlyphVector gv = textFontNative.createGlyphVector(g2.getFontRenderContext(), new String(buffer, start, stop)); - //g2.drawGlyphVector(gv, x, y); - - // return to previous smoothing state if it was changed - if (textFont.smooth != savedSmooth) { - if (savedSmooth) { - smooth(); - } else { - noSmooth(); - } - } - textX = x + textWidthImpl(buffer, start, stop); - textY = y; - textZ = 0; // this will get set by the caller if non-zero - } - - - ////////////////////////////////////////////////////////////// - - - public void translate(float tx, float ty) { - g2.translate(tx, ty); - } - - - public void rotate(float angle) { - g2.rotate(angle); - } - - - public void scale(float s) { - g2.scale(s, s); - } - - - public void scale(float sx, float sy) { - g2.scale(sx, sy); - } - - - ////////////////////////////////////////////////////////////// - - - public void pushMatrix() { - if (transformCount == transformStack.length) { - throw new RuntimeException("pushMatrix() cannot use push more than " + - transformStack.length + " times"); - } - transformStack[transformCount] = g2.getTransform(); - transformCount++; - } - - - public void popMatrix() { - if (transformCount == 0) { - throw new RuntimeException("missing a popMatrix() " + - "to go with that pushMatrix()"); - } - transformCount--; - g2.setTransform(transformStack[transformCount]); - } - - - public void resetMatrix() { - g2.setTransform(new AffineTransform()); - } - - - public void applyMatrix(float n00, float n01, float n02, - float n10, float n11, float n12) { - g2.transform(new AffineTransform(n00, n10, n01, n11, n02, n12)); - } - - - public void loadMatrix() { - g2.getTransform().getMatrix(transform); - - m00 = (float) transform[0]; - m01 = (float) transform[2]; - m02 = (float) transform[4]; - - m10 = (float) transform[1]; - m11 = (float) transform[3]; - m12 = (float) transform[5]; - } - - - public float screenX(float x, float y) { - loadMatrix(); - return super.screenX(x, y); - //g2.getTransform().getMatrix(transform); - //return (float)transform[0]*x + (float)transform[2]*y + (float)transform[4]; - } - - - public float screenY(float x, float y) { - loadMatrix(); - return super.screenY(x, y); - //g2.getTransform().getMatrix(transform); - //return (float)transform[1]*x + (float)transform[3]*y + (float)transform[5]; - } - - - ////////////////////////////////////////////////////////////// - - - protected void tintFromCalc() { - super.tintFromCalc(); - // TODO actually implement tinted images - tintColorObject = new Color(tintColor, true); - } - - protected void fillFromCalc() { - super.fillFromCalc(); - fillColorObject = new Color(fillColor, true); - } - - protected void strokeFromCalc() { - super.strokeFromCalc(); - strokeColorObject = new Color(strokeColor, true); - } - - - ////////////////////////////////////////////////////////////// - - - public void strokeWeight(float weight) { - super.strokeWeight(weight); - set_stroke(); - } - - - public void strokeJoin(int join) { - super.strokeJoin(join); - set_stroke(); - } - - - public void strokeCap(int cap) { - super.strokeCap(cap); - set_stroke(); - } - - - protected void set_stroke() { - int cap = BasicStroke.CAP_BUTT; - if (strokeCap == ROUND) { - cap = BasicStroke.CAP_ROUND; - } else if (strokeCap == PROJECT) { - cap = BasicStroke.CAP_SQUARE; - } - - int join = BasicStroke.JOIN_BEVEL; - if (strokeJoin == MITER) { - join = BasicStroke.JOIN_MITER; - } else if (strokeJoin == ROUND) { - join = BasicStroke.JOIN_ROUND; - } - - g2.setStroke(new BasicStroke(strokeWeight, cap, join)); - } - - - ////////////////////////////////////////////////////////////// - - - 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"); - } - // draw the image to screen without any transformations - set(0, 0, image); - } - - - public void clear() { - // in case people do transformations before background(), - // need to handle this with a push/reset/pop - pushMatrix(); - resetMatrix(); - g2.setColor(new Color(backgroundColor)); - g2.fillRect(0, 0, width, height); - popMatrix(); - } - - - - ////////////////////////////////////////////////////////////// - - // FROM PIMAGE - - - public void smooth() { - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - } - - - public void noSmooth() { - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_OFF); - } - - - - ////////////////////////////////////////////////////////////// - - - public void beginRaw(PGraphics recorderRaw) { - throw new RuntimeException("beginRaw() not available with this renderer"); - } - - - public void endRaw() { - } - - - ////////////////////////////////////////////////////////////// - - - public void beginPixels() { - if ((pixels == null) || (pixels.length != width * height)) { - pixels = new int[width * height]; - } - //((BufferedImage) image).getRGB(0, 0, width, height, pixels, 0, width); - WritableRaster raster = ((BufferedImage) image).getRaster(); - raster.getDataElements(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() { - //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. + *

+ * Also gets called by textFont, so the metrics + * will get recorded properly. + */ + public void textSize(float size) { + // if a native version available, subset this font + if (textFontNative != null) { + textFontNative = textFontNative.deriveFont(size); + g2.setFont(textFontNative); + textFontNativeMetrics = g2.getFontMetrics(textFontNative); + } + + // take care of setting the textSize and textLeading vars + // this has to happen second, because it calls textAscent() + // (which requires the native font metrics to be set) + super.textSize(size); + } + + + protected float textWidthImpl(char buffer[], int start, int stop) { + if (textFontNative == null) { + //System.out.println("native is null"); + return super.textWidthImpl(buffer, start, stop); + } + // maybe should use one of the newer/fancier functions for this? + int length = stop - start; + return textFontNativeMetrics.charsWidth(buffer, start, length); + } + + + protected void textLinePlacedImpl(char buffer[], int start, int stop, + float x, float y) { + if (textFontNative == null) { + super.textLinePlacedImpl(buffer, start, stop, x, y); + return; + } + + // check to see if this font wants to be smoothed but smoothing + // is currently shut off for the graphics context overall + boolean savedSmooth = smooth; + if (textFont.smooth != savedSmooth) { + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + textFont.smooth ? + RenderingHints.VALUE_ANTIALIAS_ON : + RenderingHints.VALUE_ANTIALIAS_OFF); + } + + g2.setColor(fillColorObject); + // better to use drawString(float, float)? + int length = stop - start; + g2.drawChars(buffer, start, length, (int) (x + 0.5f), (int) (y + 0.5f)); + + // this didn't seem to help the scaling issue + // and creates garbage because of the new temporary object + //java.awt.font.GlyphVector gv = textFontNative.createGlyphVector(g2.getFontRenderContext(), new String(buffer, start, stop)); + //g2.drawGlyphVector(gv, x, y); + + // return to previous smoothing state if it was changed + if (textFont.smooth != savedSmooth) { + if (savedSmooth) { + smooth(); + } else { + noSmooth(); + } + } + textX = x + textWidthImpl(buffer, start, stop); + textY = y; + textZ = 0; // this will get set by the caller if non-zero + } + + + ////////////////////////////////////////////////////////////// + + + public void translate(float tx, float ty) { + g2.translate(tx, ty); + } + + + public void rotate(float angle) { + g2.rotate(angle); + } + + + public void scale(float s) { + g2.scale(s, s); + } + + + public void scale(float sx, float sy) { + g2.scale(sx, sy); + } + + + ////////////////////////////////////////////////////////////// + + + public void pushMatrix() { + if (transformCount == transformStack.length) { + throw new RuntimeException("pushMatrix() cannot use push more than " + + transformStack.length + " times"); + } + transformStack[transformCount] = g2.getTransform(); + transformCount++; + } + + + public void popMatrix() { + if (transformCount == 0) { + throw new RuntimeException("missing a popMatrix() " + + "to go with that pushMatrix()"); + } + transformCount--; + g2.setTransform(transformStack[transformCount]); + } + + + public void resetMatrix() { + g2.setTransform(new AffineTransform()); + } + + + public void applyMatrix(float n00, float n01, float n02, + float n10, float n11, float n12) { + g2.transform(new AffineTransform(n00, n10, n01, n11, n02, n12)); + } + + + public void loadMatrix() { + g2.getTransform().getMatrix(transform); + + m00 = (float) transform[0]; + m01 = (float) transform[2]; + m02 = (float) transform[4]; + + m10 = (float) transform[1]; + m11 = (float) transform[3]; + m12 = (float) transform[5]; + } + + + public float screenX(float x, float y) { + loadMatrix(); + return super.screenX(x, y); + //g2.getTransform().getMatrix(transform); + //return (float)transform[0]*x + (float)transform[2]*y + (float)transform[4]; + } + + + public float screenY(float x, float y) { + loadMatrix(); + return super.screenY(x, y); + //g2.getTransform().getMatrix(transform); + //return (float)transform[1]*x + (float)transform[3]*y + (float)transform[5]; + } + + + ////////////////////////////////////////////////////////////// + + + protected void tintFromCalc() { + super.tintFromCalc(); + // TODO actually implement tinted images + tintColorObject = new Color(tintColor, true); + } + + protected void fillFromCalc() { + super.fillFromCalc(); + fillColorObject = new Color(fillColor, true); + } + + protected void strokeFromCalc() { + super.strokeFromCalc(); + strokeColorObject = new Color(strokeColor, true); + } + + + ////////////////////////////////////////////////////////////// + + + public void strokeWeight(float weight) { + super.strokeWeight(weight); + set_stroke(); + } + + + public void strokeJoin(int join) { + super.strokeJoin(join); + set_stroke(); + } + + + public void strokeCap(int cap) { + super.strokeCap(cap); + set_stroke(); + } + + + protected void set_stroke() { + int cap = BasicStroke.CAP_BUTT; + if (strokeCap == ROUND) { + cap = BasicStroke.CAP_ROUND; + } else if (strokeCap == PROJECT) { + cap = BasicStroke.CAP_SQUARE; + } + + int join = BasicStroke.JOIN_BEVEL; + if (strokeJoin == MITER) { + join = BasicStroke.JOIN_MITER; + } else if (strokeJoin == ROUND) { + join = BasicStroke.JOIN_ROUND; + } + + g2.setStroke(new BasicStroke(strokeWeight, cap, join)); + } + + + ////////////////////////////////////////////////////////////// + + + 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"); + } + // draw the image to screen without any transformations + set(0, 0, image); + } + + + public void clear() { + // in case people do transformations before background(), + // need to handle this with a push/reset/pop + pushMatrix(); + resetMatrix(); + g2.setColor(new Color(backgroundColor)); + g2.fillRect(0, 0, width, height); + popMatrix(); + } + + + + ////////////////////////////////////////////////////////////// + + // FROM PIMAGE + + + public void smooth() { + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + } + + + public void noSmooth() { + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_OFF); + } + + + + ////////////////////////////////////////////////////////////// + + + public void beginRaw(PGraphics recorderRaw) { + throw new RuntimeException("beginRaw() not available with this renderer"); + } + + + public void endRaw() { + } + + + ////////////////////////////////////////////////////////////// + + + public void beginPixels() { + if ((pixels == null) || (pixels.length != width * height)) { + pixels = new int[width * height]; + } + //((BufferedImage) image).getRGB(0, 0, width, height, pixels, 0, width); + WritableRaster raster = ((BufferedImage) image).getRaster(); + raster.getDataElements(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() { + //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 - * For subclasses where the pixels[] buffer isn't set by default, - * this should copy all data into the pixels[] array - */ - public void beginPixels() { // ignore - } - - - /** - * Call this when finished messing with the pixels[] array. - * Formerly called updatePixels(). - *

- * Mark all pixels as needing update. - */ - public void endPixels() { - endPixels(0, 0, width, height); - } - - - /** - * Mark the pixels in this region as needing an update. - *

- * 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. - *

- *

- * 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=maxRowIdx) - idxRight=currIdx; - if (idxUp<0) - idxUp=0; - if (idxDown>=maxIdx) - idxDown=currIdx; - - int colUp=pixels[idxUp]; - int colLeft=pixels[idxLeft]; - int colDown=pixels[idxDown]; - int colRight=pixels[idxRight]; - - // compute luminance - int currLum = - 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff); - int lumLeft = - 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff); - int lumRight = - 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff); - int lumUp = - 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff); - int lumDown = - 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff); - - if (lumLeft>currLum) { - colOut=colLeft; - currLum=lumLeft; - } - if (lumRight>currLum) { - colOut=colRight; - currLum=lumRight; - } - if (lumUp>currLum) { - colOut=colUp; - currLum=lumUp; - } - if (lumDown>currLum) { - colOut=colDown; - currLum=lumDown; - } - out[currIdx++]=colOut; - } - } - } else { - // dilate (grow dark areas) - while (currIdx=maxRowIdx) - idxRight=currIdx; - if (idxUp<0) - idxUp=0; - if (idxDown>=maxIdx) - idxDown=currIdx; - - int colUp=pixels[idxUp]; - int colLeft=pixels[idxLeft]; - int colDown=pixels[idxDown]; - int colRight=pixels[idxRight]; - - // compute luminance - int currLum = - 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff); - int lumLeft = - 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff); - int lumRight = - 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff); - int lumUp = - 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff); - int lumDown = - 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff); - - if (lumLeft - * BLEND - linear interpolation of colours: C = A*factor + B - * ADD - additive blending with white clip: C = min(A*factor + B, 255) - * SUBTRACT - substractive blend with black clip: C = max(B - A*factor, 0) - * DARKEST - only the darkest colour succeeds: C = min(A*factor, B) - * LIGHTEST - only the lightest colour succeeds: C = max(A*factor, B) - * REPLACE - destination colour equals colour of source pixel: C = A - * - */ - static public int blend(int c1, int c2, int mode) { - switch (mode) { - case BLEND: return blend_multiply(c1, c2); - case ADD: return blend_add_pin(c1, c2); - case SUBTRACT: return blend_sub_pin(c1, c2); - case LIGHTEST: return blend_lightest(c1, c2); - case DARKEST: return blend_darkest(c1, c2); - case REPLACE: return c2; - } - return 0; - } - - - /** - * Copies and blends 1 pixel with MODE to pixel in this image. - */ - public void blend(int sx, int sy, int dx, int dy, int mode) { - if ((dx >= 0) && (dx < width) && (sx >= 0) && (sx < width) && - (dy >= 0) && (dy < height) && (sy >= 0) && (sy < height)) { - pixels[dy * width + dx] = - blend(pixels[dy * width + dx], pixels[sy * width + sx], mode); - } - } - - - /** - * Copies and blends 1 pixel with MODE to pixel in another image - */ - public void blend(PImage src, - int sx, int sy, int dx, int dy, int mode) { - if ((dx >= 0) && (dx < width) && (sx >= 0) && (sx < src.width) && - (dy >= 0) && (dy < height) && (sy >= 0) && (sy < src.height)) { - pixels[dy * width + dx] = - blend(pixels[dy * width + dx], - src.pixels[sy * src.width + sx], mode); - } - } - - - /** - * Blends one area of this image to another area - */ - public void blend(int sx1, int sy1, int sx2, int sy2, - int dx1, int dy1, int dx2, int dy2, int mode) { - blend(this, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); - } - - - /** - * Copies area of one image into another PImage object - */ - public void blend(PImage src, - int sx1, int sy1, int sx2, int sy2, - int dx1, int dy1, int dx2, int dy2, int mode) { - if (imageMode == CORNER) { // if CORNERS, do nothing - sx2 += sx1; sy2 += sy1; - dx2 += dx1; dy2 += dy1; - - //} else if (imageMode == CENTER) { - //sx2 /= 2f; sy2 /= 2f; - //dx2 /= 2f; dy2 /= 2f; - } - - if ((src == this) && - intersect(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)) { - blit_resize(get(sx1, sy1, sx2 - sx1, sy2 - sy1), - 0, 0, sx2 - sx1 - 1, sy2 - sy1 - 1, - pixels, width, height, dx1, dy1, dx2, dy2, mode); - } else { - blit_resize(src, sx1, sy1, sx2, sy2, - pixels, width, height, dx1, dy1, dx2, dy2, mode); - } - } - - - /** - * Check to see if two rectangles intersect one another - */ - protected boolean intersect(int sx1, int sy1, int sx2, int sy2, - int dx1, int dy1, int dx2, int dy2) { - int sw = sx2 - sx1 + 1; - int sh = sy2 - sy1 + 1; - int dw = dx2 - dx1 + 1; - int dh = dy2 - dy1 + 1; - - if (dx1 < sx1) { - dw += dx1 - sx1; - if (dw > sw) { - dw = sw; - } - } else { - int w = sw + sx1 - dx1; - if (dw > w) { - dw = w; - } - } - if (dy1 < sy1) { - dh += dy1 - sy1; - if (dh > sh) { - dh = sh; - } - } else { - int h = sh + sy1 - dy1; - if (dh > h) { - dh = h; - } - } - return !(dw <= 0 || dh <= 0); - } - - - - ////////////////////////////////////////////////////////////// - - // COPYING IMAGE DATA - - - /** - * Duplicate an image, returns new PImage object. - * The pixels[] array for the new object will be unique - * and recopied from the source image. - */ - public Object clone() throws CloneNotSupportedException { // ignore - PImage c = (PImage) super.clone(); - - // super.clone() will only copy the reference to the pixels - // array, so this will do a proper duplication of it instead. - c.pixels = new int[width * height]; - System.arraycopy(pixels, 0, c.pixels, 0, pixels.length); - - // return the goods - return c; - } - - - - ////////////////////////////////////////////////////////////// - - /** - * Internal blitter/resizer/copier from toxi. - * Uses bilinear filtering if smooth() has been enabled - * 'mode' determines the blending mode used in the process. - */ - private void blit_resize(PImage img, - int srcX1, int srcY1, int srcX2, int srcY2, - int[] destPixels, int screenW, int screenH, - int destX1, int destY1, int destX2, int destY2, - int mode) { - if (srcX1 < 0) srcX1 = 0; - if (srcY1 < 0) srcY1 = 0; - if (srcX2 >= img.width) srcX2 = img.width - 1; - if (srcY2 >= img.height) srcY2 = img.height - 1; - - int srcW = srcX2 - srcX1; - int srcH = srcY2 - srcY1; - int destW = destX2 - destX1; - int destH = destY2 - destY1; - - if (!smooth) { - srcW++; srcH++; - } - - if (destW <= 0 || destH <= 0 || - srcW <= 0 || srcH <= 0 || - destX1 >= screenW || destY1 >= screenH || - srcX1 >= img.width || srcY1 >= img.height) { - return; - } - - int dx = (int) (srcW / (float) destW * PRECISIONF); - int dy = (int) (srcH / (float) destH * PRECISIONF); - - srcXOffset = (int) (destX1 < 0 ? -destX1 * dx : srcX1 * PRECISIONF); - srcYOffset = (int) (destY1 < 0 ? -destY1 * dy : srcY1 * PRECISIONF); - - if (destX1 < 0) { - destW += destX1; - destX1 = 0; - } - if (destY1 < 0) { - destH += destY1; - destY1 = 0; - } - - destW = low(destW, screenW - destX1); - destH = low(destH, screenH - destY1); - - int destOffset = destY1 * screenW + destX1; - srcBuffer = img.pixels; - - if (smooth) { - // use bilinear filtering - iw = img.width; - iw1 = img.width - 1; - ih1 = img.height - 1; - - switch (mode) { - - case BLEND: - for (int y = 0; y < destH; y++) { - filter_new_scanline(); - for (int x = 0; x < destW; x++) { - destPixels[destOffset + x] = - blend_multiply(destPixels[destOffset + x], filter_bilinear()); - sX += dx; - } - destOffset += screenW; - srcYOffset += dy; - } - break; - - case ADD: - for (int y = 0; y < destH; y++) { - filter_new_scanline(); - for (int x = 0; x < destW; x++) { - destPixels[destOffset + x] = - blend_add_pin(destPixels[destOffset + x], filter_bilinear()); - sX += dx; - } - destOffset += screenW; - srcYOffset += dy; - } - break; - - case SUBTRACT: - for (int y = 0; y < destH; y++) { - filter_new_scanline(); - for (int x = 0; x < destW; x++) { - destPixels[destOffset + x] = - blend_sub_pin(destPixels[destOffset + x], filter_bilinear()); - sX += dx; - } - destOffset += screenW; - srcYOffset += dy; - } - break; - - case LIGHTEST: - for (int y = 0; y < destH; y++) { - filter_new_scanline(); - for (int x = 0; x < destW; x++) { - destPixels[destOffset + x] = - blend_lightest(destPixels[destOffset + x], filter_bilinear()); - sX += dx; - } - destOffset += screenW; - srcYOffset += dy; - } - break; - - case DARKEST: - for (int y = 0; y < destH; y++) { - filter_new_scanline(); - for (int x = 0; x < destW; x++) { - destPixels[destOffset + x] = - blend_darkest(destPixels[destOffset + x], filter_bilinear()); - sX += dx; - } - destOffset += screenW; - srcYOffset += dy; - } - break; - - case REPLACE: - for (int y = 0; y < destH; y++) { - filter_new_scanline(); - for (int x = 0; x < destW; x++) { - destPixels[destOffset + x] = filter_bilinear(); - sX += dx; - } - destOffset += screenW; - srcYOffset += dy; - } - break; - } - - } else { - // nearest neighbour scaling (++fast!) - switch (mode) { - - case BLEND: - for (int y = 0; y < destH; y++) { - sX = srcXOffset; - sY = (srcYOffset >> PRECISIONB) * img.width; - for (int x = 0; x < destW; x++) { - destPixels[destOffset + x] = - blend_multiply(destPixels[destOffset + x], - srcBuffer[sY + (sX >> PRECISIONB)]); - sX += dx; - } - destOffset += screenW; - srcYOffset += dy; - } - break; - - case ADD: - for (int y = 0; y < destH; y++) { - sX = srcXOffset; - sY = (srcYOffset >> PRECISIONB) * img.width; - for (int x = 0; x < destW; x++) { - destPixels[destOffset + x] = - blend_add_pin(destPixels[destOffset + x], - srcBuffer[sY + (sX >> PRECISIONB)]); - sX += dx; - } - destOffset += screenW; - srcYOffset += dy; - } - break; - - case SUBTRACT: - for (int y = 0; y < destH; y++) { - sX = srcXOffset; - sY = (srcYOffset >> PRECISIONB) * img.width; - for (int x = 0; x < destW; x++) { - destPixels[destOffset + x] = - blend_sub_pin(destPixels[destOffset + x], - srcBuffer[sY + (sX >> PRECISIONB)]); - sX += dx; - } - destOffset += screenW; - srcYOffset += dy; - } - break; - - case LIGHTEST: - for (int y = 0; y < destH; y++) { - sX = srcXOffset; - sY = (srcYOffset >> PRECISIONB) * img.width; - for (int x = 0; x < destW; x++) { - destPixels[destOffset + x] = - blend_lightest(destPixels[destOffset + x], - srcBuffer[sY + (sX >> PRECISIONB)]); - sX += dx; - } - destOffset += screenW; - srcYOffset += dy; - } - break; - - case DARKEST: - for (int y = 0; y < destH; y++) { - sX = srcXOffset; - sY = (srcYOffset >> PRECISIONB) * img.width; - for (int x = 0; x < destW; x++) { - destPixels[destOffset + x] = - blend_darkest(destPixels[destOffset + x], - srcBuffer[sY + (sX >> PRECISIONB)]); - sX += dx; - } - destOffset += screenW; - srcYOffset += dy; - } - break; - - case REPLACE: - for (int y = 0; y < destH; y++) { - sX = srcXOffset; - sY = (srcYOffset >> PRECISIONB) * img.width; - for (int x = 0; x < destW; x++) { - destPixels[destOffset + x] = srcBuffer[sY + (sX >> PRECISIONB)]; - sX += dx; - } - destOffset += screenW; - srcYOffset += dy; - } - break; - } - } - } - - - private void filter_new_scanline() { - sX = srcXOffset; - fracV = srcYOffset & PREC_MAXVAL; - ifV = PREC_MAXVAL - fracV; - v1 = (srcYOffset >> PRECISIONB) * iw; - v2 = low((srcYOffset >> PRECISIONB) + 1, ih1) * iw; - } - - - private int filter_bilinear() { - fracU = sX & PREC_MAXVAL; - ifU = PREC_MAXVAL - fracU; - ul = (ifU * ifV) >> PRECISIONB; - ll = (ifU * fracV) >> PRECISIONB; - ur = (fracU * ifV) >> PRECISIONB; - lr = (fracU * fracV) >> PRECISIONB; - u1 = (sX >> PRECISIONB); - u2 = low(u1 + 1, iw1); - - // get color values of the 4 neighbouring texels - cUL = srcBuffer[v1 + u1]; - cUR = srcBuffer[v1 + u2]; - cLL = srcBuffer[v2 + u1]; - cLR = srcBuffer[v2 + u2]; - - r = ((ul*((cUL&RED_MASK)>>16) + ll*((cLL&RED_MASK)>>16) + - ur*((cUR&RED_MASK)>>16) + lr*((cLR&RED_MASK)>>16)) - << PREC_RED_SHIFT) & RED_MASK; - - g = ((ul*(cUL&GREEN_MASK) + ll*(cLL&GREEN_MASK) + - ur*(cUR&GREEN_MASK) + lr*(cLR&GREEN_MASK)) - >>> PRECISIONB) & GREEN_MASK; - - b = (ul*(cUL&BLUE_MASK) + ll*(cLL&BLUE_MASK) + - ur*(cUR&BLUE_MASK) + lr*(cLR&BLUE_MASK)) - >>> PRECISIONB; - - a = ((ul*((cUL&ALPHA_MASK)>>>24) + ll*((cLL&ALPHA_MASK)>>>24) + - ur*((cUR&ALPHA_MASK)>>>24) + lr*((cLR&ALPHA_MASK)>>>24)) - << PREC_ALPHA_SHIFT) & ALPHA_MASK; - - return a | r | g | b; - } - - - - ////////////////////////////////////////////////////////////// - - // internal blending methods - - - private static int low(int a, int b) { - return (a < b) ? a : b; - } - - - private static int high(int a, int b) { - return (a > b) ? a : b; - } - - - /** - * returns the fractional portion of a number: frac(2.3) = .3; - */ - /* - private static float frac(float x) { - return (x - (int) x); - } - */ - - - /** - * generic linear interpolation - */ - private static int mix(int a, int b, int f) { - return a + (((b - a) * f) >> 8); - } - - - - ///////////////////////////////////////////////////////////// - - // BLEND MODE IMPLEMENTIONS - - private static int blend_multiply(int a, int b) { - int f = (b & ALPHA_MASK) >>> 24; - - return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 | - mix(a & RED_MASK, b & RED_MASK, f) & RED_MASK | - mix(a & GREEN_MASK, b & GREEN_MASK, f) & GREEN_MASK | - mix(a & BLUE_MASK, b & BLUE_MASK, f)); - } - - - /** - * additive blend with clipping - */ - private static int blend_add_pin(int a, int b) { - int f = (b & ALPHA_MASK) >>> 24; - - return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 | - low(((a & RED_MASK) + - ((b & RED_MASK) >> 8) * f), RED_MASK) & RED_MASK | - low(((a & GREEN_MASK) + - ((b & GREEN_MASK) >> 8) * f), GREEN_MASK) & GREEN_MASK | - low((a & BLUE_MASK) + - (((b & BLUE_MASK) * f) >> 8), BLUE_MASK)); - } - - - /** - * subtractive blend with clipping - */ - private static int blend_sub_pin(int a, int b) { - int f = (b & ALPHA_MASK) >>> 24; - - return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 | - high(((a & RED_MASK) - ((b & RED_MASK) >> 8) * f), - GREEN_MASK) & RED_MASK | - high(((a & GREEN_MASK) - ((b & GREEN_MASK) >> 8) * f), - BLUE_MASK) & GREEN_MASK | - high((a & BLUE_MASK) - (((b & BLUE_MASK) * f) >> 8), 0)); - } - - - /** - * only returns the blended lightest colour - */ - private static int blend_lightest(int a, int b) { - int f = (b & ALPHA_MASK) >>> 24; - - return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 | - high(a & RED_MASK, ((b & RED_MASK) >> 8) * f) & RED_MASK | - high(a & GREEN_MASK, ((b & GREEN_MASK) >> 8) * f) & GREEN_MASK | - high(a & BLUE_MASK, ((b & BLUE_MASK) * f) >> 8)); - } - - - /** - * only returns the blended darkest colour - */ - private static int blend_darkest(int a, int b) { - int f = (b & ALPHA_MASK) >>> 24; - - return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 | - mix(a & RED_MASK, - low(a & RED_MASK, - ((b & RED_MASK) >> 8) * f), f) & RED_MASK | - mix(a & GREEN_MASK, - low(a & GREEN_MASK, - ((b & GREEN_MASK) >> 8) * f), f) & GREEN_MASK | - mix(a & BLUE_MASK, - low(a & BLUE_MASK, - ((b & BLUE_MASK) * f) >> 8), f)); - } - - - - ////////////////////////////////////////////////////////////// - - // FILE I/O - - - static byte TIFF_HEADER[] = { - 77, 77, 0, 42, 0, 0, 0, 8, 0, 9, 0, -2, 0, 4, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 3, 0, 0, 0, 1, - 0, 0, 0, 0, 1, 2, 0, 3, 0, 0, 0, 3, 0, 0, 0, 122, 1, 6, 0, 3, 0, - 0, 0, 1, 0, 2, 0, 0, 1, 17, 0, 4, 0, 0, 0, 1, 0, 0, 3, 0, 1, 21, - 0, 3, 0, 0, 0, 1, 0, 3, 0, 0, 1, 22, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, - 1, 23, 0, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 8, 0, 8 - }; - - - /* - protected boolean saveHeaderTIFF(OutputStream output) { - try { - byte tiff[] = new byte[768]; - System.arraycopy(tiff_header, 0, tiff, 0, tiff_header.length); - - tiff[30] = (byte) ((width >> 8) & 0xff); - tiff[31] = (byte) ((width) & 0xff); - tiff[42] = tiff[102] = (byte) ((height >> 8) & 0xff); - tiff[43] = tiff[103] = (byte) ((height) & 0xff); - - int count = width*height*3; - tiff[114] = (byte) ((count >> 24) & 0xff); - tiff[115] = (byte) ((count >> 16) & 0xff); - tiff[116] = (byte) ((count >> 8) & 0xff); - tiff[117] = (byte) ((count) & 0xff); - - output.write(tiff); - return true; - - } catch (IOException e) { - e.printStackTrace(); - } - return false; - } - */ - - - static final String TIFF_ERROR = - "Error: Processing can only read its own TIFF files."; - - static protected PImage loadTIFF(byte tiff[]) { - if ((tiff[42] != tiff[102]) || // width/height in both places - (tiff[43] != tiff[103])) { - System.err.println(TIFF_ERROR); - return null; - } - - int width = - ((tiff[30] & 0xff) << 8) | (tiff[31] & 0xff); - int height = - ((tiff[42] & 0xff) << 8) | (tiff[43] & 0xff); - - int count = - ((tiff[114] & 0xff) << 24) | - ((tiff[115] & 0xff) << 16) | - ((tiff[116] & 0xff) << 8) | - (tiff[117] & 0xff); - if (count != width * height * 3) { - System.err.println(TIFF_ERROR + " (" + width + ", " + height +")"); - return null; - } - - // check the rest of the header - for (int i = 0; i < TIFF_HEADER.length; i++) { - if ((i == 30) || (i == 31) || (i == 42) || (i == 43) || - (i == 102) || (i == 103) || - (i == 114) || (i == 115) || (i == 116) || (i == 117)) continue; - - if (tiff[i] != TIFF_HEADER[i]) { - System.err.println(TIFF_ERROR + " (" + i + ")"); - return null; - } - } - - PImage outgoing = new PImage(width, height, RGB); - int index = 768; - count /= 3; - for (int i = 0; i < count; i++) { - outgoing.pixels[i] = - 0xFF000000 | - (tiff[index++] & 0xff) << 16 | - (tiff[index++] & 0xff) << 8 | - (tiff[index++] & 0xff); - } - return outgoing; - } - - - protected boolean saveTIFF(OutputStream output) { - if (format != RGB) { - System.out.println("Warning: only RGB information is saved with " + - ".tif files. Use .tga or .png if you want alpha."); - } - try { - byte tiff[] = new byte[768]; - System.arraycopy(TIFF_HEADER, 0, tiff, 0, TIFF_HEADER.length); - - tiff[30] = (byte) ((width >> 8) & 0xff); - tiff[31] = (byte) ((width) & 0xff); - tiff[42] = tiff[102] = (byte) ((height >> 8) & 0xff); - tiff[43] = tiff[103] = (byte) ((height) & 0xff); - - int count = width*height*3; - tiff[114] = (byte) ((count >> 24) & 0xff); - tiff[115] = (byte) ((count >> 16) & 0xff); - tiff[116] = (byte) ((count >> 8) & 0xff); - tiff[117] = (byte) ((count) & 0xff); - - //if (!saveHeaderTIFF(output)) { //, width, height)) { - //return false; - //} - for (int i = 0; i < pixels.length; i++) { - output.write((pixels[i] >> 16) & 0xff); - output.write((pixels[i] >> 8) & 0xff); - output.write(pixels[i] & 0xff); - } - output.flush(); - return true; - - } catch (IOException e) { - e.printStackTrace(); - } - return false; - } - - - /** - * Creates a Targa32 formatted byte sequence of specified - * pixel buffer now using RLE compression. - *

- * Also figured out how to avoid parsing the image upside-down - * (there's a header flag to set the image origin to top-left) - *

- * Starting with revision 0092, the format setting is taken into account: - *
    - *
  • ALPHA images written as 8bit grayscale (uses lowest byte) - *
  • RGB → 24 bits - *
  • ARGB → 32 bits - *
- * all versions are RLE compressed - *

- * Contributed by toxi 8-10 May 2005, based on this RLE - * specification - */ - protected boolean saveTGA(OutputStream output) { - byte header[] = new byte[18]; - - if (format == ALPHA) { // save ALPHA images as 8bit grayscale - header[2] = 0x0B; - header[16] = 0x08; - header[17] = 0x28; - - } else if (format == RGB) { - header[2] = 0x0A; - header[16] = 24; - header[17] = 0x20; - - } else if (format == ARGB) { - header[2] = 0x0A; - header[16] = 32; - header[17] = 0x28; - - } else { - throw new RuntimeException("Image format not recognized inside save()"); - } - // set image dimensions lo-hi byte order - header[12] = (byte) (width & 0xff); - header[13] = (byte) (width >> 8); - header[14] = (byte) (height & 0xff); - header[15] = (byte) (height >> 8); - - try { - output.write(header); - - int maxLen = height * width; - int index = 0; - int col; //, prevCol; - int[] currChunk = new int[128]; - - // 8bit image exporter is in separate loop - // to avoid excessive conditionals... - if (format == ALPHA) { - while (index < maxLen) { - boolean isRLE = false; - int rle = 1; - currChunk[0] = col = pixels[index] & 0xff; - while (index + rle < maxLen) { - if (col != (pixels[index + rle]&0xff) || rle == 128) { - isRLE = (rle > 1); - break; - } - rle++; - } - if (isRLE) { - output.write(0x80 | (rle - 1)); - output.write(col); - - } else { - rle = 1; - while (index + rle < maxLen) { - int cscan = pixels[index + rle] & 0xff; - if ((col != cscan && rle < 128) || rle < 3) { - currChunk[rle] = col = cscan; - } else { - if (col == cscan) rle -= 2; - break; - } - rle++; - } - output.write(rle - 1); - for (int i = 0; i < rle; i++) output.write(currChunk[i]); - } - index += rle; - } - } else { // export 24/32 bit TARGA - while (index < maxLen) { - boolean isRLE = false; - currChunk[0] = col = pixels[index]; - int rle = 1; - // try to find repeating bytes (min. len = 2 pixels) - // maximum chunk size is 128 pixels - while (index + rle < maxLen) { - if (col != pixels[index + rle] || rle == 128) { - isRLE = (rle > 1); // set flag for RLE chunk - break; - } - rle++; - } - if (isRLE) { - output.write(128 | (rle - 1)); - output.write(col & 0xff); - output.write(col >> 8 & 0xff); - output.write(col >> 16 & 0xff); - if (format == ARGB) output.write(col >>> 24 & 0xff); - - } else { // not RLE - rle = 1; - while (index + rle < maxLen) { - if ((col != pixels[index + rle] && rle < 128) || rle < 3) { - currChunk[rle] = col = pixels[index + rle]; - } else { - // check if the exit condition was the start of - // a repeating colour - if (col == pixels[index + rle]) rle -= 2; - break; - } - rle++; - } - // write uncompressed chunk - output.write(rle - 1); - if (format == ARGB) { - for (int i = 0; i < rle; i++) { - col = currChunk[i]; - output.write(col & 0xff); - output.write(col >> 8 & 0xff); - output.write(col >> 16 & 0xff); - output.write(col >>> 24 & 0xff); - } - } else { - for (int i = 0; i < rle; i++) { - col = currChunk[i]; - output.write(col & 0xff); - output.write(col >> 8 & 0xff); - output.write(col >> 16 & 0xff); - } - } - } - index += rle; - } - } - output.flush(); - return true; - - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } - - - /** - * Use ImageIO functions from Java 1.4 and later to handle image save. - * Various formats are supported, typically jpeg, png, bmp, and wbmp. - * To get a list of the supported formats for writing, use:
- * println(javax.imageio.ImageIO.getReaderFormatNames()) - */ - protected void saveImageIO(String path) throws IOException { - try { - //BufferedImage bimage = - // new BufferedImage(width, height, (format == ARGB) ? - // BufferedImage.TYPE_INT_ARGB : - // BufferedImage.TYPE_INT_RGB); - 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); - Field typeIntArgbField = bufferedImageClass.getField("TYPE_INT_ARGB"); - int typeIntArgb = typeIntArgbField.getInt(typeIntArgbField); - Object bimage = - bufferedImageConstructor.newInstance(new Object[] { - new Integer(width), - new Integer(height), - new Integer((format == ARGB) ? typeIntArgb : typeIntRgb) - }); - - //bimage.setRGB(0, 0, width, height, pixels, 0, width); - Method setRgbMethod = - bufferedImageClass.getMethod("setRGB", new Class[] { - Integer.TYPE, Integer.TYPE, - Integer.TYPE, Integer.TYPE, - pixels.getClass(), - Integer.TYPE, Integer.TYPE - }); - setRgbMethod.invoke(bimage, new Object[] { - new Integer(0), new Integer(0), - new Integer(width), new Integer(height), - pixels, new Integer(0), new Integer(width) - }); - - File file = new File(path); - String extension = path.substring(path.lastIndexOf('.') + 1); - - //ImageIO.write(bimage, extension, file); - Class renderedImageClass = - Class.forName("java.awt.image.RenderedImage"); - Class ioClass = Class.forName("javax.imageio.ImageIO"); - Method writeMethod = - ioClass.getMethod("write", new Class[] { - renderedImageClass, String.class, File.class - }); - writeMethod.invoke(null, new Object[] { bimage, extension, file }); - - } catch (Exception e) { - e.printStackTrace(); - throw new IOException("image save failed."); - } - } - - - protected String[] saveImageFormats; - - /** - * Save this image to disk. - *

- * 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 + * For subclasses where the pixels[] buffer isn't set by default, + * this should copy all data into the pixels[] array + */ + public void beginPixels() { // ignore + } + + + /** + * Call this when finished messing with the pixels[] array. + * Formerly called updatePixels(). + *

+ * Mark all pixels as needing update. + */ + public void endPixels() { + endPixels(0, 0, width, height); + } + + + /** + * Mark the pixels in this region as needing an update. + *

+ * 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. + *

+ *

    + *
  • filter(BLUR) provides a basic blur. + *
  • filter(GRAY) converts the image to grayscale based on luminance. + *
  • filter(INVERT) will invert the color components in the image. + *
  • filter(OPAQUE) set all the high bits in the image to opaque + *
  • filter(THRESHOLD) converts the image to black and white. + *
  • filter(DILATE) grow white/light areas + *
  • filter(ERODE) shrink white/light areas + *
+ * 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. + *

+ *

    + *
  • filter(BLUR, int radius) performs a gaussian blur of the + * specified radius. + *
  • filter(POSTERIZE, int levels) will posterize the image to + * between 2 and 255 levels. + *
  • filter(THRESHOLD, float center) allows you to set the + * center point for the threshold. It takes a value from 0 to 1.0. + *
+ * 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=maxRowIdx) + idxRight=currIdx; + if (idxUp<0) + idxUp=0; + if (idxDown>=maxIdx) + idxDown=currIdx; + + int colUp=pixels[idxUp]; + int colLeft=pixels[idxLeft]; + int colDown=pixels[idxDown]; + int colRight=pixels[idxRight]; + + // compute luminance + int currLum = + 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff); + int lumLeft = + 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff); + int lumRight = + 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff); + int lumUp = + 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff); + int lumDown = + 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff); + + if (lumLeft>currLum) { + colOut=colLeft; + currLum=lumLeft; + } + if (lumRight>currLum) { + colOut=colRight; + currLum=lumRight; + } + if (lumUp>currLum) { + colOut=colUp; + currLum=lumUp; + } + if (lumDown>currLum) { + colOut=colDown; + currLum=lumDown; + } + out[currIdx++]=colOut; + } + } + } else { + // dilate (grow dark areas) + while (currIdx=maxRowIdx) + idxRight=currIdx; + if (idxUp<0) + idxUp=0; + if (idxDown>=maxIdx) + idxDown=currIdx; + + int colUp=pixels[idxUp]; + int colLeft=pixels[idxLeft]; + int colDown=pixels[idxDown]; + int colRight=pixels[idxRight]; + + // compute luminance + int currLum = + 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff); + int lumLeft = + 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff); + int lumRight = + 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff); + int lumUp = + 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff); + int lumDown = + 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff); + + if (lumLeft + * BLEND - linear interpolation of colours: C = A*factor + B + * ADD - additive blending with white clip: C = min(A*factor + B, 255) + * SUBTRACT - substractive blend with black clip: C = max(B - A*factor, 0) + * DARKEST - only the darkest colour succeeds: C = min(A*factor, B) + * LIGHTEST - only the lightest colour succeeds: C = max(A*factor, B) + * REPLACE - destination colour equals colour of source pixel: C = A + * + */ + static public int blend(int c1, int c2, int mode) { + switch (mode) { + case BLEND: return blend_multiply(c1, c2); + case ADD: return blend_add_pin(c1, c2); + case SUBTRACT: return blend_sub_pin(c1, c2); + case LIGHTEST: return blend_lightest(c1, c2); + case DARKEST: return blend_darkest(c1, c2); + case REPLACE: return c2; + } + return 0; + } + + + /** + * Copies and blends 1 pixel with MODE to pixel in this image. + */ + public void blend(int sx, int sy, int dx, int dy, int mode) { + if ((dx >= 0) && (dx < width) && (sx >= 0) && (sx < width) && + (dy >= 0) && (dy < height) && (sy >= 0) && (sy < height)) { + pixels[dy * width + dx] = + blend(pixels[dy * width + dx], pixels[sy * width + sx], mode); + } + } + + + /** + * Copies and blends 1 pixel with MODE to pixel in another image + */ + public void blend(PImage src, + int sx, int sy, int dx, int dy, int mode) { + if ((dx >= 0) && (dx < width) && (sx >= 0) && (sx < src.width) && + (dy >= 0) && (dy < height) && (sy >= 0) && (sy < src.height)) { + pixels[dy * width + dx] = + blend(pixels[dy * width + dx], + src.pixels[sy * src.width + sx], mode); + } + } + + + /** + * Blends one area of this image to another area + */ + public void blend(int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2, int mode) { + blend(this, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); + } + + + /** + * Copies area of one image into another PImage object + */ + public void blend(PImage src, + int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2, int mode) { + if (imageMode == CORNER) { // if CORNERS, do nothing + sx2 += sx1; sy2 += sy1; + dx2 += dx1; dy2 += dy1; + + //} else if (imageMode == CENTER) { + //sx2 /= 2f; sy2 /= 2f; + //dx2 /= 2f; dy2 /= 2f; + } + + if ((src == this) && + intersect(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)) { + blit_resize(get(sx1, sy1, sx2 - sx1, sy2 - sy1), + 0, 0, sx2 - sx1 - 1, sy2 - sy1 - 1, + pixels, width, height, dx1, dy1, dx2, dy2, mode); + } else { + blit_resize(src, sx1, sy1, sx2, sy2, + pixels, width, height, dx1, dy1, dx2, dy2, mode); + } + } + + + /** + * Check to see if two rectangles intersect one another + */ + protected boolean intersect(int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2) { + int sw = sx2 - sx1 + 1; + int sh = sy2 - sy1 + 1; + int dw = dx2 - dx1 + 1; + int dh = dy2 - dy1 + 1; + + if (dx1 < sx1) { + dw += dx1 - sx1; + if (dw > sw) { + dw = sw; + } + } else { + int w = sw + sx1 - dx1; + if (dw > w) { + dw = w; + } + } + if (dy1 < sy1) { + dh += dy1 - sy1; + if (dh > sh) { + dh = sh; + } + } else { + int h = sh + sy1 - dy1; + if (dh > h) { + dh = h; + } + } + return !(dw <= 0 || dh <= 0); + } + + + + ////////////////////////////////////////////////////////////// + + // COPYING IMAGE DATA + + + /** + * Duplicate an image, returns new PImage object. + * The pixels[] array for the new object will be unique + * and recopied from the source image. + */ + public Object clone() throws CloneNotSupportedException { // ignore + PImage c = (PImage) super.clone(); + + // super.clone() will only copy the reference to the pixels + // array, so this will do a proper duplication of it instead. + c.pixels = new int[width * height]; + System.arraycopy(pixels, 0, c.pixels, 0, pixels.length); + + // return the goods + return c; + } + + + + ////////////////////////////////////////////////////////////// + + /** + * Internal blitter/resizer/copier from toxi. + * Uses bilinear filtering if smooth() has been enabled + * 'mode' determines the blending mode used in the process. + */ + private void blit_resize(PImage img, + int srcX1, int srcY1, int srcX2, int srcY2, + int[] destPixels, int screenW, int screenH, + int destX1, int destY1, int destX2, int destY2, + int mode) { + if (srcX1 < 0) srcX1 = 0; + if (srcY1 < 0) srcY1 = 0; + if (srcX2 >= img.width) srcX2 = img.width - 1; + if (srcY2 >= img.height) srcY2 = img.height - 1; + + int srcW = srcX2 - srcX1; + int srcH = srcY2 - srcY1; + int destW = destX2 - destX1; + int destH = destY2 - destY1; + + if (!smooth) { + srcW++; srcH++; + } + + if (destW <= 0 || destH <= 0 || + srcW <= 0 || srcH <= 0 || + destX1 >= screenW || destY1 >= screenH || + srcX1 >= img.width || srcY1 >= img.height) { + return; + } + + int dx = (int) (srcW / (float) destW * PRECISIONF); + int dy = (int) (srcH / (float) destH * PRECISIONF); + + srcXOffset = (int) (destX1 < 0 ? -destX1 * dx : srcX1 * PRECISIONF); + srcYOffset = (int) (destY1 < 0 ? -destY1 * dy : srcY1 * PRECISIONF); + + if (destX1 < 0) { + destW += destX1; + destX1 = 0; + } + if (destY1 < 0) { + destH += destY1; + destY1 = 0; + } + + destW = low(destW, screenW - destX1); + destH = low(destH, screenH - destY1); + + int destOffset = destY1 * screenW + destX1; + srcBuffer = img.pixels; + + if (smooth) { + // use bilinear filtering + iw = img.width; + iw1 = img.width - 1; + ih1 = img.height - 1; + + switch (mode) { + + case BLEND: + for (int y = 0; y < destH; y++) { + filter_new_scanline(); + for (int x = 0; x < destW; x++) { + destPixels[destOffset + x] = + blend_multiply(destPixels[destOffset + x], filter_bilinear()); + sX += dx; + } + destOffset += screenW; + srcYOffset += dy; + } + break; + + case ADD: + for (int y = 0; y < destH; y++) { + filter_new_scanline(); + for (int x = 0; x < destW; x++) { + destPixels[destOffset + x] = + blend_add_pin(destPixels[destOffset + x], filter_bilinear()); + sX += dx; + } + destOffset += screenW; + srcYOffset += dy; + } + break; + + case SUBTRACT: + for (int y = 0; y < destH; y++) { + filter_new_scanline(); + for (int x = 0; x < destW; x++) { + destPixels[destOffset + x] = + blend_sub_pin(destPixels[destOffset + x], filter_bilinear()); + sX += dx; + } + destOffset += screenW; + srcYOffset += dy; + } + break; + + case LIGHTEST: + for (int y = 0; y < destH; y++) { + filter_new_scanline(); + for (int x = 0; x < destW; x++) { + destPixels[destOffset + x] = + blend_lightest(destPixels[destOffset + x], filter_bilinear()); + sX += dx; + } + destOffset += screenW; + srcYOffset += dy; + } + break; + + case DARKEST: + for (int y = 0; y < destH; y++) { + filter_new_scanline(); + for (int x = 0; x < destW; x++) { + destPixels[destOffset + x] = + blend_darkest(destPixels[destOffset + x], filter_bilinear()); + sX += dx; + } + destOffset += screenW; + srcYOffset += dy; + } + break; + + case REPLACE: + for (int y = 0; y < destH; y++) { + filter_new_scanline(); + for (int x = 0; x < destW; x++) { + destPixels[destOffset + x] = filter_bilinear(); + sX += dx; + } + destOffset += screenW; + srcYOffset += dy; + } + break; + } + + } else { + // nearest neighbour scaling (++fast!) + switch (mode) { + + case BLEND: + for (int y = 0; y < destH; y++) { + sX = srcXOffset; + sY = (srcYOffset >> PRECISIONB) * img.width; + for (int x = 0; x < destW; x++) { + destPixels[destOffset + x] = + blend_multiply(destPixels[destOffset + x], + srcBuffer[sY + (sX >> PRECISIONB)]); + sX += dx; + } + destOffset += screenW; + srcYOffset += dy; + } + break; + + case ADD: + for (int y = 0; y < destH; y++) { + sX = srcXOffset; + sY = (srcYOffset >> PRECISIONB) * img.width; + for (int x = 0; x < destW; x++) { + destPixels[destOffset + x] = + blend_add_pin(destPixels[destOffset + x], + srcBuffer[sY + (sX >> PRECISIONB)]); + sX += dx; + } + destOffset += screenW; + srcYOffset += dy; + } + break; + + case SUBTRACT: + for (int y = 0; y < destH; y++) { + sX = srcXOffset; + sY = (srcYOffset >> PRECISIONB) * img.width; + for (int x = 0; x < destW; x++) { + destPixels[destOffset + x] = + blend_sub_pin(destPixels[destOffset + x], + srcBuffer[sY + (sX >> PRECISIONB)]); + sX += dx; + } + destOffset += screenW; + srcYOffset += dy; + } + break; + + case LIGHTEST: + for (int y = 0; y < destH; y++) { + sX = srcXOffset; + sY = (srcYOffset >> PRECISIONB) * img.width; + for (int x = 0; x < destW; x++) { + destPixels[destOffset + x] = + blend_lightest(destPixels[destOffset + x], + srcBuffer[sY + (sX >> PRECISIONB)]); + sX += dx; + } + destOffset += screenW; + srcYOffset += dy; + } + break; + + case DARKEST: + for (int y = 0; y < destH; y++) { + sX = srcXOffset; + sY = (srcYOffset >> PRECISIONB) * img.width; + for (int x = 0; x < destW; x++) { + destPixels[destOffset + x] = + blend_darkest(destPixels[destOffset + x], + srcBuffer[sY + (sX >> PRECISIONB)]); + sX += dx; + } + destOffset += screenW; + srcYOffset += dy; + } + break; + + case REPLACE: + for (int y = 0; y < destH; y++) { + sX = srcXOffset; + sY = (srcYOffset >> PRECISIONB) * img.width; + for (int x = 0; x < destW; x++) { + destPixels[destOffset + x] = srcBuffer[sY + (sX >> PRECISIONB)]; + sX += dx; + } + destOffset += screenW; + srcYOffset += dy; + } + break; + } + } + } + + + private void filter_new_scanline() { + sX = srcXOffset; + fracV = srcYOffset & PREC_MAXVAL; + ifV = PREC_MAXVAL - fracV; + v1 = (srcYOffset >> PRECISIONB) * iw; + v2 = low((srcYOffset >> PRECISIONB) + 1, ih1) * iw; + } + + + private int filter_bilinear() { + fracU = sX & PREC_MAXVAL; + ifU = PREC_MAXVAL - fracU; + ul = (ifU * ifV) >> PRECISIONB; + ll = (ifU * fracV) >> PRECISIONB; + ur = (fracU * ifV) >> PRECISIONB; + lr = (fracU * fracV) >> PRECISIONB; + u1 = (sX >> PRECISIONB); + u2 = low(u1 + 1, iw1); + + // get color values of the 4 neighbouring texels + cUL = srcBuffer[v1 + u1]; + cUR = srcBuffer[v1 + u2]; + cLL = srcBuffer[v2 + u1]; + cLR = srcBuffer[v2 + u2]; + + r = ((ul*((cUL&RED_MASK)>>16) + ll*((cLL&RED_MASK)>>16) + + ur*((cUR&RED_MASK)>>16) + lr*((cLR&RED_MASK)>>16)) + << PREC_RED_SHIFT) & RED_MASK; + + g = ((ul*(cUL&GREEN_MASK) + ll*(cLL&GREEN_MASK) + + ur*(cUR&GREEN_MASK) + lr*(cLR&GREEN_MASK)) + >>> PRECISIONB) & GREEN_MASK; + + b = (ul*(cUL&BLUE_MASK) + ll*(cLL&BLUE_MASK) + + ur*(cUR&BLUE_MASK) + lr*(cLR&BLUE_MASK)) + >>> PRECISIONB; + + a = ((ul*((cUL&ALPHA_MASK)>>>24) + ll*((cLL&ALPHA_MASK)>>>24) + + ur*((cUR&ALPHA_MASK)>>>24) + lr*((cLR&ALPHA_MASK)>>>24)) + << PREC_ALPHA_SHIFT) & ALPHA_MASK; + + return a | r | g | b; + } + + + + ////////////////////////////////////////////////////////////// + + // internal blending methods + + + private static int low(int a, int b) { + return (a < b) ? a : b; + } + + + private static int high(int a, int b) { + return (a > b) ? a : b; + } + + + /** + * returns the fractional portion of a number: frac(2.3) = .3; + */ + /* + private static float frac(float x) { + return (x - (int) x); + } + */ + + + /** + * generic linear interpolation + */ + private static int mix(int a, int b, int f) { + return a + (((b - a) * f) >> 8); + } + + + + ///////////////////////////////////////////////////////////// + + // BLEND MODE IMPLEMENTIONS + + private static int blend_multiply(int a, int b) { + int f = (b & ALPHA_MASK) >>> 24; + + return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 | + mix(a & RED_MASK, b & RED_MASK, f) & RED_MASK | + mix(a & GREEN_MASK, b & GREEN_MASK, f) & GREEN_MASK | + mix(a & BLUE_MASK, b & BLUE_MASK, f)); + } + + + /** + * additive blend with clipping + */ + private static int blend_add_pin(int a, int b) { + int f = (b & ALPHA_MASK) >>> 24; + + return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 | + low(((a & RED_MASK) + + ((b & RED_MASK) >> 8) * f), RED_MASK) & RED_MASK | + low(((a & GREEN_MASK) + + ((b & GREEN_MASK) >> 8) * f), GREEN_MASK) & GREEN_MASK | + low((a & BLUE_MASK) + + (((b & BLUE_MASK) * f) >> 8), BLUE_MASK)); + } + + + /** + * subtractive blend with clipping + */ + private static int blend_sub_pin(int a, int b) { + int f = (b & ALPHA_MASK) >>> 24; + + return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 | + high(((a & RED_MASK) - ((b & RED_MASK) >> 8) * f), + GREEN_MASK) & RED_MASK | + high(((a & GREEN_MASK) - ((b & GREEN_MASK) >> 8) * f), + BLUE_MASK) & GREEN_MASK | + high((a & BLUE_MASK) - (((b & BLUE_MASK) * f) >> 8), 0)); + } + + + /** + * only returns the blended lightest colour + */ + private static int blend_lightest(int a, int b) { + int f = (b & ALPHA_MASK) >>> 24; + + return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 | + high(a & RED_MASK, ((b & RED_MASK) >> 8) * f) & RED_MASK | + high(a & GREEN_MASK, ((b & GREEN_MASK) >> 8) * f) & GREEN_MASK | + high(a & BLUE_MASK, ((b & BLUE_MASK) * f) >> 8)); + } + + + /** + * only returns the blended darkest colour + */ + private static int blend_darkest(int a, int b) { + int f = (b & ALPHA_MASK) >>> 24; + + return (low(((a & ALPHA_MASK) >>> 24) + f, 0xff) << 24 | + mix(a & RED_MASK, + low(a & RED_MASK, + ((b & RED_MASK) >> 8) * f), f) & RED_MASK | + mix(a & GREEN_MASK, + low(a & GREEN_MASK, + ((b & GREEN_MASK) >> 8) * f), f) & GREEN_MASK | + mix(a & BLUE_MASK, + low(a & BLUE_MASK, + ((b & BLUE_MASK) * f) >> 8), f)); + } + + + + ////////////////////////////////////////////////////////////// + + // FILE I/O + + + static byte TIFF_HEADER[] = { + 77, 77, 0, 42, 0, 0, 0, 8, 0, 9, 0, -2, 0, 4, 0, 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 3, 0, 0, 0, 1, + 0, 0, 0, 0, 1, 2, 0, 3, 0, 0, 0, 3, 0, 0, 0, 122, 1, 6, 0, 3, 0, + 0, 0, 1, 0, 2, 0, 0, 1, 17, 0, 4, 0, 0, 0, 1, 0, 0, 3, 0, 1, 21, + 0, 3, 0, 0, 0, 1, 0, 3, 0, 0, 1, 22, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, + 1, 23, 0, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 8, 0, 8 + }; + + + /* + protected boolean saveHeaderTIFF(OutputStream output) { + try { + byte tiff[] = new byte[768]; + System.arraycopy(tiff_header, 0, tiff, 0, tiff_header.length); + + tiff[30] = (byte) ((width >> 8) & 0xff); + tiff[31] = (byte) ((width) & 0xff); + tiff[42] = tiff[102] = (byte) ((height >> 8) & 0xff); + tiff[43] = tiff[103] = (byte) ((height) & 0xff); + + int count = width*height*3; + tiff[114] = (byte) ((count >> 24) & 0xff); + tiff[115] = (byte) ((count >> 16) & 0xff); + tiff[116] = (byte) ((count >> 8) & 0xff); + tiff[117] = (byte) ((count) & 0xff); + + output.write(tiff); + return true; + + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + */ + + + static final String TIFF_ERROR = + "Error: Processing can only read its own TIFF files."; + + static protected PImage loadTIFF(byte tiff[]) { + if ((tiff[42] != tiff[102]) || // width/height in both places + (tiff[43] != tiff[103])) { + System.err.println(TIFF_ERROR); + return null; + } + + int width = + ((tiff[30] & 0xff) << 8) | (tiff[31] & 0xff); + int height = + ((tiff[42] & 0xff) << 8) | (tiff[43] & 0xff); + + int count = + ((tiff[114] & 0xff) << 24) | + ((tiff[115] & 0xff) << 16) | + ((tiff[116] & 0xff) << 8) | + (tiff[117] & 0xff); + if (count != width * height * 3) { + System.err.println(TIFF_ERROR + " (" + width + ", " + height +")"); + return null; + } + + // check the rest of the header + for (int i = 0; i < TIFF_HEADER.length; i++) { + if ((i == 30) || (i == 31) || (i == 42) || (i == 43) || + (i == 102) || (i == 103) || + (i == 114) || (i == 115) || (i == 116) || (i == 117)) continue; + + if (tiff[i] != TIFF_HEADER[i]) { + System.err.println(TIFF_ERROR + " (" + i + ")"); + return null; + } + } + + PImage outgoing = new PImage(width, height, RGB); + int index = 768; + count /= 3; + for (int i = 0; i < count; i++) { + outgoing.pixels[i] = + 0xFF000000 | + (tiff[index++] & 0xff) << 16 | + (tiff[index++] & 0xff) << 8 | + (tiff[index++] & 0xff); + } + return outgoing; + } + + + protected boolean saveTIFF(OutputStream output) { + if (format != RGB) { + System.out.println("Warning: only RGB information is saved with " + + ".tif files. Use .tga or .png if you want alpha."); + } + try { + byte tiff[] = new byte[768]; + System.arraycopy(TIFF_HEADER, 0, tiff, 0, TIFF_HEADER.length); + + tiff[30] = (byte) ((width >> 8) & 0xff); + tiff[31] = (byte) ((width) & 0xff); + tiff[42] = tiff[102] = (byte) ((height >> 8) & 0xff); + tiff[43] = tiff[103] = (byte) ((height) & 0xff); + + int count = width*height*3; + tiff[114] = (byte) ((count >> 24) & 0xff); + tiff[115] = (byte) ((count >> 16) & 0xff); + tiff[116] = (byte) ((count >> 8) & 0xff); + tiff[117] = (byte) ((count) & 0xff); + + //if (!saveHeaderTIFF(output)) { //, width, height)) { + //return false; + //} + for (int i = 0; i < pixels.length; i++) { + output.write((pixels[i] >> 16) & 0xff); + output.write((pixels[i] >> 8) & 0xff); + output.write(pixels[i] & 0xff); + } + output.flush(); + return true; + + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + + + /** + * Creates a Targa32 formatted byte sequence of specified + * pixel buffer now using RLE compression. + *

+ * Also figured out how to avoid parsing the image upside-down + * (there's a header flag to set the image origin to top-left) + *

+ * Starting with revision 0092, the format setting is taken into account: + *
    + *
  • ALPHA images written as 8bit grayscale (uses lowest byte) + *
  • RGB → 24 bits + *
  • ARGB → 32 bits + *
+ * all versions are RLE compressed + *

+ * Contributed by toxi 8-10 May 2005, based on this RLE + * specification + */ + protected boolean saveTGA(OutputStream output) { + byte header[] = new byte[18]; + + if (format == ALPHA) { // save ALPHA images as 8bit grayscale + header[2] = 0x0B; + header[16] = 0x08; + header[17] = 0x28; + + } else if (format == RGB) { + header[2] = 0x0A; + header[16] = 24; + header[17] = 0x20; + + } else if (format == ARGB) { + header[2] = 0x0A; + header[16] = 32; + header[17] = 0x28; + + } else { + throw new RuntimeException("Image format not recognized inside save()"); + } + // set image dimensions lo-hi byte order + header[12] = (byte) (width & 0xff); + header[13] = (byte) (width >> 8); + header[14] = (byte) (height & 0xff); + header[15] = (byte) (height >> 8); + + try { + output.write(header); + + int maxLen = height * width; + int index = 0; + int col; //, prevCol; + int[] currChunk = new int[128]; + + // 8bit image exporter is in separate loop + // to avoid excessive conditionals... + if (format == ALPHA) { + while (index < maxLen) { + boolean isRLE = false; + int rle = 1; + currChunk[0] = col = pixels[index] & 0xff; + while (index + rle < maxLen) { + if (col != (pixels[index + rle]&0xff) || rle == 128) { + isRLE = (rle > 1); + break; + } + rle++; + } + if (isRLE) { + output.write(0x80 | (rle - 1)); + output.write(col); + + } else { + rle = 1; + while (index + rle < maxLen) { + int cscan = pixels[index + rle] & 0xff; + if ((col != cscan && rle < 128) || rle < 3) { + currChunk[rle] = col = cscan; + } else { + if (col == cscan) rle -= 2; + break; + } + rle++; + } + output.write(rle - 1); + for (int i = 0; i < rle; i++) output.write(currChunk[i]); + } + index += rle; + } + } else { // export 24/32 bit TARGA + while (index < maxLen) { + boolean isRLE = false; + currChunk[0] = col = pixels[index]; + int rle = 1; + // try to find repeating bytes (min. len = 2 pixels) + // maximum chunk size is 128 pixels + while (index + rle < maxLen) { + if (col != pixels[index + rle] || rle == 128) { + isRLE = (rle > 1); // set flag for RLE chunk + break; + } + rle++; + } + if (isRLE) { + output.write(128 | (rle - 1)); + output.write(col & 0xff); + output.write(col >> 8 & 0xff); + output.write(col >> 16 & 0xff); + if (format == ARGB) output.write(col >>> 24 & 0xff); + + } else { // not RLE + rle = 1; + while (index + rle < maxLen) { + if ((col != pixels[index + rle] && rle < 128) || rle < 3) { + currChunk[rle] = col = pixels[index + rle]; + } else { + // check if the exit condition was the start of + // a repeating colour + if (col == pixels[index + rle]) rle -= 2; + break; + } + rle++; + } + // write uncompressed chunk + output.write(rle - 1); + if (format == ARGB) { + for (int i = 0; i < rle; i++) { + col = currChunk[i]; + output.write(col & 0xff); + output.write(col >> 8 & 0xff); + output.write(col >> 16 & 0xff); + output.write(col >>> 24 & 0xff); + } + } else { + for (int i = 0; i < rle; i++) { + col = currChunk[i]; + output.write(col & 0xff); + output.write(col >> 8 & 0xff); + output.write(col >> 16 & 0xff); + } + } + } + index += rle; + } + } + output.flush(); + return true; + + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + + /** + * Use ImageIO functions from Java 1.4 and later to handle image save. + * Various formats are supported, typically jpeg, png, bmp, and wbmp. + * To get a list of the supported formats for writing, use:
+ * println(javax.imageio.ImageIO.getReaderFormatNames()) + */ + protected void saveImageIO(String path) throws IOException { + try { + //BufferedImage bimage = + // new BufferedImage(width, height, (format == ARGB) ? + // BufferedImage.TYPE_INT_ARGB : + // BufferedImage.TYPE_INT_RGB); + 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); + Field typeIntArgbField = bufferedImageClass.getField("TYPE_INT_ARGB"); + int typeIntArgb = typeIntArgbField.getInt(typeIntArgbField); + Object bimage = + bufferedImageConstructor.newInstance(new Object[] { + new Integer(width), + new Integer(height), + new Integer((format == ARGB) ? typeIntArgb : typeIntRgb) + }); + + //bimage.setRGB(0, 0, width, height, pixels, 0, width); + Method setRgbMethod = + bufferedImageClass.getMethod("setRGB", new Class[] { + Integer.TYPE, Integer.TYPE, + Integer.TYPE, Integer.TYPE, + pixels.getClass(), + Integer.TYPE, Integer.TYPE + }); + setRgbMethod.invoke(bimage, new Object[] { + new Integer(0), new Integer(0), + new Integer(width), new Integer(height), + pixels, new Integer(0), new Integer(width) + }); + + File file = new File(path); + String extension = path.substring(path.lastIndexOf('.') + 1); + + //ImageIO.write(bimage, extension, file); + Class renderedImageClass = + Class.forName("java.awt.image.RenderedImage"); + Class ioClass = Class.forName("javax.imageio.ImageIO"); + Method writeMethod = + ioClass.getMethod("write", new Class[] { + renderedImageClass, String.class, File.class + }); + writeMethod.invoke(null, new Object[] { bimage, extension, file }); + + } catch (Exception e) { + e.printStackTrace(); + throw new IOException("image save failed."); + } + } + + + protected String[] saveImageFormats; + + /** + * Save this image to disk. + *

+ * 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 a1) { - return false; - } else { - float xt = x_array[0]; - float yt = y_array[0]; - - x_array[0] = xt + a0 * (x_array[1] - xt); - y_array[0] = yt + a0 * (y_array[1] - yt); - x_array[1] = xt + a1 * (x_array[1] - xt); - y_array[1] = yt + a1 * (y_array[1] - yt); - - // interpolate remaining parameters - if (INTERPOLATE_RGB) { - float t = r_array[0]; - r_array[0] = t + a0 * (r_array[1] - t); - r_array[1] = t + a1 * (r_array[1] - t); - t = g_array[0]; - g_array[0] = t + a0 * (g_array[1] - t); - g_array[1] = t + a1 * (g_array[1] - t); - t = b_array[0]; - b_array[0] = t + a0 * (b_array[1] - t); - b_array[1] = t + a1 * (b_array[1] - t); - } - - if (INTERPOLATE_ALPHA) { - float t = a_array[0]; - a_array[0] = t + a0 * (a_array[1] - t); - a_array[1] = t + a1 * (a_array[1] - t); - } - } - } - return true; - } - - - private int lineClipCode(float xi, float yi) { - int xmin = 0; - int ymin = 0; - int xmax = SCREEN_WIDTH1; - int ymax = SCREEN_HEIGHT1; - - return ((yi < ymin ? 8 : 0) | (yi > ymax ? 4 : 0) | - (xi < xmin ? 2 : 0) | (xi > xmax ? 1 : 0)); - } - - - private float lineSlope(float x1, float y1, float x2, float y2, int border) { - int xmin = 0; - int ymin = 0; - int xmax = SCREEN_WIDTH1; - int ymax = SCREEN_HEIGHT1; - - switch (border) { - case 4: return (ymin-y1)/(y2-y1); - case 3: return (ymax-y1)/(y2-y1); - case 2: return (xmin-x1)/(x2-x1); - case 1: return (xmax-x1)/(x2-x1); - } - return -1f; - } - - - private void drawPoint(int x0, int y0) { - float iz = m_z0; - int offset = y0 * SCREEN_WIDTH + x0; - - if (iz <= m_zbuffer[offset]) { - m_pixels[offset] = m_stroke; - m_zbuffer[offset] = iz; - } - } - - - private void drawPoint_alpha(int x0, int y0) { - int ia = (int) a_array[0]; - int pr = m_stroke & 0xFF0000; - int pg = m_stroke & 0xFF00; - int pb = m_stroke & 0xFF; - float iz = m_z0; - int offset = y0 * SCREEN_WIDTH + x0; - - if (iz <= m_zbuffer[offset]) { - int alpha = ia >> 16; - int r0 = m_pixels[offset]; - int g0 = r0 & 0xFF00; - int b0 = r0 & 0xFF; - r0 &= 0xFF0000; - - r0 = r0 + (((pr - r0) * alpha) >> 8); - g0 = g0 + (((pg - g0) * alpha) >> 8); - b0 = b0 + (((pb - b0) * alpha) >> 8); - - m_pixels[offset] = 0xFF000000 | - (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); - //m_zbuffer[offset] = iz; - } - } - - - private void drawLine_plain(int x0, int y0, int dt, - int length, boolean vertical) { - // new "extremely fast" line code - // adapted from http://www.edepot.com/linee.html - // first version modified by [toxi] - // simplified by [rocha] - // length must be >= 0 - - //assert length>=0:length; - - int offset = 0; - - if (vertical) { - // vertical - length += y0; - for (int j = 0x8000 + (x0<<16); y0 <= length; ++y0) { - offset = y0 * SCREEN_WIDTH + (j>>16); - m_pixels[offset] = m_stroke; - m_zbuffer[offset] = m_z0; - j+=dt; - } - - } else { - // horizontal - length += x0; - for (int j = 0x8000 + (y0<<16); x0 <= length; ++x0) { - offset = (j>>16) * SCREEN_WIDTH + x0; - m_pixels[offset] = m_stroke; - m_zbuffer[offset] = m_z0; - j+=dt; - } - } - } - - - private void drawLine_plain_alpha(int x0, int y0, int dt, - int length, boolean vertical) { - int offset = 0; - - int pr = m_stroke & 0xFF0000; - int pg = m_stroke & 0xFF00; - int pb = m_stroke & 0xFF; - - int ia = (int) (m_a0); - - if (vertical) { - length += y0; - for (int j = 0x8000 + (x0<<16); y0 <= length; ++y0) { - offset = y0 * SCREEN_WIDTH + (j>>16); - - int alpha = ia >> 16; - int r0 = m_pixels[offset]; - int g0 = r0 & 0xFF00; - int b0 = r0 & 0xFF; - r0 &= 0xFF0000; - r0 = r0 + (((pr - r0) * alpha) >> 8); - g0 = g0 + (((pg - g0) * alpha) >> 8); - b0 = b0 + (((pb - b0) * alpha) >> 8); - - m_pixels[offset] = 0xFF000000 | - (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); - m_zbuffer[offset] = m_z0; - - ia += da; - j += dt; - } - - } else { // horizontal - length += x0; - for (int j = 0x8000 + (y0<<16); x0 <= length; ++x0) { - offset = (j>>16) * SCREEN_WIDTH + x0; - - int alpha = ia >> 16; - int r0 = m_pixels[offset]; - int g0 = r0 & 0xFF00; - int b0 = r0 & 0xFF; - r0&=0xFF0000; - r0 = r0 + (((pr - r0) * alpha) >> 8); - g0 = g0 + (((pg - g0) * alpha) >> 8); - b0 = b0 + (((pb - b0) * alpha) >> 8); - - m_pixels[offset] = 0xFF000000 | - (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); - m_zbuffer[offset] = m_z0; - - ia += da; - j += dt; - } - } - } - - - private void drawLine_color(int x0, int y0, int dt, - int length, boolean vertical) { - int offset = 0; - - int ir = (int) m_r0; - int ig = (int) m_g0; - int ib = (int) m_b0; - - if (vertical) { - length += y0; - for (int j = 0x8000 + (x0<<16); y0 <= length; ++y0) { - offset = y0 * SCREEN_WIDTH + (j>>16); - m_pixels[offset] = 0xFF000000 | - ((ir & 0xFF0000) | ((ig >> 8) & 0xFF00) | (ib >> 16)); - m_zbuffer[offset] = m_z0; - ir += dr; - ig += dg; - ib += db; - j +=dt; - } - - } else { // horizontal - length += x0; - for (int j = 0x8000 + (y0<<16); x0 <= length; ++x0) { - offset = (j>>16) * SCREEN_WIDTH + x0; - m_pixels[offset] = 0xFF000000 | - ((ir & 0xFF0000) | ((ig >> 8) & 0xFF00) | (ib >> 16)); - m_zbuffer[offset] = m_z0; - ir += dr; - ig += dg; - ib += db; - j += dt; - } - } - } - - - private void drawLine_color_alpha(int x0, int y0, int dt, - int length, boolean vertical) { - int offset = 0; - - int ir = (int) m_r0; - int ig = (int) m_g0; - int ib = (int) m_b0; - int ia = (int) m_a0; - - if (vertical) { - length += y0; - for (int j = 0x8000 + (x0<<16); y0 <= length; ++y0) { - offset = y0 * SCREEN_WIDTH + (j>>16); - - int pr = ir & 0xFF0000; - int pg = (ig >> 8) & 0xFF00; - int pb = (ib >> 16); - - int r0 = m_pixels[offset]; - int g0 = r0 & 0xFF00; - int b0 = r0 & 0xFF; - r0&=0xFF0000; - - int alpha = ia >> 16; - - r0 = r0 + (((pr - r0) * alpha) >> 8); - g0 = g0 + (((pg - g0) * alpha) >> 8); - b0 = b0 + (((pb - b0) * alpha) >> 8); - - m_pixels[offset] = 0xFF000000 | - (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); - m_zbuffer[offset] = m_z0; - - ir+= dr; - ig+= dg; - ib+= db; - ia+= da; - j+=dt; - } - - } else { // horizontal - length += x0; - for (int j = 0x8000 + (y0<<16); x0 <= length; ++x0) { - offset = (j>>16) * SCREEN_WIDTH + x0; - - int pr = ir & 0xFF0000; - int pg = (ig >> 8) & 0xFF00; - int pb = (ib >> 16); - - int r0 = m_pixels[offset]; - int g0 = r0 & 0xFF00; - int b0 = r0 & 0xFF; - r0&=0xFF0000; - - int alpha = ia >> 16; - - r0 = r0 + (((pr - r0) * alpha) >> 8); - g0 = g0 + (((pg - g0) * alpha) >> 8); - b0 = b0 + (((pb - b0) * alpha) >> 8); - - m_pixels[offset] = 0xFF000000 | - (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); - m_zbuffer[offset] = m_z0; - - ir+= dr; - ig+= dg; - ib+= db; - ia+= da; - j+=dt; - } - } - } - - - private void drawLine_plain_spatial(int x0, int y0, int dt, - int length, boolean vertical) { - int offset = 0; - float iz = m_z0; - - if (vertical) { - length += y0; - for (int j = 0x8000 + (x0<<16); y0 <= length; ++y0) { - offset = y0 * SCREEN_WIDTH + (j>>16); - if (iz <= m_zbuffer[offset]) { - m_pixels[offset] = m_stroke; - m_zbuffer[offset] = iz; - } - iz+=dz; - j+=dt; - } - - } else { // horizontal - length += x0; - for (int j = 0x8000 + (y0<<16); x0 <= length; ++x0) { - offset = (j>>16) * SCREEN_WIDTH + x0; - if (iz <= m_zbuffer[offset]) { - m_pixels[offset] = m_stroke; - m_zbuffer[offset] = iz; - } - iz+=dz; - j+=dt; - } - } - } - - - private void drawLine_plain_alpha_spatial(int x0, int y0, int dt, - int length, boolean vertical) { - int offset = 0; - float iz = m_z0; - - int pr = m_stroke & 0xFF0000; - int pg = m_stroke & 0xFF00; - int pb = m_stroke & 0xFF; - - int ia = (int) m_a0; - - if (vertical) { - length += y0; - for (int j = 0x8000 + (x0<<16); y0 <= length; ++y0) { - offset = y0 * SCREEN_WIDTH + (j>>16); - - if (iz <= m_zbuffer[offset]) { - int alpha = ia >> 16; - int r0 = m_pixels[offset]; - int g0 = r0 & 0xFF00; - int b0 = r0 & 0xFF; - r0 &= 0xFF0000; - r0 = r0 + (((pr - r0) * alpha) >> 8); - g0 = g0 + (((pg - g0) * alpha) >> 8); - b0 = b0 + (((pb - b0) * alpha) >> 8); - - m_pixels[offset] = 0xFF000000 | - (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); - //m_zbuffer[offset] = iz; - } - iz +=dz; - ia += da; - j += dt; - } - - } else { // horizontal - length += x0; - for (int j = 0x8000 + (y0<<16); x0 <= length; ++x0) { - offset = (j>>16) * SCREEN_WIDTH + x0; - - if (iz <= m_zbuffer[offset]) { - int alpha = ia >> 16; - int r0 = m_pixels[offset]; - int g0 = r0 & 0xFF00; - int b0 = r0 & 0xFF; - r0&=0xFF0000; - r0 = r0 + (((pr - r0) * alpha) >> 8); - g0 = g0 + (((pg - g0) * alpha) >> 8); - b0 = b0 + (((pb - b0) * alpha) >> 8); - - m_pixels[offset] = 0xFF000000 | - (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); - //m_zbuffer[offset] = iz; - } - iz += dz; - ia += da; - j += dt; - } - } - } - - - private void drawLine_color_spatial(int x0, int y0, int dt, - int length, boolean vertical) { - int offset = 0; - float iz = m_z0; - - int ir = (int) m_r0; - int ig = (int) m_g0; - int ib = (int) m_b0; - - if (vertical) { - length += y0; - for (int j = 0x8000 + (x0<<16); y0 <= length; ++y0) { - offset = y0 * SCREEN_WIDTH + (j>>16); - - if (iz <= m_zbuffer[offset]) { - m_pixels[offset] = 0xFF000000 | - ((ir & 0xFF0000) | ((ig >> 8) & 0xFF00) | (ib >> 16)); - m_zbuffer[offset] = iz; - } - iz +=dz; - ir += dr; - ig += dg; - ib += db; - j += dt; - } - } else { // horizontal - length += x0; - for (int j = 0x8000 + (y0<<16); x0 <= length; ++x0) { - offset = (j>>16) * SCREEN_WIDTH + x0; - if (iz <= m_zbuffer[offset]) { - m_pixels[offset] = 0xFF000000 | - ((ir & 0xFF0000) | ((ig >> 8) & 0xFF00) | (ib >> 16)); - m_zbuffer[offset] = iz; - } - iz += dz; - ir += dr; - ig += dg; - ib += db; - j += dt; - } - return; - } - } - - - private void drawLine_color_alpha_spatial(int x0, int y0, int dt, - int length, boolean vertical) { - int offset = 0; - float iz = m_z0; - - int ir = (int) m_r0; - int ig = (int) m_g0; - int ib = (int) m_b0; - int ia = (int) m_a0; - - if (vertical) { - length += y0; - for (int j = 0x8000 + (x0<<16); y0 <= length; ++y0) { - offset = y0 * SCREEN_WIDTH + (j>>16); - - if (iz <= m_zbuffer[offset]) { - int pr = ir & 0xFF0000; - int pg = (ig >> 8) & 0xFF00; - int pb = (ib >> 16); - - int r0 = m_pixels[offset]; - int g0 = r0 & 0xFF00; - int b0 = r0 & 0xFF; - r0&=0xFF0000; - - int alpha = ia >> 16; - - r0 = r0 + (((pr - r0) * alpha) >> 8); - g0 = g0 + (((pg - g0) * alpha) >> 8); - b0 = b0 + (((pb - b0) * alpha) >> 8); - - m_pixels[offset] = 0xFF000000 | - (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); - m_zbuffer[offset] = iz; - } - iz+=dz; - ir+= dr; - ig+= dg; - ib+= db; - ia+= da; - j+=dt; - } - - } else { // horizontal - length += x0; - for (int j = 0x8000 + (y0<<16); x0 <= length; ++x0) { - offset = (j>>16) * SCREEN_WIDTH + x0; - - if (iz <= m_zbuffer[offset]) { - int pr = ir & 0xFF0000; - int pg = (ig >> 8) & 0xFF00; - int pb = (ib >> 16); - - int r0 = m_pixels[offset]; - int g0 = r0 & 0xFF00; - int b0 = r0 & 0xFF; - r0 &= 0xFF0000; - - int alpha = ia >> 16; - - r0 = r0 + (((pr - r0) * alpha) >> 8); - g0 = g0 + (((pg - g0) * alpha) >> 8); - b0 = b0 + (((pb - b0) * alpha) >> 8); - - m_pixels[offset] = 0xFF000000 | - (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); - m_zbuffer[offset] = iz; - } - iz += dz; - ir += dr; - ig += dg; - ib += db; - ia += da; - j += dt; - } - } - } - - - void drawLine_smooth(int x0, int y0, int dt, - int length, boolean vertical) { - int xi, yi; // these must be >=32 bits - int offset = 0; - int temp; - int end; - - float iz = m_z0; - - int ir = (int) m_r0; - int ig = (int) m_g0; - int ib = (int) m_b0; - int ia = (int) m_a0; - - if (vertical) { - xi = x0 << 16; - yi = y0 << 16; - - end = length + y0; - - while ((yi >> 16) < end) { - - offset = (yi>>16) * SCREEN_WIDTH + (xi>>16); - - int pr = ir & 0xFF0000; - int pg = (ig >> 8) & 0xFF00; - int pb = (ib >> 16); - - if (iz <= m_zbuffer[offset]) { - int alpha = (((~xi >> 8) & 0xFF) * (ia >> 16)) >> 8; - - int r0 = m_pixels[offset]; - int g0 = r0 & 0xFF00; - int b0 = r0 & 0xFF; - r0&=0xFF0000; - - r0 = r0 + (((pr - r0) * alpha) >> 8); - g0 = g0 + (((pg - g0) * alpha) >> 8); - b0 = b0 + (((pb - b0) * alpha) >> 8); - - m_pixels[offset] = 0xFF000000 | - (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); - m_zbuffer[offset] = iz; - } - - // this if() makes things slow. there shoudl be - // a better way to check if the second pixel is - // withing the image array [rocha] - temp = ((xi>>16)+1); - if (temp >= SCREEN_WIDTH) { - xi += dt; - yi += (1 << 16); - continue; - } - - offset = (yi>>16) * SCREEN_WIDTH + temp; - - if (iz <= m_zbuffer[offset]) { - int alpha = (((xi >> 8) & 0xFF) * (ia >> 16)) >> 8; - - int r0 = m_pixels[offset]; - int g0 = r0 & 0xFF00; - int b0 = r0 & 0xFF; - r0 &= 0xFF0000; - - r0 = r0 + (((pr - r0) * alpha) >> 8); - g0 = g0 + (((pg - g0) * alpha) >> 8); - b0 = b0 + (((pb - b0) * alpha) >> 8); - - m_pixels[offset] = 0xFF000000 | - (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); - m_zbuffer[offset] = iz; - } - - xi += dt; - yi += (1 << 16); - - iz+=dz; - ir+= dr; - ig+= dg; - ib+= db; - ia+= da; - } - - } else { // horizontal - xi = x0 << 16; - yi = y0 << 16; - end = length + x0; - - while ((xi >> 16) < end) { - offset = (yi>>16) * SCREEN_WIDTH + (xi>>16); - - int pr = ir & 0xFF0000; - int pg = (ig >> 8) & 0xFF00; - int pb = (ib >> 16); - - if (iz <= m_zbuffer[offset]) { - int alpha = (((~yi >> 8) & 0xFF) * (ia >> 16)) >> 8; - - int r0 = m_pixels[offset]; - int g0 = r0 & 0xFF00; - int b0 = r0 & 0xFF; - r0 &= 0xFF0000; - - r0 = r0 + (((pr - r0) * alpha) >> 8); - g0 = g0 + (((pg - g0) * alpha) >> 8); - b0 = b0 + (((pb - b0) * alpha) >> 8); - - m_pixels[offset] = 0xFF000000 | - (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); - m_zbuffer[offset] = iz; - } - - // see above [rocha] - temp = ((yi>>16)+1); - if (temp >= SCREEN_HEIGHT) { - xi += (1 << 16); - yi += dt; - continue; - } - - offset = temp * SCREEN_WIDTH + (xi>>16); - - if (iz <= m_zbuffer[offset]) { - int alpha = (((yi >> 8) & 0xFF) * (ia >> 16)) >> 8; - - int r0 = m_pixels[offset]; - int g0 = r0 & 0xFF00; - int b0 = r0 & 0xFF; - r0&=0xFF0000; - - r0 = r0 + (((pr - r0) * alpha) >> 8); - g0 = g0 + (((pg - g0) * alpha) >> 8); - b0 = b0 + (((pb - b0) * alpha) >> 8); - - m_pixels[offset] = 0xFF000000 | - (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); - m_zbuffer[offset] = iz; - } - - xi += (1 << 16); - yi += dt; - - iz+=dz; - ir+= dr; - ig+= dg; - ib+= db; - ia+= da; - } - } - } - - - /** - * Special "blender" line code by sami, - * used for anti-aliasing polygon edges - */ - /* - private void drawline_blender(double x0, double y0, double x1, double y1) - { - double tmp; - double dx = x1-x0; - double dy = y1-y0; - double adx = (dx >= 0) ? dx : -dx; - double ady = (dy >= 0) ? dy : -dy; - - // VERY small line --> skip - if (adx < 0.0001d && ady < 0.0001d) - return; - - // pixel color - int pxl; - - // vaakaviiva - if (adx > ady) { - // flip if x0 > x1 - if (x0 > x1) { - tmp = x0; - x0 = x1; - x1 = tmp; - tmp = y0; - y0 = y1; - y1 = tmp; - dx = x1-x0; - dy = y1-y0; - } - - // add interpolation params here - double addy = dy / dx; - - int ix0 = (int) (x0 + PIXEL_CENTER); - if (ix0 < 0) - ix0 = 0; - - int ix1 = (int) (x1 + PIXEL_CENTER); - if (ix1 > SCREEN_WIDTH) - ix1 = SCREEN_WIDTH; - - double delta = (ix0 + PIXEL_CENTER) - x0; - double ys = y0 + delta * addy; - - for (int a = ix0; a < ix1; a++,ys+=addy) { - int iy = (int) (ys - PIXEL_CENTER); - if ((iy >= 0) && (iy < SCREEN_HEIGHT1)) { - int ofs1 = iy * SCREEN_WIDTH + a; - int ofs2 = ofs1 + SCREEN_WIDTH; - - if (m_stencil[ofs1] == m_index) { - pxl = m_pixels[ofs1]; - } else if (m_stencil[ofs2] == m_index) { - pxl = m_pixels[ofs2]; - } else { - //m_pixels[ofs1] = 0xFFFFFF; - //m_pixels[ofs2] = 0xFFFFFF; - continue; - } - - double frcf = ys - PIXEL_CENTER; - - int frac1 = ((int) (frcf * 256f) & 0xFF); - int frac2 = 255 - frac1; - int pr = (pxl & 0xFF0000); - int pg = (pxl & 0xFF00); - int pb = (pxl & 0xFF); - - int r0 = m_pixels[ofs1]; - int g0 = (r0 & 0xFF00); - int b0 = (r0 & 0xFF); - r0 = (r0 & 0xFF0000); - r0 = r0 + (((pr - r0) * frac2) >> 8); - g0 = g0 + (((pg - g0) * frac2) >> 8); - b0 = b0 + (((pb - b0) * frac2) >> 8); - m_pixels[ofs1] = 0xFF000000 | - (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); - - r0 = m_pixels[ofs2]; - g0 = (r0 & 0xFF00); - b0 = (r0 & 0xFF); - r0 = (r0 & 0xFF0000); - r0 = r0 + (((pr - r0) * frac1) >> 8); - g0 = g0 + (((pg - g0) * frac1) >> 8); - b0 = b0 + (((pb - b0) * frac1) >> 8); - m_pixels[ofs2] = 0xFF000000 | - (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); - - //m_pixels[ofs1] = 0xFF00FF; - //m_pixels[ofs2] = 0xFFFF00; - } - } - } else { // pystyviiva - // flip if y1 > y0 - if (y0 > y1) { - tmp = x0; - x0 = x1; - x1 = tmp; - tmp = y0; - y0 = y1; - y1 = tmp; - dx = x1-x0; - dy = y1-y0; - } - - double addx = dx / dy; - int iy0 = (int) (y0 + PIXEL_CENTER); - if (iy0 < 0) - iy0 = 0; - int iy1 = (int) (y1 + PIXEL_CENTER); - if (iy1 > SCREEN_HEIGHT) - iy1 = SCREEN_HEIGHT; - - double delta = (iy0 + PIXEL_CENTER) - y0; - double xs = x0 + delta * addx; - - iy0*=SCREEN_WIDTH; - iy1*=SCREEN_WIDTH; - for (int a = iy0; a < iy1; a+=SCREEN_WIDTH,xs+=addx) { - int ix = (int) (xs - PIXEL_CENTER); - if ((ix >= 0) && (ix < SCREEN_WIDTH1)) { - int ofs1 = a + ix; - int ofs2 = ofs1+1; - - if (m_stencil[ofs1] == m_index) { - pxl = m_pixels[ofs1]; - } else if (m_stencil[ofs2] == m_index) { - pxl = m_pixels[ofs2]; - } else { - //m_pixels[ofs1] = 0xFFFFFF; - //m_pixels[ofs2] = 0xFFFFFF; - continue; - } - - int pr = (pxl & 0xFF0000); - int pg = (pxl & 0xFF00); - int pb = (pxl & 0xFF); - - double frcf = xs - PIXEL_CENTER; - int frac1 = ((int) (frcf * 256f) & 0xFF); - int frac2 = 255 - frac1; - - int r0 = m_pixels[ofs1]; - int g0 = (r0 & 0xFF00); - int b0 = (r0 & 0xFF); - r0 = (r0 & 0xFF0000); - r0 = r0 + (((pr - r0) * frac2) >> 8); - g0 = g0 + (((pg - g0) * frac2) >> 8); - b0 = b0 + (((pb - b0) * frac2) >> 8); - m_pixels[ofs1] = 0xFF000000 | - (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); - - r0 = m_pixels[ofs2]; - g0 = (r0 & 0xFF00); - b0 = (r0 & 0xFF); - r0 = (r0 & 0xFF0000); - r0 = r0 + (((pr - r0) * frac1) >> 8); - g0 = g0 + (((pg - g0) * frac1) >> 8); - b0 = b0 + (((pb - b0) * frac1) >> 8); - - //m_pixels[ofs1] = 0x0000FF; - //m_pixels[ofs2] = 0x00FFFF; - } - } - } - } - */ -} +/* -*- 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 a1) { + return false; + } else { + float xt = x_array[0]; + float yt = y_array[0]; + + x_array[0] = xt + a0 * (x_array[1] - xt); + y_array[0] = yt + a0 * (y_array[1] - yt); + x_array[1] = xt + a1 * (x_array[1] - xt); + y_array[1] = yt + a1 * (y_array[1] - yt); + + // interpolate remaining parameters + if (INTERPOLATE_RGB) { + float t = r_array[0]; + r_array[0] = t + a0 * (r_array[1] - t); + r_array[1] = t + a1 * (r_array[1] - t); + t = g_array[0]; + g_array[0] = t + a0 * (g_array[1] - t); + g_array[1] = t + a1 * (g_array[1] - t); + t = b_array[0]; + b_array[0] = t + a0 * (b_array[1] - t); + b_array[1] = t + a1 * (b_array[1] - t); + } + + if (INTERPOLATE_ALPHA) { + float t = a_array[0]; + a_array[0] = t + a0 * (a_array[1] - t); + a_array[1] = t + a1 * (a_array[1] - t); + } + } + } + return true; + } + + + private int lineClipCode(float xi, float yi) { + int xmin = 0; + int ymin = 0; + int xmax = SCREEN_WIDTH1; + int ymax = SCREEN_HEIGHT1; + + return ((yi < ymin ? 8 : 0) | (yi > ymax ? 4 : 0) | + (xi < xmin ? 2 : 0) | (xi > xmax ? 1 : 0)); + } + + + private float lineSlope(float x1, float y1, float x2, float y2, int border) { + int xmin = 0; + int ymin = 0; + int xmax = SCREEN_WIDTH1; + int ymax = SCREEN_HEIGHT1; + + switch (border) { + case 4: return (ymin-y1)/(y2-y1); + case 3: return (ymax-y1)/(y2-y1); + case 2: return (xmin-x1)/(x2-x1); + case 1: return (xmax-x1)/(x2-x1); + } + return -1f; + } + + + private void drawPoint(int x0, int y0) { + float iz = m_z0; + int offset = y0 * SCREEN_WIDTH + x0; + + if (iz <= m_zbuffer[offset]) { + m_pixels[offset] = m_stroke; + m_zbuffer[offset] = iz; + } + } + + + private void drawPoint_alpha(int x0, int y0) { + int ia = (int) a_array[0]; + int pr = m_stroke & 0xFF0000; + int pg = m_stroke & 0xFF00; + int pb = m_stroke & 0xFF; + float iz = m_z0; + int offset = y0 * SCREEN_WIDTH + x0; + + if (iz <= m_zbuffer[offset]) { + int alpha = ia >> 16; + int r0 = m_pixels[offset]; + int g0 = r0 & 0xFF00; + int b0 = r0 & 0xFF; + r0 &= 0xFF0000; + + r0 = r0 + (((pr - r0) * alpha) >> 8); + g0 = g0 + (((pg - g0) * alpha) >> 8); + b0 = b0 + (((pb - b0) * alpha) >> 8); + + m_pixels[offset] = 0xFF000000 | + (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); + //m_zbuffer[offset] = iz; + } + } + + + private void drawLine_plain(int x0, int y0, int dt, + int length, boolean vertical) { + // new "extremely fast" line code + // adapted from http://www.edepot.com/linee.html + // first version modified by [toxi] + // simplified by [rocha] + // length must be >= 0 + + //assert length>=0:length; + + int offset = 0; + + if (vertical) { + // vertical + length += y0; + for (int j = 0x8000 + (x0<<16); y0 <= length; ++y0) { + offset = y0 * SCREEN_WIDTH + (j>>16); + m_pixels[offset] = m_stroke; + m_zbuffer[offset] = m_z0; + j+=dt; + } + + } else { + // horizontal + length += x0; + for (int j = 0x8000 + (y0<<16); x0 <= length; ++x0) { + offset = (j>>16) * SCREEN_WIDTH + x0; + m_pixels[offset] = m_stroke; + m_zbuffer[offset] = m_z0; + j+=dt; + } + } + } + + + private void drawLine_plain_alpha(int x0, int y0, int dt, + int length, boolean vertical) { + int offset = 0; + + int pr = m_stroke & 0xFF0000; + int pg = m_stroke & 0xFF00; + int pb = m_stroke & 0xFF; + + int ia = (int) (m_a0); + + if (vertical) { + length += y0; + for (int j = 0x8000 + (x0<<16); y0 <= length; ++y0) { + offset = y0 * SCREEN_WIDTH + (j>>16); + + int alpha = ia >> 16; + int r0 = m_pixels[offset]; + int g0 = r0 & 0xFF00; + int b0 = r0 & 0xFF; + r0 &= 0xFF0000; + r0 = r0 + (((pr - r0) * alpha) >> 8); + g0 = g0 + (((pg - g0) * alpha) >> 8); + b0 = b0 + (((pb - b0) * alpha) >> 8); + + m_pixels[offset] = 0xFF000000 | + (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); + m_zbuffer[offset] = m_z0; + + ia += da; + j += dt; + } + + } else { // horizontal + length += x0; + for (int j = 0x8000 + (y0<<16); x0 <= length; ++x0) { + offset = (j>>16) * SCREEN_WIDTH + x0; + + int alpha = ia >> 16; + int r0 = m_pixels[offset]; + int g0 = r0 & 0xFF00; + int b0 = r0 & 0xFF; + r0&=0xFF0000; + r0 = r0 + (((pr - r0) * alpha) >> 8); + g0 = g0 + (((pg - g0) * alpha) >> 8); + b0 = b0 + (((pb - b0) * alpha) >> 8); + + m_pixels[offset] = 0xFF000000 | + (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); + m_zbuffer[offset] = m_z0; + + ia += da; + j += dt; + } + } + } + + + private void drawLine_color(int x0, int y0, int dt, + int length, boolean vertical) { + int offset = 0; + + int ir = (int) m_r0; + int ig = (int) m_g0; + int ib = (int) m_b0; + + if (vertical) { + length += y0; + for (int j = 0x8000 + (x0<<16); y0 <= length; ++y0) { + offset = y0 * SCREEN_WIDTH + (j>>16); + m_pixels[offset] = 0xFF000000 | + ((ir & 0xFF0000) | ((ig >> 8) & 0xFF00) | (ib >> 16)); + m_zbuffer[offset] = m_z0; + ir += dr; + ig += dg; + ib += db; + j +=dt; + } + + } else { // horizontal + length += x0; + for (int j = 0x8000 + (y0<<16); x0 <= length; ++x0) { + offset = (j>>16) * SCREEN_WIDTH + x0; + m_pixels[offset] = 0xFF000000 | + ((ir & 0xFF0000) | ((ig >> 8) & 0xFF00) | (ib >> 16)); + m_zbuffer[offset] = m_z0; + ir += dr; + ig += dg; + ib += db; + j += dt; + } + } + } + + + private void drawLine_color_alpha(int x0, int y0, int dt, + int length, boolean vertical) { + int offset = 0; + + int ir = (int) m_r0; + int ig = (int) m_g0; + int ib = (int) m_b0; + int ia = (int) m_a0; + + if (vertical) { + length += y0; + for (int j = 0x8000 + (x0<<16); y0 <= length; ++y0) { + offset = y0 * SCREEN_WIDTH + (j>>16); + + int pr = ir & 0xFF0000; + int pg = (ig >> 8) & 0xFF00; + int pb = (ib >> 16); + + int r0 = m_pixels[offset]; + int g0 = r0 & 0xFF00; + int b0 = r0 & 0xFF; + r0&=0xFF0000; + + int alpha = ia >> 16; + + r0 = r0 + (((pr - r0) * alpha) >> 8); + g0 = g0 + (((pg - g0) * alpha) >> 8); + b0 = b0 + (((pb - b0) * alpha) >> 8); + + m_pixels[offset] = 0xFF000000 | + (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); + m_zbuffer[offset] = m_z0; + + ir+= dr; + ig+= dg; + ib+= db; + ia+= da; + j+=dt; + } + + } else { // horizontal + length += x0; + for (int j = 0x8000 + (y0<<16); x0 <= length; ++x0) { + offset = (j>>16) * SCREEN_WIDTH + x0; + + int pr = ir & 0xFF0000; + int pg = (ig >> 8) & 0xFF00; + int pb = (ib >> 16); + + int r0 = m_pixels[offset]; + int g0 = r0 & 0xFF00; + int b0 = r0 & 0xFF; + r0&=0xFF0000; + + int alpha = ia >> 16; + + r0 = r0 + (((pr - r0) * alpha) >> 8); + g0 = g0 + (((pg - g0) * alpha) >> 8); + b0 = b0 + (((pb - b0) * alpha) >> 8); + + m_pixels[offset] = 0xFF000000 | + (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); + m_zbuffer[offset] = m_z0; + + ir+= dr; + ig+= dg; + ib+= db; + ia+= da; + j+=dt; + } + } + } + + + private void drawLine_plain_spatial(int x0, int y0, int dt, + int length, boolean vertical) { + int offset = 0; + float iz = m_z0; + + if (vertical) { + length += y0; + for (int j = 0x8000 + (x0<<16); y0 <= length; ++y0) { + offset = y0 * SCREEN_WIDTH + (j>>16); + if (iz <= m_zbuffer[offset]) { + m_pixels[offset] = m_stroke; + m_zbuffer[offset] = iz; + } + iz+=dz; + j+=dt; + } + + } else { // horizontal + length += x0; + for (int j = 0x8000 + (y0<<16); x0 <= length; ++x0) { + offset = (j>>16) * SCREEN_WIDTH + x0; + if (iz <= m_zbuffer[offset]) { + m_pixels[offset] = m_stroke; + m_zbuffer[offset] = iz; + } + iz+=dz; + j+=dt; + } + } + } + + + private void drawLine_plain_alpha_spatial(int x0, int y0, int dt, + int length, boolean vertical) { + int offset = 0; + float iz = m_z0; + + int pr = m_stroke & 0xFF0000; + int pg = m_stroke & 0xFF00; + int pb = m_stroke & 0xFF; + + int ia = (int) m_a0; + + if (vertical) { + length += y0; + for (int j = 0x8000 + (x0<<16); y0 <= length; ++y0) { + offset = y0 * SCREEN_WIDTH + (j>>16); + + if (iz <= m_zbuffer[offset]) { + int alpha = ia >> 16; + int r0 = m_pixels[offset]; + int g0 = r0 & 0xFF00; + int b0 = r0 & 0xFF; + r0 &= 0xFF0000; + r0 = r0 + (((pr - r0) * alpha) >> 8); + g0 = g0 + (((pg - g0) * alpha) >> 8); + b0 = b0 + (((pb - b0) * alpha) >> 8); + + m_pixels[offset] = 0xFF000000 | + (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); + //m_zbuffer[offset] = iz; + } + iz +=dz; + ia += da; + j += dt; + } + + } else { // horizontal + length += x0; + for (int j = 0x8000 + (y0<<16); x0 <= length; ++x0) { + offset = (j>>16) * SCREEN_WIDTH + x0; + + if (iz <= m_zbuffer[offset]) { + int alpha = ia >> 16; + int r0 = m_pixels[offset]; + int g0 = r0 & 0xFF00; + int b0 = r0 & 0xFF; + r0&=0xFF0000; + r0 = r0 + (((pr - r0) * alpha) >> 8); + g0 = g0 + (((pg - g0) * alpha) >> 8); + b0 = b0 + (((pb - b0) * alpha) >> 8); + + m_pixels[offset] = 0xFF000000 | + (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); + //m_zbuffer[offset] = iz; + } + iz += dz; + ia += da; + j += dt; + } + } + } + + + private void drawLine_color_spatial(int x0, int y0, int dt, + int length, boolean vertical) { + int offset = 0; + float iz = m_z0; + + int ir = (int) m_r0; + int ig = (int) m_g0; + int ib = (int) m_b0; + + if (vertical) { + length += y0; + for (int j = 0x8000 + (x0<<16); y0 <= length; ++y0) { + offset = y0 * SCREEN_WIDTH + (j>>16); + + if (iz <= m_zbuffer[offset]) { + m_pixels[offset] = 0xFF000000 | + ((ir & 0xFF0000) | ((ig >> 8) & 0xFF00) | (ib >> 16)); + m_zbuffer[offset] = iz; + } + iz +=dz; + ir += dr; + ig += dg; + ib += db; + j += dt; + } + } else { // horizontal + length += x0; + for (int j = 0x8000 + (y0<<16); x0 <= length; ++x0) { + offset = (j>>16) * SCREEN_WIDTH + x0; + if (iz <= m_zbuffer[offset]) { + m_pixels[offset] = 0xFF000000 | + ((ir & 0xFF0000) | ((ig >> 8) & 0xFF00) | (ib >> 16)); + m_zbuffer[offset] = iz; + } + iz += dz; + ir += dr; + ig += dg; + ib += db; + j += dt; + } + return; + } + } + + + private void drawLine_color_alpha_spatial(int x0, int y0, int dt, + int length, boolean vertical) { + int offset = 0; + float iz = m_z0; + + int ir = (int) m_r0; + int ig = (int) m_g0; + int ib = (int) m_b0; + int ia = (int) m_a0; + + if (vertical) { + length += y0; + for (int j = 0x8000 + (x0<<16); y0 <= length; ++y0) { + offset = y0 * SCREEN_WIDTH + (j>>16); + + if (iz <= m_zbuffer[offset]) { + int pr = ir & 0xFF0000; + int pg = (ig >> 8) & 0xFF00; + int pb = (ib >> 16); + + int r0 = m_pixels[offset]; + int g0 = r0 & 0xFF00; + int b0 = r0 & 0xFF; + r0&=0xFF0000; + + int alpha = ia >> 16; + + r0 = r0 + (((pr - r0) * alpha) >> 8); + g0 = g0 + (((pg - g0) * alpha) >> 8); + b0 = b0 + (((pb - b0) * alpha) >> 8); + + m_pixels[offset] = 0xFF000000 | + (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); + m_zbuffer[offset] = iz; + } + iz+=dz; + ir+= dr; + ig+= dg; + ib+= db; + ia+= da; + j+=dt; + } + + } else { // horizontal + length += x0; + for (int j = 0x8000 + (y0<<16); x0 <= length; ++x0) { + offset = (j>>16) * SCREEN_WIDTH + x0; + + if (iz <= m_zbuffer[offset]) { + int pr = ir & 0xFF0000; + int pg = (ig >> 8) & 0xFF00; + int pb = (ib >> 16); + + int r0 = m_pixels[offset]; + int g0 = r0 & 0xFF00; + int b0 = r0 & 0xFF; + r0 &= 0xFF0000; + + int alpha = ia >> 16; + + r0 = r0 + (((pr - r0) * alpha) >> 8); + g0 = g0 + (((pg - g0) * alpha) >> 8); + b0 = b0 + (((pb - b0) * alpha) >> 8); + + m_pixels[offset] = 0xFF000000 | + (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); + m_zbuffer[offset] = iz; + } + iz += dz; + ir += dr; + ig += dg; + ib += db; + ia += da; + j += dt; + } + } + } + + + void drawLine_smooth(int x0, int y0, int dt, + int length, boolean vertical) { + int xi, yi; // these must be >=32 bits + int offset = 0; + int temp; + int end; + + float iz = m_z0; + + int ir = (int) m_r0; + int ig = (int) m_g0; + int ib = (int) m_b0; + int ia = (int) m_a0; + + if (vertical) { + xi = x0 << 16; + yi = y0 << 16; + + end = length + y0; + + while ((yi >> 16) < end) { + + offset = (yi>>16) * SCREEN_WIDTH + (xi>>16); + + int pr = ir & 0xFF0000; + int pg = (ig >> 8) & 0xFF00; + int pb = (ib >> 16); + + if (iz <= m_zbuffer[offset]) { + int alpha = (((~xi >> 8) & 0xFF) * (ia >> 16)) >> 8; + + int r0 = m_pixels[offset]; + int g0 = r0 & 0xFF00; + int b0 = r0 & 0xFF; + r0&=0xFF0000; + + r0 = r0 + (((pr - r0) * alpha) >> 8); + g0 = g0 + (((pg - g0) * alpha) >> 8); + b0 = b0 + (((pb - b0) * alpha) >> 8); + + m_pixels[offset] = 0xFF000000 | + (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); + m_zbuffer[offset] = iz; + } + + // this if() makes things slow. there shoudl be + // a better way to check if the second pixel is + // withing the image array [rocha] + temp = ((xi>>16)+1); + if (temp >= SCREEN_WIDTH) { + xi += dt; + yi += (1 << 16); + continue; + } + + offset = (yi>>16) * SCREEN_WIDTH + temp; + + if (iz <= m_zbuffer[offset]) { + int alpha = (((xi >> 8) & 0xFF) * (ia >> 16)) >> 8; + + int r0 = m_pixels[offset]; + int g0 = r0 & 0xFF00; + int b0 = r0 & 0xFF; + r0 &= 0xFF0000; + + r0 = r0 + (((pr - r0) * alpha) >> 8); + g0 = g0 + (((pg - g0) * alpha) >> 8); + b0 = b0 + (((pb - b0) * alpha) >> 8); + + m_pixels[offset] = 0xFF000000 | + (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); + m_zbuffer[offset] = iz; + } + + xi += dt; + yi += (1 << 16); + + iz+=dz; + ir+= dr; + ig+= dg; + ib+= db; + ia+= da; + } + + } else { // horizontal + xi = x0 << 16; + yi = y0 << 16; + end = length + x0; + + while ((xi >> 16) < end) { + offset = (yi>>16) * SCREEN_WIDTH + (xi>>16); + + int pr = ir & 0xFF0000; + int pg = (ig >> 8) & 0xFF00; + int pb = (ib >> 16); + + if (iz <= m_zbuffer[offset]) { + int alpha = (((~yi >> 8) & 0xFF) * (ia >> 16)) >> 8; + + int r0 = m_pixels[offset]; + int g0 = r0 & 0xFF00; + int b0 = r0 & 0xFF; + r0 &= 0xFF0000; + + r0 = r0 + (((pr - r0) * alpha) >> 8); + g0 = g0 + (((pg - g0) * alpha) >> 8); + b0 = b0 + (((pb - b0) * alpha) >> 8); + + m_pixels[offset] = 0xFF000000 | + (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); + m_zbuffer[offset] = iz; + } + + // see above [rocha] + temp = ((yi>>16)+1); + if (temp >= SCREEN_HEIGHT) { + xi += (1 << 16); + yi += dt; + continue; + } + + offset = temp * SCREEN_WIDTH + (xi>>16); + + if (iz <= m_zbuffer[offset]) { + int alpha = (((yi >> 8) & 0xFF) * (ia >> 16)) >> 8; + + int r0 = m_pixels[offset]; + int g0 = r0 & 0xFF00; + int b0 = r0 & 0xFF; + r0&=0xFF0000; + + r0 = r0 + (((pr - r0) * alpha) >> 8); + g0 = g0 + (((pg - g0) * alpha) >> 8); + b0 = b0 + (((pb - b0) * alpha) >> 8); + + m_pixels[offset] = 0xFF000000 | + (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); + m_zbuffer[offset] = iz; + } + + xi += (1 << 16); + yi += dt; + + iz+=dz; + ir+= dr; + ig+= dg; + ib+= db; + ia+= da; + } + } + } + + + /** + * Special "blender" line code by sami, + * used for anti-aliasing polygon edges + */ + /* + private void drawline_blender(double x0, double y0, double x1, double y1) + { + double tmp; + double dx = x1-x0; + double dy = y1-y0; + double adx = (dx >= 0) ? dx : -dx; + double ady = (dy >= 0) ? dy : -dy; + + // VERY small line --> skip + if (adx < 0.0001d && ady < 0.0001d) + return; + + // pixel color + int pxl; + + // vaakaviiva + if (adx > ady) { + // flip if x0 > x1 + if (x0 > x1) { + tmp = x0; + x0 = x1; + x1 = tmp; + tmp = y0; + y0 = y1; + y1 = tmp; + dx = x1-x0; + dy = y1-y0; + } + + // add interpolation params here + double addy = dy / dx; + + int ix0 = (int) (x0 + PIXEL_CENTER); + if (ix0 < 0) + ix0 = 0; + + int ix1 = (int) (x1 + PIXEL_CENTER); + if (ix1 > SCREEN_WIDTH) + ix1 = SCREEN_WIDTH; + + double delta = (ix0 + PIXEL_CENTER) - x0; + double ys = y0 + delta * addy; + + for (int a = ix0; a < ix1; a++,ys+=addy) { + int iy = (int) (ys - PIXEL_CENTER); + if ((iy >= 0) && (iy < SCREEN_HEIGHT1)) { + int ofs1 = iy * SCREEN_WIDTH + a; + int ofs2 = ofs1 + SCREEN_WIDTH; + + if (m_stencil[ofs1] == m_index) { + pxl = m_pixels[ofs1]; + } else if (m_stencil[ofs2] == m_index) { + pxl = m_pixels[ofs2]; + } else { + //m_pixels[ofs1] = 0xFFFFFF; + //m_pixels[ofs2] = 0xFFFFFF; + continue; + } + + double frcf = ys - PIXEL_CENTER; + + int frac1 = ((int) (frcf * 256f) & 0xFF); + int frac2 = 255 - frac1; + int pr = (pxl & 0xFF0000); + int pg = (pxl & 0xFF00); + int pb = (pxl & 0xFF); + + int r0 = m_pixels[ofs1]; + int g0 = (r0 & 0xFF00); + int b0 = (r0 & 0xFF); + r0 = (r0 & 0xFF0000); + r0 = r0 + (((pr - r0) * frac2) >> 8); + g0 = g0 + (((pg - g0) * frac2) >> 8); + b0 = b0 + (((pb - b0) * frac2) >> 8); + m_pixels[ofs1] = 0xFF000000 | + (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); + + r0 = m_pixels[ofs2]; + g0 = (r0 & 0xFF00); + b0 = (r0 & 0xFF); + r0 = (r0 & 0xFF0000); + r0 = r0 + (((pr - r0) * frac1) >> 8); + g0 = g0 + (((pg - g0) * frac1) >> 8); + b0 = b0 + (((pb - b0) * frac1) >> 8); + m_pixels[ofs2] = 0xFF000000 | + (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); + + //m_pixels[ofs1] = 0xFF00FF; + //m_pixels[ofs2] = 0xFFFF00; + } + } + } else { // pystyviiva + // flip if y1 > y0 + if (y0 > y1) { + tmp = x0; + x0 = x1; + x1 = tmp; + tmp = y0; + y0 = y1; + y1 = tmp; + dx = x1-x0; + dy = y1-y0; + } + + double addx = dx / dy; + int iy0 = (int) (y0 + PIXEL_CENTER); + if (iy0 < 0) + iy0 = 0; + int iy1 = (int) (y1 + PIXEL_CENTER); + if (iy1 > SCREEN_HEIGHT) + iy1 = SCREEN_HEIGHT; + + double delta = (iy0 + PIXEL_CENTER) - y0; + double xs = x0 + delta * addx; + + iy0*=SCREEN_WIDTH; + iy1*=SCREEN_WIDTH; + for (int a = iy0; a < iy1; a+=SCREEN_WIDTH,xs+=addx) { + int ix = (int) (xs - PIXEL_CENTER); + if ((ix >= 0) && (ix < SCREEN_WIDTH1)) { + int ofs1 = a + ix; + int ofs2 = ofs1+1; + + if (m_stencil[ofs1] == m_index) { + pxl = m_pixels[ofs1]; + } else if (m_stencil[ofs2] == m_index) { + pxl = m_pixels[ofs2]; + } else { + //m_pixels[ofs1] = 0xFFFFFF; + //m_pixels[ofs2] = 0xFFFFFF; + continue; + } + + int pr = (pxl & 0xFF0000); + int pg = (pxl & 0xFF00); + int pb = (pxl & 0xFF); + + double frcf = xs - PIXEL_CENTER; + int frac1 = ((int) (frcf * 256f) & 0xFF); + int frac2 = 255 - frac1; + + int r0 = m_pixels[ofs1]; + int g0 = (r0 & 0xFF00); + int b0 = (r0 & 0xFF); + r0 = (r0 & 0xFF0000); + r0 = r0 + (((pr - r0) * frac2) >> 8); + g0 = g0 + (((pg - g0) * frac2) >> 8); + b0 = b0 + (((pb - b0) * frac2) >> 8); + m_pixels[ofs1] = 0xFF000000 | + (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 0xFF); + + r0 = m_pixels[ofs2]; + g0 = (r0 & 0xFF00); + b0 = (r0 & 0xFF); + r0 = (r0 & 0xFF0000); + r0 = r0 + (((pr - r0) * frac1) >> 8); + g0 = g0 + (((pg - g0) * frac1) >> 8); + b0 = b0 + (((pb - b0) * frac1) >> 8); + + //m_pixels[ofs1] = 0x0000FF; + //m_pixels[ofs2] = 0x00FFFF; + } + } + } + } + */ +} diff --git a/core/PMatrix.java b/core/src/processing/core/PMatrix.java old mode 100755 new mode 100644 similarity index 96% rename from core/PMatrix.java rename to core/src/processing/core/PMatrix.java index 9b19da613..7ed0aaa1f --- a/core/PMatrix.java +++ b/core/src/processing/core/PMatrix.java @@ -1,640 +1,640 @@ -/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://Proce55ing.net - - 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; - - -/** - * 4x4 matrix implementation. - */ -public final class PMatrix implements PConstants { - - public float m00, m01, m02, m03; - public float m10, m11, m12, m13; - public float m20, m21, m22, m23; - public float m30, m31, m32, m33; - - final static int DEFAULT_STACK_DEPTH = 0; - int maxStackDepth; - int stackPointer = 0; - float stack[][]; - - - // locally allocated version to avoid creating new memory - static protected PMatrix inverseCopy; - - - public PMatrix() { - set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); - maxStackDepth = DEFAULT_STACK_DEPTH; - } - - - public PMatrix(int stackDepth) { - set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); - stack = new float[stackDepth][16]; - maxStackDepth = stackDepth; - } - - - public PMatrix(float m00, float m01, float m02, float m03, - float m10, float m11, float m12, float m13, - float m20, float m21, float m22, float m23, - float m30, float m31, float m32, float m33) { - set(m00, m01, m02, m03, - m10, m11, m12, m13, - m20, m21, m22, m23, - m30, m31, m32, m33); - maxStackDepth = DEFAULT_STACK_DEPTH; - } - - - // Make a copy of a matrix. We copy the stack depth, - // but we don't make a copy of the stack or the stack pointer. - public PMatrix(PMatrix src) { - set(src); - maxStackDepth = src.maxStackDepth; - stack = new float[maxStackDepth][16]; - } - - - public void reset() { - set(1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - } - - - public void clearStack() { - stackPointer = 0; - } - - - public boolean push() { - if (stackPointer == maxStackDepth) return false; - - stack[stackPointer][0] = m00; - stack[stackPointer][1] = m01; - stack[stackPointer][2] = m02; - stack[stackPointer][3] = m03; - - stack[stackPointer][4] = m10; - stack[stackPointer][5] = m11; - stack[stackPointer][6] = m12; - stack[stackPointer][7] = m13; - - stack[stackPointer][8] = m20; - stack[stackPointer][9] = m21; - stack[stackPointer][10] = m22; - stack[stackPointer][11] = m23; - - stack[stackPointer][12] = m30; - stack[stackPointer][13] = m31; - stack[stackPointer][14] = m32; - stack[stackPointer][15] = m33; - - stackPointer++; - return true; - } - - - public boolean pop() { - if (stackPointer == 0) return false; - stackPointer--; - - m00 = stack[stackPointer][0]; - m01 = stack[stackPointer][1]; - m02 = stack[stackPointer][2]; - m03 = stack[stackPointer][3]; - - m10 = stack[stackPointer][4]; - m11 = stack[stackPointer][5]; - m12 = stack[stackPointer][6]; - m13 = stack[stackPointer][7]; - - m20 = stack[stackPointer][8]; - m21 = stack[stackPointer][9]; - m22 = stack[stackPointer][10]; - m23 = stack[stackPointer][11]; - - m30 = stack[stackPointer][12]; - m31 = stack[stackPointer][13]; - m32 = stack[stackPointer][14]; - m33 = stack[stackPointer][15]; - - return true; - } - - - public void set(PMatrix src) { - set(src.m00, src.m01, src.m02, src.m03, - src.m10, src.m11, src.m12, src.m13, - src.m20, src.m21, src.m22, src.m23, - src.m30, src.m31, src.m32, src.m33); - } - - - public void set(float m00, float m01, float m02, float m03, - float m10, float m11, float m12, float m13, - float m20, float m21, float m22, float m23, - float m30, float m31, float m32, float m33) { - this.m00 = m00; this.m01 = m01; this.m02 = m02; this.m03 = m03; - this.m10 = m10; this.m11 = m11; this.m12 = m12; this.m13 = m13; - this.m20 = m20; this.m21 = m21; this.m22 = m22; this.m23 = m23; - this.m30 = m30; this.m31 = m31; this.m32 = m32; this.m33 = m33; - } - - - public void translate(float tx, float ty) { - translate(tx, ty, 0); - } - - public void invTranslate(float tx, float ty) { - invTranslate(tx, ty, 0); - } - - - public void translate(float tx, float ty, float tz) { - m03 += tx*m00 + ty*m01 + tz*m02; - m13 += tx*m10 + ty*m11 + tz*m12; - m23 += tx*m20 + ty*m21 + tz*m22; - m33 += tx*m30 + ty*m31 + tz*m32; - } - - public void invTranslate(float tx, float ty, float tz) { - preApply(1, 0, 0, -tx, - 0, 1, 0, -ty, - 0, 0, 1, -tz, - 0, 0, 0, 1); - } - - - // 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) { - float c = cos(angle); - float s = sin(angle); - apply(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1); - } - - - public void invRotateX(float angle) { - float c = cos(-angle); - float s = sin(-angle); - preApply(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1); - } - - - public void rotateY(float angle) { - float c = cos(angle); - float s = sin(angle); - apply(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1); - } - - - public void invRotateY(float angle) { - float c = cos(-angle); - float s = sin(-angle); - preApply(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1); - } - - - /** - * Just calls rotateZ because two dimensional rotation - * is the same as rotating along the z-axis. - */ - public void rotate(float angle) { - rotateZ(angle); - } - - - public void invRotate(float angle) { - invRotateZ(angle); - } - - - public void rotateZ(float angle) { - float c = cos(angle); - float s = sin(angle); - apply(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); - } - - - public void invRotateZ(float angle) { - float c = cos(-angle); - float s = sin(-angle); - preApply(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); - } - - - public void rotate(float angle, float v0, float v1, float v2) { - // should be in radians (i think), instead of degrees (gl uses degrees) - // based on 15-463 code, but similar to opengl ref p.443 - - // TODO should make sure this vector is normalized - - float c = cos(angle); - float s = sin(angle); - float t = 1.0f - c; - - apply((t*v0*v0) + c, (t*v0*v1) - (s*v2), (t*v0*v2) + (s*v1), 0, - (t*v0*v1) + (s*v2), (t*v1*v1) + c, (t*v1*v2) - (s*v0), 0, - (t*v0*v2) - (s*v1), (t*v1*v2) + (s*v0), (t*v2*v2) + c, 0, - 0, 0, 0, 1); - } - - - public void invRotate(float angle, float v0, float v1, float v2) { - // TODO should make sure this vector is normalized - - float c = cos(-angle); - float s = sin(-angle); - float t = 1.0f - c; - - preApply((t*v0*v0) + c, (t*v0*v1) - (s*v2), (t*v0*v2) + (s*v1), 0, - (t*v0*v1) + (s*v2), (t*v1*v1) + c, (t*v1*v2) - (s*v0), 0, - (t*v0*v2) - (s*v1), (t*v1*v2) + (s*v0), (t*v2*v2) + c, 0, - 0, 0, 0, 1); - } - - - public void scale(float s) { - apply(s, 0, 0, 0, 0, s, 0, 0, 0, 0, s, 0, 0, 0, 0, 1); - } - - - public void invScale(float s) { - preApply(1/s, 0, 0, 0, 0, 1/s, 0, 0, 0, 0, 1/s, 0, 0, 0, 0, 1); - } - - - public void scale(float sx, float sy) { - apply(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); - } - - - public void invScale(float sx, float sy) { - preApply(1/sx, 0, 0, 0, 0, 1/sy, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); - } - - - // OPTIMIZE: same as above - public void scale(float x, float y, float z) { - apply(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1); - } - - - public void invScale(float x, float y, float z) { - preApply(1/x, 0, 0, 0, 0, 1/y, 0, 0, 0, 0, 1/z, 0, 0, 0, 0, 1); - } - - - /* - public void transform(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) { - apply(n00, n01, n02, n03, - n10, n11, n12, n13, - n20, n21, n22, n23, - n30, n31, n32, n33); - } - */ - - - public void preApply(PMatrix lhs) { - preApply(lhs.m00, lhs.m01, lhs.m02, lhs.m03, - lhs.m10, lhs.m11, lhs.m12, lhs.m13, - lhs.m20, lhs.m21, lhs.m22, lhs.m23, - lhs.m30, lhs.m31, lhs.m32, lhs.m33); - } - - - // for inverse operations, like multiplying the matrix on the left - public void preApply(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) { - - float r00 = n00*m00 + n01*m10 + n02*m20 + n03*m30; - float r01 = n00*m01 + n01*m11 + n02*m21 + n03*m31; - float r02 = n00*m02 + n01*m12 + n02*m22 + n03*m32; - float r03 = n00*m03 + n01*m13 + n02*m23 + n03*m33; - - float r10 = n10*m00 + n11*m10 + n12*m20 + n13*m30; - float r11 = n10*m01 + n11*m11 + n12*m21 + n13*m31; - float r12 = n10*m02 + n11*m12 + n12*m22 + n13*m32; - float r13 = n10*m03 + n11*m13 + n12*m23 + n13*m33; - - float r20 = n20*m00 + n21*m10 + n22*m20 + n23*m30; - float r21 = n20*m01 + n21*m11 + n22*m21 + n23*m31; - float r22 = n20*m02 + n21*m12 + n22*m22 + n23*m32; - float r23 = n20*m03 + n21*m13 + n22*m23 + n23*m33; - - float r30 = n30*m00 + n31*m10 + n32*m20 + n33*m30; - float r31 = n30*m01 + n31*m11 + n32*m21 + n33*m31; - float r32 = n30*m02 + n31*m12 + n32*m22 + n33*m32; - float r33 = n30*m03 + n31*m13 + n32*m23 + n33*m33; - - m00 = r00; m01 = r01; m02 = r02; m03 = r03; - m10 = r10; m11 = r11; m12 = r12; m13 = r13; - m20 = r20; m21 = r21; m22 = r22; m23 = r23; - m30 = r30; m31 = r31; m32 = r32; m33 = r33; - } - - - public boolean invApply(PMatrix rhs) { - PMatrix copy = new PMatrix(rhs); - PMatrix inverse = copy.invert(); - if (inverse == null) return false; - preApply(inverse); - return true; - } - - - public boolean invApply(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) { - if (inverseCopy == null) { - inverseCopy = new PMatrix(); - } - inverseCopy.set(n00, n01, n02, n03, - n10, n11, n12, n13, - n20, n21, n22, n23, - n30, n31, n32, n33); - PMatrix inverse = inverseCopy.invert(); - if (inverse == null) return false; - preApply(inverse); - return true; - } - - - public void apply(PMatrix rhs) { - apply(rhs.m00, rhs.m01, rhs.m02, rhs.m03, - rhs.m10, rhs.m11, rhs.m12, rhs.m13, - rhs.m20, rhs.m21, rhs.m22, rhs.m23, - rhs.m30, rhs.m31, rhs.m32, rhs.m33); - } - - - public void apply(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) { - - float r00 = m00*n00 + m01*n10 + m02*n20 + m03*n30; - float r01 = m00*n01 + m01*n11 + m02*n21 + m03*n31; - float r02 = m00*n02 + m01*n12 + m02*n22 + m03*n32; - float r03 = m00*n03 + m01*n13 + m02*n23 + m03*n33; - - float r10 = m10*n00 + m11*n10 + m12*n20 + m13*n30; - float r11 = m10*n01 + m11*n11 + m12*n21 + m13*n31; - float r12 = m10*n02 + m11*n12 + m12*n22 + m13*n32; - float r13 = m10*n03 + m11*n13 + m12*n23 + m13*n33; - - float r20 = m20*n00 + m21*n10 + m22*n20 + m23*n30; - float r21 = m20*n01 + m21*n11 + m22*n21 + m23*n31; - float r22 = m20*n02 + m21*n12 + m22*n22 + m23*n32; - float r23 = m20*n03 + m21*n13 + m22*n23 + m23*n33; - - float r30 = m30*n00 + m31*n10 + m32*n20 + m33*n30; - float r31 = m30*n01 + m31*n11 + m32*n21 + m33*n31; - float r32 = m30*n02 + m31*n12 + m32*n22 + m33*n32; - float r33 = m30*n03 + m31*n13 + m32*n23 + m33*n33; - - m00 = r00; m01 = r01; m02 = r02; m03 = r03; - m10 = r10; m11 = r11; m12 = r12; m13 = r13; - m20 = r20; m21 = r21; m22 = r22; m23 = r23; - m30 = r30; m31 = r31; m32 = r32; m33 = r33; - } - - - public void mult3(float vec[], float out[]) { - // must use these temp vars because vec may be the same as out - float tmpx = m00*vec[0] + m01*vec[1] + m02*vec[2] + m03; - float tmpy = m10*vec[0] + m11*vec[1] + m12*vec[2] + m13; - float tmpz = m20*vec[0] + m21*vec[1] + m22*vec[2] + m23; - - out[0] = tmpx; - out[1] = tmpy; - out[2] = tmpz; - } - - - public void mult(float vec[], float out[]) { - // must use these temp vars because vec may be the same as out - float tmpx = m00*vec[0] + m01*vec[1] + m02*vec[2] + m03*vec[3]; - float tmpy = m10*vec[0] + m11*vec[1] + m12*vec[2] + m13*vec[3]; - float tmpz = m20*vec[0] + m21*vec[1] + m22*vec[2] + m23*vec[3]; - float tmpw = m30*vec[0] + m31*vec[1] + m32*vec[2] + m33*vec[3]; - - out[0] = tmpx; - out[1] = tmpy; - out[2] = tmpz; - out[3] = tmpw; - } - - - /** - * @return the determinant of the matrix - */ - public float determinant() { - float f = - m00 - * ((m11 * m22 * m33 + m12 * m23 * m31 + m13 * m21 * m32) - - m13 * m22 * m31 - - m11 * m23 * m32 - - m12 * m21 * m33); - f -= m01 - * ((m10 * m22 * m33 + m12 * m23 * m30 + m13 * m20 * m32) - - m13 * m22 * m30 - - m10 * m23 * m32 - - m12 * m20 * m33); - f += m02 - * ((m10 * m21 * m33 + m11 * m23 * m30 + m13 * m20 * m31) - - m13 * m21 * m30 - - m10 * m23 * m31 - - m11 * m20 * m33); - f -= m03 - * ((m10 * m21 * m32 + m11 * m22 * m30 + m12 * m20 * m31) - - m12 * m21 * m30 - - m10 * m22 * m31 - - m11 * m20 * m32); - return f; - } - - - /** - * Calculate the determinant of a 3x3 matrix - * @return result - */ - private float determinant3x3(float t00, float t01, float t02, - float t10, float t11, float t12, - float t20, float t21, float t22) { - return (t00 * (t11 * t22 - t12 * t21) + - t01 * (t12 * t20 - t10 * t22) + - t02 * (t10 * t21 - t11 * t20)); - } - - - public PMatrix transpose() { - float temp; - temp = m01; m01 = m10; m10 = temp; - temp = m02; m02 = m20; m20 = temp; - temp = m03; m03 = m30; m30 = temp; - temp = m12; m12 = m21; m21 = temp; - temp = m13; m13 = m31; m31 = temp; - temp = m23; m23 = m32; m32 = temp; - return this; - } - - - /** - * Invert this matrix - * @return this if successful, null otherwise - */ - public PMatrix invert() { - - float determinant = determinant(); - - if (determinant != 0) { - // m00 m01 m02 m03 - // m10 m11 m12 m13 - // m20 m21 m22 m23 - // m30 m31 m32 m33 - float determinant_inv = 1f / determinant; - - // first row - float t00 = determinant3x3(m11, m12, m13, m21, m22, m23, m31, m32, m33); - float t01 = -determinant3x3(m10, m12, m13, m20, m22, m23, m30, m32, m33); - float t02 = determinant3x3(m10, m11, m13, m20, m21, m23, m30, m31, m33); - float t03 = -determinant3x3(m10, m11, m12, m20, m21, m22, m30, m31, m32); - - // second row - float t10 = -determinant3x3(m01, m02, m03, m21, m22, m23, m31, m32, m33); - float t11 = determinant3x3(m00, m02, m03, m20, m22, m23, m30, m32, m33); - float t12 = -determinant3x3(m00, m01, m03, m20, m21, m23, m30, m31, m33); - float t13 = determinant3x3(m00, m01, m02, m20, m21, m22, m30, m31, m32); - - // third row - float t20 = determinant3x3(m01, m02, m03, m11, m12, m13, m31, m32, m33); - float t21 = -determinant3x3(m00, m02, m03, m10, m12, m13, m30, m32, m33); - float t22 = determinant3x3(m00, m01, m03, m10, m11, m13, m30, m31, m33); - float t23 = -determinant3x3(m00, m01, m02, m10, m11, m12, m30, m31, m32); - - // fourth row - float t30 = -determinant3x3(m01, m02, m03, m11, m12, m13, m21, m22, m23); - float t31 = determinant3x3(m00, m02, m03, m10, m12, m13, m20, m22, m23); - float t32 = -determinant3x3(m00, m01, m03, m10, m11, m13, m20, m21, m23); - float t33 = determinant3x3(m00, m01, m02, m10, m11, m12, m20, m21, m22); - - // transpose and divide by the determinant - m00 = t00*determinant_inv; - m11 = t11*determinant_inv; - m22 = t22*determinant_inv; - m33 = t33*determinant_inv; - m01 = t10*determinant_inv; - m10 = t01*determinant_inv; - m20 = t02*determinant_inv; - m02 = t20*determinant_inv; - m12 = t21*determinant_inv; - m21 = t12*determinant_inv; - m03 = t30*determinant_inv; - m30 = t03*determinant_inv; - m13 = t31*determinant_inv; - m31 = t13*determinant_inv; - m32 = t23*determinant_inv; - m23 = t32*determinant_inv; - return this; - } - return null; - } - - - ////////////////////////////////////////////////////////////// - - - public void print() { - int big = (int) Math.abs(max(max(max(max(abs(m00), abs(m01)), - max(abs(m02), abs(m03))), - max(max(abs(m10), abs(m11)), - max(abs(m12), abs(m13)))), - max(max(max(abs(m20), abs(m21)), - max(abs(m22), abs(m23))), - max(max(abs(m30), abs(m31)), - max(abs(m32), abs(m33)))))); - - // avoid infinite loop - if (Float.isNaN(big) || Float.isInfinite(big)) { - big = 1000000; // set to something arbitrary - } - - int d = 1; - while ((big /= 10) != 0) d++; // cheap log() - - System.out.println(PApplet.nfs(m00, d, 4) + " " + - PApplet.nfs(m01, d, 4) + " " + - PApplet.nfs(m02, d, 4) + " " + - PApplet.nfs(m03, d, 4)); - - System.out.println(PApplet.nfs(m10, d, 4) + " " + - PApplet.nfs(m11, d, 4) + " " + - PApplet.nfs(m12, d, 4) + " " + - PApplet.nfs(m13, d, 4)); - - System.out.println(PApplet.nfs(m20, d, 4) + " " + - PApplet.nfs(m21, d, 4) + " " + - PApplet.nfs(m22, d, 4) + " " + - PApplet.nfs(m23, d, 4)); - - System.out.println(PApplet.nfs(m30, d, 4) + " " + - PApplet.nfs(m31, d, 4) + " " + - PApplet.nfs(m32, d, 4) + " " + - PApplet.nfs(m33, d, 4)); - - System.out.println(); - } - - - ////////////////////////////////////////////////////////////// - - - private final float max(float a, float b) { - return (a > b) ? 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); - } -} +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://Proce55ing.net + + 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; + + +/** + * 4x4 matrix implementation. + */ +public final class PMatrix implements PConstants { + + public float m00, m01, m02, m03; + public float m10, m11, m12, m13; + public float m20, m21, m22, m23; + public float m30, m31, m32, m33; + + final static int DEFAULT_STACK_DEPTH = 0; + int maxStackDepth; + int stackPointer = 0; + float stack[][]; + + + // locally allocated version to avoid creating new memory + static protected PMatrix inverseCopy; + + + public PMatrix() { + set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + maxStackDepth = DEFAULT_STACK_DEPTH; + } + + + public PMatrix(int stackDepth) { + set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + stack = new float[stackDepth][16]; + maxStackDepth = stackDepth; + } + + + public PMatrix(float m00, float m01, float m02, float m03, + float m10, float m11, float m12, float m13, + float m20, float m21, float m22, float m23, + float m30, float m31, float m32, float m33) { + set(m00, m01, m02, m03, + m10, m11, m12, m13, + m20, m21, m22, m23, + m30, m31, m32, m33); + maxStackDepth = DEFAULT_STACK_DEPTH; + } + + + // Make a copy of a matrix. We copy the stack depth, + // but we don't make a copy of the stack or the stack pointer. + public PMatrix(PMatrix src) { + set(src); + maxStackDepth = src.maxStackDepth; + stack = new float[maxStackDepth][16]; + } + + + public void reset() { + set(1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + } + + + public void clearStack() { + stackPointer = 0; + } + + + public boolean push() { + if (stackPointer == maxStackDepth) return false; + + stack[stackPointer][0] = m00; + stack[stackPointer][1] = m01; + stack[stackPointer][2] = m02; + stack[stackPointer][3] = m03; + + stack[stackPointer][4] = m10; + stack[stackPointer][5] = m11; + stack[stackPointer][6] = m12; + stack[stackPointer][7] = m13; + + stack[stackPointer][8] = m20; + stack[stackPointer][9] = m21; + stack[stackPointer][10] = m22; + stack[stackPointer][11] = m23; + + stack[stackPointer][12] = m30; + stack[stackPointer][13] = m31; + stack[stackPointer][14] = m32; + stack[stackPointer][15] = m33; + + stackPointer++; + return true; + } + + + public boolean pop() { + if (stackPointer == 0) return false; + stackPointer--; + + m00 = stack[stackPointer][0]; + m01 = stack[stackPointer][1]; + m02 = stack[stackPointer][2]; + m03 = stack[stackPointer][3]; + + m10 = stack[stackPointer][4]; + m11 = stack[stackPointer][5]; + m12 = stack[stackPointer][6]; + m13 = stack[stackPointer][7]; + + m20 = stack[stackPointer][8]; + m21 = stack[stackPointer][9]; + m22 = stack[stackPointer][10]; + m23 = stack[stackPointer][11]; + + m30 = stack[stackPointer][12]; + m31 = stack[stackPointer][13]; + m32 = stack[stackPointer][14]; + m33 = stack[stackPointer][15]; + + return true; + } + + + public void set(PMatrix src) { + set(src.m00, src.m01, src.m02, src.m03, + src.m10, src.m11, src.m12, src.m13, + src.m20, src.m21, src.m22, src.m23, + src.m30, src.m31, src.m32, src.m33); + } + + + public void set(float m00, float m01, float m02, float m03, + float m10, float m11, float m12, float m13, + float m20, float m21, float m22, float m23, + float m30, float m31, float m32, float m33) { + this.m00 = m00; this.m01 = m01; this.m02 = m02; this.m03 = m03; + this.m10 = m10; this.m11 = m11; this.m12 = m12; this.m13 = m13; + this.m20 = m20; this.m21 = m21; this.m22 = m22; this.m23 = m23; + this.m30 = m30; this.m31 = m31; this.m32 = m32; this.m33 = m33; + } + + + public void translate(float tx, float ty) { + translate(tx, ty, 0); + } + + public void invTranslate(float tx, float ty) { + invTranslate(tx, ty, 0); + } + + + public void translate(float tx, float ty, float tz) { + m03 += tx*m00 + ty*m01 + tz*m02; + m13 += tx*m10 + ty*m11 + tz*m12; + m23 += tx*m20 + ty*m21 + tz*m22; + m33 += tx*m30 + ty*m31 + tz*m32; + } + + public void invTranslate(float tx, float ty, float tz) { + preApply(1, 0, 0, -tx, + 0, 1, 0, -ty, + 0, 0, 1, -tz, + 0, 0, 0, 1); + } + + + // 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) { + float c = cos(angle); + float s = sin(angle); + apply(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1); + } + + + public void invRotateX(float angle) { + float c = cos(-angle); + float s = sin(-angle); + preApply(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1); + } + + + public void rotateY(float angle) { + float c = cos(angle); + float s = sin(angle); + apply(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1); + } + + + public void invRotateY(float angle) { + float c = cos(-angle); + float s = sin(-angle); + preApply(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1); + } + + + /** + * Just calls rotateZ because two dimensional rotation + * is the same as rotating along the z-axis. + */ + public void rotate(float angle) { + rotateZ(angle); + } + + + public void invRotate(float angle) { + invRotateZ(angle); + } + + + public void rotateZ(float angle) { + float c = cos(angle); + float s = sin(angle); + apply(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + + + public void invRotateZ(float angle) { + float c = cos(-angle); + float s = sin(-angle); + preApply(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + + + public void rotate(float angle, float v0, float v1, float v2) { + // should be in radians (i think), instead of degrees (gl uses degrees) + // based on 15-463 code, but similar to opengl ref p.443 + + // TODO should make sure this vector is normalized + + float c = cos(angle); + float s = sin(angle); + float t = 1.0f - c; + + apply((t*v0*v0) + c, (t*v0*v1) - (s*v2), (t*v0*v2) + (s*v1), 0, + (t*v0*v1) + (s*v2), (t*v1*v1) + c, (t*v1*v2) - (s*v0), 0, + (t*v0*v2) - (s*v1), (t*v1*v2) + (s*v0), (t*v2*v2) + c, 0, + 0, 0, 0, 1); + } + + + public void invRotate(float angle, float v0, float v1, float v2) { + // TODO should make sure this vector is normalized + + float c = cos(-angle); + float s = sin(-angle); + float t = 1.0f - c; + + preApply((t*v0*v0) + c, (t*v0*v1) - (s*v2), (t*v0*v2) + (s*v1), 0, + (t*v0*v1) + (s*v2), (t*v1*v1) + c, (t*v1*v2) - (s*v0), 0, + (t*v0*v2) - (s*v1), (t*v1*v2) + (s*v0), (t*v2*v2) + c, 0, + 0, 0, 0, 1); + } + + + public void scale(float s) { + apply(s, 0, 0, 0, 0, s, 0, 0, 0, 0, s, 0, 0, 0, 0, 1); + } + + + public void invScale(float s) { + preApply(1/s, 0, 0, 0, 0, 1/s, 0, 0, 0, 0, 1/s, 0, 0, 0, 0, 1); + } + + + public void scale(float sx, float sy) { + apply(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + + + public void invScale(float sx, float sy) { + preApply(1/sx, 0, 0, 0, 0, 1/sy, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + + + // OPTIMIZE: same as above + public void scale(float x, float y, float z) { + apply(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1); + } + + + public void invScale(float x, float y, float z) { + preApply(1/x, 0, 0, 0, 0, 1/y, 0, 0, 0, 0, 1/z, 0, 0, 0, 0, 1); + } + + + /* + public void transform(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) { + apply(n00, n01, n02, n03, + n10, n11, n12, n13, + n20, n21, n22, n23, + n30, n31, n32, n33); + } + */ + + + public void preApply(PMatrix lhs) { + preApply(lhs.m00, lhs.m01, lhs.m02, lhs.m03, + lhs.m10, lhs.m11, lhs.m12, lhs.m13, + lhs.m20, lhs.m21, lhs.m22, lhs.m23, + lhs.m30, lhs.m31, lhs.m32, lhs.m33); + } + + + // for inverse operations, like multiplying the matrix on the left + public void preApply(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) { + + float r00 = n00*m00 + n01*m10 + n02*m20 + n03*m30; + float r01 = n00*m01 + n01*m11 + n02*m21 + n03*m31; + float r02 = n00*m02 + n01*m12 + n02*m22 + n03*m32; + float r03 = n00*m03 + n01*m13 + n02*m23 + n03*m33; + + float r10 = n10*m00 + n11*m10 + n12*m20 + n13*m30; + float r11 = n10*m01 + n11*m11 + n12*m21 + n13*m31; + float r12 = n10*m02 + n11*m12 + n12*m22 + n13*m32; + float r13 = n10*m03 + n11*m13 + n12*m23 + n13*m33; + + float r20 = n20*m00 + n21*m10 + n22*m20 + n23*m30; + float r21 = n20*m01 + n21*m11 + n22*m21 + n23*m31; + float r22 = n20*m02 + n21*m12 + n22*m22 + n23*m32; + float r23 = n20*m03 + n21*m13 + n22*m23 + n23*m33; + + float r30 = n30*m00 + n31*m10 + n32*m20 + n33*m30; + float r31 = n30*m01 + n31*m11 + n32*m21 + n33*m31; + float r32 = n30*m02 + n31*m12 + n32*m22 + n33*m32; + float r33 = n30*m03 + n31*m13 + n32*m23 + n33*m33; + + m00 = r00; m01 = r01; m02 = r02; m03 = r03; + m10 = r10; m11 = r11; m12 = r12; m13 = r13; + m20 = r20; m21 = r21; m22 = r22; m23 = r23; + m30 = r30; m31 = r31; m32 = r32; m33 = r33; + } + + + public boolean invApply(PMatrix rhs) { + PMatrix copy = new PMatrix(rhs); + PMatrix inverse = copy.invert(); + if (inverse == null) return false; + preApply(inverse); + return true; + } + + + public boolean invApply(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) { + if (inverseCopy == null) { + inverseCopy = new PMatrix(); + } + inverseCopy.set(n00, n01, n02, n03, + n10, n11, n12, n13, + n20, n21, n22, n23, + n30, n31, n32, n33); + PMatrix inverse = inverseCopy.invert(); + if (inverse == null) return false; + preApply(inverse); + return true; + } + + + public void apply(PMatrix rhs) { + apply(rhs.m00, rhs.m01, rhs.m02, rhs.m03, + rhs.m10, rhs.m11, rhs.m12, rhs.m13, + rhs.m20, rhs.m21, rhs.m22, rhs.m23, + rhs.m30, rhs.m31, rhs.m32, rhs.m33); + } + + + public void apply(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) { + + float r00 = m00*n00 + m01*n10 + m02*n20 + m03*n30; + float r01 = m00*n01 + m01*n11 + m02*n21 + m03*n31; + float r02 = m00*n02 + m01*n12 + m02*n22 + m03*n32; + float r03 = m00*n03 + m01*n13 + m02*n23 + m03*n33; + + float r10 = m10*n00 + m11*n10 + m12*n20 + m13*n30; + float r11 = m10*n01 + m11*n11 + m12*n21 + m13*n31; + float r12 = m10*n02 + m11*n12 + m12*n22 + m13*n32; + float r13 = m10*n03 + m11*n13 + m12*n23 + m13*n33; + + float r20 = m20*n00 + m21*n10 + m22*n20 + m23*n30; + float r21 = m20*n01 + m21*n11 + m22*n21 + m23*n31; + float r22 = m20*n02 + m21*n12 + m22*n22 + m23*n32; + float r23 = m20*n03 + m21*n13 + m22*n23 + m23*n33; + + float r30 = m30*n00 + m31*n10 + m32*n20 + m33*n30; + float r31 = m30*n01 + m31*n11 + m32*n21 + m33*n31; + float r32 = m30*n02 + m31*n12 + m32*n22 + m33*n32; + float r33 = m30*n03 + m31*n13 + m32*n23 + m33*n33; + + m00 = r00; m01 = r01; m02 = r02; m03 = r03; + m10 = r10; m11 = r11; m12 = r12; m13 = r13; + m20 = r20; m21 = r21; m22 = r22; m23 = r23; + m30 = r30; m31 = r31; m32 = r32; m33 = r33; + } + + + public void mult3(float vec[], float out[]) { + // must use these temp vars because vec may be the same as out + float tmpx = m00*vec[0] + m01*vec[1] + m02*vec[2] + m03; + float tmpy = m10*vec[0] + m11*vec[1] + m12*vec[2] + m13; + float tmpz = m20*vec[0] + m21*vec[1] + m22*vec[2] + m23; + + out[0] = tmpx; + out[1] = tmpy; + out[2] = tmpz; + } + + + public void mult(float vec[], float out[]) { + // must use these temp vars because vec may be the same as out + float tmpx = m00*vec[0] + m01*vec[1] + m02*vec[2] + m03*vec[3]; + float tmpy = m10*vec[0] + m11*vec[1] + m12*vec[2] + m13*vec[3]; + float tmpz = m20*vec[0] + m21*vec[1] + m22*vec[2] + m23*vec[3]; + float tmpw = m30*vec[0] + m31*vec[1] + m32*vec[2] + m33*vec[3]; + + out[0] = tmpx; + out[1] = tmpy; + out[2] = tmpz; + out[3] = tmpw; + } + + + /** + * @return the determinant of the matrix + */ + public float determinant() { + float f = + m00 + * ((m11 * m22 * m33 + m12 * m23 * m31 + m13 * m21 * m32) + - m13 * m22 * m31 + - m11 * m23 * m32 + - m12 * m21 * m33); + f -= m01 + * ((m10 * m22 * m33 + m12 * m23 * m30 + m13 * m20 * m32) + - m13 * m22 * m30 + - m10 * m23 * m32 + - m12 * m20 * m33); + f += m02 + * ((m10 * m21 * m33 + m11 * m23 * m30 + m13 * m20 * m31) + - m13 * m21 * m30 + - m10 * m23 * m31 + - m11 * m20 * m33); + f -= m03 + * ((m10 * m21 * m32 + m11 * m22 * m30 + m12 * m20 * m31) + - m12 * m21 * m30 + - m10 * m22 * m31 + - m11 * m20 * m32); + return f; + } + + + /** + * Calculate the determinant of a 3x3 matrix + * @return result + */ + private float determinant3x3(float t00, float t01, float t02, + float t10, float t11, float t12, + float t20, float t21, float t22) { + return (t00 * (t11 * t22 - t12 * t21) + + t01 * (t12 * t20 - t10 * t22) + + t02 * (t10 * t21 - t11 * t20)); + } + + + public PMatrix transpose() { + float temp; + temp = m01; m01 = m10; m10 = temp; + temp = m02; m02 = m20; m20 = temp; + temp = m03; m03 = m30; m30 = temp; + temp = m12; m12 = m21; m21 = temp; + temp = m13; m13 = m31; m31 = temp; + temp = m23; m23 = m32; m32 = temp; + return this; + } + + + /** + * Invert this matrix + * @return this if successful, null otherwise + */ + public PMatrix invert() { + + float determinant = determinant(); + + if (determinant != 0) { + // m00 m01 m02 m03 + // m10 m11 m12 m13 + // m20 m21 m22 m23 + // m30 m31 m32 m33 + float determinant_inv = 1f / determinant; + + // first row + float t00 = determinant3x3(m11, m12, m13, m21, m22, m23, m31, m32, m33); + float t01 = -determinant3x3(m10, m12, m13, m20, m22, m23, m30, m32, m33); + float t02 = determinant3x3(m10, m11, m13, m20, m21, m23, m30, m31, m33); + float t03 = -determinant3x3(m10, m11, m12, m20, m21, m22, m30, m31, m32); + + // second row + float t10 = -determinant3x3(m01, m02, m03, m21, m22, m23, m31, m32, m33); + float t11 = determinant3x3(m00, m02, m03, m20, m22, m23, m30, m32, m33); + float t12 = -determinant3x3(m00, m01, m03, m20, m21, m23, m30, m31, m33); + float t13 = determinant3x3(m00, m01, m02, m20, m21, m22, m30, m31, m32); + + // third row + float t20 = determinant3x3(m01, m02, m03, m11, m12, m13, m31, m32, m33); + float t21 = -determinant3x3(m00, m02, m03, m10, m12, m13, m30, m32, m33); + float t22 = determinant3x3(m00, m01, m03, m10, m11, m13, m30, m31, m33); + float t23 = -determinant3x3(m00, m01, m02, m10, m11, m12, m30, m31, m32); + + // fourth row + float t30 = -determinant3x3(m01, m02, m03, m11, m12, m13, m21, m22, m23); + float t31 = determinant3x3(m00, m02, m03, m10, m12, m13, m20, m22, m23); + float t32 = -determinant3x3(m00, m01, m03, m10, m11, m13, m20, m21, m23); + float t33 = determinant3x3(m00, m01, m02, m10, m11, m12, m20, m21, m22); + + // transpose and divide by the determinant + m00 = t00*determinant_inv; + m11 = t11*determinant_inv; + m22 = t22*determinant_inv; + m33 = t33*determinant_inv; + m01 = t10*determinant_inv; + m10 = t01*determinant_inv; + m20 = t02*determinant_inv; + m02 = t20*determinant_inv; + m12 = t21*determinant_inv; + m21 = t12*determinant_inv; + m03 = t30*determinant_inv; + m30 = t03*determinant_inv; + m13 = t31*determinant_inv; + m31 = t13*determinant_inv; + m32 = t23*determinant_inv; + m23 = t32*determinant_inv; + return this; + } + return null; + } + + + ////////////////////////////////////////////////////////////// + + + public void print() { + int big = (int) Math.abs(max(max(max(max(abs(m00), abs(m01)), + max(abs(m02), abs(m03))), + max(max(abs(m10), abs(m11)), + max(abs(m12), abs(m13)))), + max(max(max(abs(m20), abs(m21)), + max(abs(m22), abs(m23))), + max(max(abs(m30), abs(m31)), + max(abs(m32), abs(m33)))))); + + // avoid infinite loop + if (Float.isNaN(big) || Float.isInfinite(big)) { + big = 1000000; // set to something arbitrary + } + + int d = 1; + while ((big /= 10) != 0) d++; // cheap log() + + System.out.println(PApplet.nfs(m00, d, 4) + " " + + PApplet.nfs(m01, d, 4) + " " + + PApplet.nfs(m02, d, 4) + " " + + PApplet.nfs(m03, d, 4)); + + System.out.println(PApplet.nfs(m10, d, 4) + " " + + PApplet.nfs(m11, d, 4) + " " + + PApplet.nfs(m12, d, 4) + " " + + PApplet.nfs(m13, d, 4)); + + System.out.println(PApplet.nfs(m20, d, 4) + " " + + PApplet.nfs(m21, d, 4) + " " + + PApplet.nfs(m22, d, 4) + " " + + PApplet.nfs(m23, d, 4)); + + System.out.println(PApplet.nfs(m30, d, 4) + " " + + PApplet.nfs(m31, d, 4) + " " + + PApplet.nfs(m32, d, 4) + " " + + PApplet.nfs(m33, d, 4)); + + System.out.println(); + } + + + ////////////////////////////////////////////////////////////// + + + private final float max(float a, float b) { + return (a > b) ? 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); + } +} diff --git a/core/PMethods.java b/core/src/processing/core/PMethods.java old mode 100755 new mode 100644 similarity index 96% rename from core/PMethods.java rename to core/src/processing/core/PMethods.java index 70329ca6f..7a518d7b5 --- a/core/PMethods.java +++ b/core/src/processing/core/PMethods.java @@ -1,596 +1,596 @@ -/* -package processing.core; - - - -// this file is semiautomatically generated. no touchy-touchy. - -public interface PMethods { - - public void beginFrame(); - - public void endFrame(); - - public void defaults(); - - public void hint(int which); - - public void unhint(int which); - - // - - public void beginShape(); - - public void beginShape(int kind); - - public void normal(float nx, float ny, float nz); // 3D - - public void textureMode(int mode); // 3D - - public void texture(PImage image); // 3D - - public void vertex(float x, float y); - - public void vertex(float x, float y, float u, float v); // 3D - - public void vertex(float x, float y, float z); // 3D - - public void vertex(float x, float y, float z, // 3D - float u, float v); - - public void bezierVertex(float x1, float y1, - float x2, float y2, - float x3, float y3); - - public void bezierVertex(float x1, float y1, float z1, // 3D - float x2, float y2, float z2, - float x3, float y3, float z3); - - public void curveVertex(float x, float y); - - public void curveVertex(float x, float y, float z); // 3D - - public void endShape(); - - // - - public void point(float x, float y); - - public void point(float x, float y, float z); // 3D - - public void line(float x1, float y1, float x2, float y2); - - public void line(float x1, float y1, float z1, - float x2, float y2, float z2); // 3D - - public void triangle(float x1, float y1, float x2, float y2, - float x3, float y3); - - public void quad(float x1, float y1, float x2, float y2, - float x3, float y3, float x4, float y4); - - // - - public void rectMode(int mode); - - public void rect(float x1, float y1, float x2, float y2); - - //protected void rectImpl(float x1, float y1, float x2, float y2); - - // - - public void ellipseMode(int mode); - - public void ellipse(float x, float y, float hradius, float vradius); - - //protected void ellipseImpl(float x, float y, float hradius, float vradius); - - // - - public void arc(float x, float y, float hr, float vr, - float start, float stop); - - //protected void arcImpl(float start, float stop, - // float x, float y, float hr, float vr); - - // - - // also considered using the modes for box() and sphere(), - // but then decided against because when rotating/scaling/etc - // it's gonna make things totally bizarre. - - // rectMode(CORNER, CORNERS, CENTER, CENTER_DIAMETER - // box(x, y, z, w, h, d) CORNER - // box(x, y, z, x2, y2, z2) CORNERS - // box(x, y, z, w, h, d) CENTER (but centered around x, y, z) - // box(x, y, z, xr, yr, zr) CENTER_RADIUS - - // sphere(x, y, z, r) CENTER - // sphere(x, y, z, r) CORNER draws the ellipse starting int the corner - - public void box(float size); // 3D - - public void box(float w, float h, float d); // 3D - - public void sphereDetail(int res); // 3D - - public void sphere(float r); // 3D - - // - - public float bezierPoint(float a, float b, float c, float d, - float t); - - public float bezierTangent(float a, float b, float c, float d, - float t); - - public void bezierDetail(int detail); - - public void bezier(float x1, float y1, - float x2, float y2, - float x3, float y3, - float x4, float y4); - - 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); // 3D - - // - - public float curvePoint(float a, float b, float c, float d, - float t); - - public float curveTangent(float a, float b, float c, float d, - float t); - - public void curveDetail(int detail); - - public void curveTightness(float tightness); - - public void curve(float x1, float y1, - float x2, float y2, - float x3, float y3, - float x4, float y4); - - 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); // 3D - // - - public void image(PImage image, float x1, float y1); - - public void image(PImage image, - float x1, float y1, float x2, float y2); - - public void image(PImage image, - float x1, float y1, float x2, float y2, - int u1, int v1, int u2, int v2); - - //protected void imageImpl(PImage image, - // float x1, float y1, float x2, float y2, - // int u1, int v1, int u2, int v2); - - // - - public void textAlign(int alignment); - - public float textAscent(); - - public float textDescent(); - - public void textFont(PFont which); - - public void textFont(PFont which, float size); - - public void textLeading(float leading); - - public void textMode(int mode); - - public void textSize(float size); - - public float textWidth(char c); - - public float textWidth(String s); - - // - - public void text(char c, float x, float y); - - public void text(char c, float x, float y, float z); // 3D - - public void text(String s, float x, float y); - - public void text(String s, float x, float y, float z); // 3D - - public void text(String s, float x, float y, float w, float h); - - public void text(String s, float x, float y, float w, float h, // 3D - float z); - - public void text(int num, float x, float y); - - public void text(int num, float x, float y, float z); // 3D - - public void text(float num, float x, float y); - - public void text(float num, float x, float y, float z); // 3D - - //protected void textImpl(char ch, float x, float y, float z); - - //protected void textImplObject(PImage glyph, - // float x1, float y1, float z1, - // float x2, float y2, float z2, - // int u2, int v2); - - //protected void textImplScreen(PImage glyph, - // int xx, int yy, //int x2, int y2, - // int w0, int h0); - - // - - public void translate(float tx, float ty); - - public void translate(float tx, float ty, float tz); // 3D - - public void rotate(float angle); - - public void rotateX(float angle); // 3D - - public void rotateY(float angle); // 3D - - public void rotateZ(float angle); // 3D - - public void rotate(float angle, float v0, float v1, float v2); // 3D - - public void scale(float s); - - public void scale(float sx, float sy); - - public void scale(float sx, float sy, float sz); // 3D - - // - - public void pushMatrix(); - - public void popMatrix(); - - public void resetMatrix(); - - public void applyMatrix(float n00, float n01, float n02, - float n10, float n11, float n12); - - 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); // 3D - - public void loadMatrix(); - - public void printMatrix(); - - // - - public void beginCamera(); // 3D - - public void endCamera(); // 3D - - public void camera(); // 3D - - public void camera(float eyeX, float eyeY, float eyeZ, - float centerX, float centerY, float centerZ, - float upX, float upY, float upZ); // 3D - - public void printCamera(); // 3D - - // - - public void ortho(); // 3D - - public void ortho(float left, float right, // 3D - float bottom, float top, - float near, float far); - - public void perspective(); // 3D - - public void perspective(float fovy, float aspect, - float zNear, float zFar); // 3D - - public void frustum(float left, float right, float bottom, - float top, float znear, float zfar); // 3D - - public void printProjection(); // 3D - - // - - public float screenX(float x, float y); - - public float screenY(float x, float y); - - public float screenX(float x, float y, float z); // 3D - - public float screenY(float x, float y, float z); // 3D - - public float screenZ(float x, float y, float z); // 3D - - public float modelX(float x, float y, float z); // 3D - - public float modelY(float x, float y, float z); // 3D - - public float modelZ(float x, float y, float z); // 3D - - // - - public void colorMode(int mode); - - public void colorMode(int mode, float max); - - public void colorMode(int mode, - float maxX, float maxY, float maxZ); - - public void colorMode(int mode, - float maxX, float maxY, float maxZ, float maxA); - - // - - //protected void colorCalc(float gray); - - //protected void colorCalc(float gray, float alpha); - - //protected void colorCalc(float x, float y, float z); - - //protected void colorCalc(float x, float y, float z, float a); - - //protected void colorCalcARGB(int argb); - - // - - public void strokeWeight(float weight); - - public void strokeJoin(int join); - - public void strokeCap(int cap); - - public void noStroke(); - - public void stroke(int rgb); - - public void stroke(float gray); - - public void stroke(float gray, float alpha); - - public void stroke(float x, float y, float z); - - public void stroke(float x, float y, float z, float a); - - protected void colorStroke(); - - // - - public void noTint(); - - public void tint(int rgb); - - public void tint(float gray); - - public void tint(float gray, float alpha); - - public void tint(float x, float y, float z); - - public void tint(float x, float y, float z, float a); - - //protected void tintFromCalc(); - - // - - public void noFill(); - - public void fill(int rgb); - - public void fill(float gray); - - public void fill(float gray, float alpha); - - public void fill(float x, float y, float z); - - public void fill(float x, float y, float z, float a); - - //protected void fillFromCalc(); - - // - - public void ambient(int rgb); // 3D - - public void ambient(float gray); // 3D - - public void ambient(float x, float y, float z); // 3D - - //protected void ambientFromCalc(); // 3D - - // - - public void specular(int rgb); // 3D - - public void specular(float gray); // 3D - - public void specular(float gray, float alpha); // 3D - - public void specular(float x, float y, float z); // 3D - - public void specular(float x, float y, float z, float a); // 3D - - //protected void specularFromCalc(); // 3D - - public void shininess(float shine); // 3D - - // - - public void emissive(int rgb); // 3D - - public void emissive(float gray); // 3D - - public void emissive(float x, float y, float z); // 3D - - //protected void emissiveFromCalc(); - - // - - public void lights(); // 3D - - public void ambientLight(float red, float green, float blue); // 3D - - public void ambientLight(float red, float green, float blue, // 3D - float x, float y, float z); - - public void directionalLight(float red, float green, float blue, // 3D - float nx, float ny, float nz); - - public void pointLight(float red, float green, float blue, // 3D - float x, float y, float z); - - public void spotLight(float red, float green, float blue, // 3D - float x, float y, float z, - float nx, float ny, float nz, - float angle, float concentration); - - public void lightFalloff(float constant, - float linear, float quadratic); // 3D - - public void lightSpecular(float x, float y, float z); // 3D - - // - - public void background(int rgb); - - public void background(float gray); - - public void background(float x, float y, float z); - - public void background(PImage image); - - //protected void backgroundFromCalc(); - - public void clear(); - - // - - //public final int color(int gray) - - //public final int color(float gray) - - //public final int color(int gray, int alpha) - - //public final int color(float gray, float alpha) - - //public final int color(int x, int y, int z) - - //public final int color(float x, float y, float z) - - //public final int color(int x, int y, int z, int a) - - //public final int color(float x, float y, float z, float a) - - //public final float alpha(int what) - - //public final float red(int what) - - //public final float green(int what) - - //public final float blue(int what) - - //public final float hue(int what) - - //public final float saturation(int what) - - //public final float brightness(int what) - - // - - public void imageMode(int mode); - - public void smooth(); - - public void noSmooth(); - - - /////////////////////////////////////////////////////////// - - // all functions below this point require pixel buffer manipulation - // and generally are wrapped or handled internally with a call to - // loadPixels or updatePixels.. not supported for vector subclasses. - - - public void loadPixels(); - - public void updatePixels(); - - public void updatePixels(int x, int y, int c, int d); - - // - - public int get(int x, int y); - - public PImage get(int x, int y, int c, int d); - - public PImage get(); - - public void set(int x, int y, int argb); - - public void set(int x, int y, PImage image); - - //protected void setImpl(int dx, int dy, int sx, int sy, int sw, int sh, - // PImage src) { - - // - - public void mask(int alpha[]); - - public void mask(PImage alpha); - - // - - public void filter(int kind); - - public void filter(int kind, float param); - - // - - public void copy(int sx1, int sy1, int sx2, int sy2, - int dx1, int dy1, int dx2, int dy2); - - public void copy(PImage src, - int sx1, int sy1, int sx2, int sy2, - int dx1, int dy1, int dx2, int dy2); - - // - - public void blend(int sx, int sy, int dx, int dy, int mode); - - public void blend(PImage src, - int sx, int sy, int dx, int dy, int mode); - - public void blend(int sx1, int sy1, int sx2, int sy2, - int dx1, int dy1, int dx2, int dy2, int mode); - - public void blend(PImage src, - int sx1, int sy1, int sx2, int sy2, - int dx1, int dy1, int dx2, int dy2, int mode); - - // - - //static public boolean saveHeaderTIFF(OutputStream output, - // int width, int height); - - //static public boolean saveTIFF(OutputStream output, int pixels[], - // int width, int height); - - //static public boolean saveHeaderTGA(OutputStream output, - // int width, int height) { - - //static public boolean saveTGA(OutputStream output, int pixels[], - // int width, int height) { - - public void save(String filename); - -*/ +/* +package processing.core; + + + +// this file is semiautomatically generated. no touchy-touchy. + +public interface PMethods { + + public void beginFrame(); + + public void endFrame(); + + public void defaults(); + + public void hint(int which); + + public void unhint(int which); + + // + + public void beginShape(); + + public void beginShape(int kind); + + public void normal(float nx, float ny, float nz); // 3D + + public void textureMode(int mode); // 3D + + public void texture(PImage image); // 3D + + public void vertex(float x, float y); + + public void vertex(float x, float y, float u, float v); // 3D + + public void vertex(float x, float y, float z); // 3D + + public void vertex(float x, float y, float z, // 3D + float u, float v); + + public void bezierVertex(float x1, float y1, + float x2, float y2, + float x3, float y3); + + public void bezierVertex(float x1, float y1, float z1, // 3D + float x2, float y2, float z2, + float x3, float y3, float z3); + + public void curveVertex(float x, float y); + + public void curveVertex(float x, float y, float z); // 3D + + public void endShape(); + + // + + public void point(float x, float y); + + public void point(float x, float y, float z); // 3D + + public void line(float x1, float y1, float x2, float y2); + + public void line(float x1, float y1, float z1, + float x2, float y2, float z2); // 3D + + public void triangle(float x1, float y1, float x2, float y2, + float x3, float y3); + + public void quad(float x1, float y1, float x2, float y2, + float x3, float y3, float x4, float y4); + + // + + public void rectMode(int mode); + + public void rect(float x1, float y1, float x2, float y2); + + //protected void rectImpl(float x1, float y1, float x2, float y2); + + // + + public void ellipseMode(int mode); + + public void ellipse(float x, float y, float hradius, float vradius); + + //protected void ellipseImpl(float x, float y, float hradius, float vradius); + + // + + public void arc(float x, float y, float hr, float vr, + float start, float stop); + + //protected void arcImpl(float start, float stop, + // float x, float y, float hr, float vr); + + // + + // also considered using the modes for box() and sphere(), + // but then decided against because when rotating/scaling/etc + // it's gonna make things totally bizarre. + + // rectMode(CORNER, CORNERS, CENTER, CENTER_DIAMETER + // box(x, y, z, w, h, d) CORNER + // box(x, y, z, x2, y2, z2) CORNERS + // box(x, y, z, w, h, d) CENTER (but centered around x, y, z) + // box(x, y, z, xr, yr, zr) CENTER_RADIUS + + // sphere(x, y, z, r) CENTER + // sphere(x, y, z, r) CORNER draws the ellipse starting int the corner + + public void box(float size); // 3D + + public void box(float w, float h, float d); // 3D + + public void sphereDetail(int res); // 3D + + public void sphere(float r); // 3D + + // + + public float bezierPoint(float a, float b, float c, float d, + float t); + + public float bezierTangent(float a, float b, float c, float d, + float t); + + public void bezierDetail(int detail); + + public void bezier(float x1, float y1, + float x2, float y2, + float x3, float y3, + float x4, float y4); + + 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); // 3D + + // + + public float curvePoint(float a, float b, float c, float d, + float t); + + public float curveTangent(float a, float b, float c, float d, + float t); + + public void curveDetail(int detail); + + public void curveTightness(float tightness); + + public void curve(float x1, float y1, + float x2, float y2, + float x3, float y3, + float x4, float y4); + + 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); // 3D + // + + public void image(PImage image, float x1, float y1); + + public void image(PImage image, + float x1, float y1, float x2, float y2); + + public void image(PImage image, + float x1, float y1, float x2, float y2, + int u1, int v1, int u2, int v2); + + //protected void imageImpl(PImage image, + // float x1, float y1, float x2, float y2, + // int u1, int v1, int u2, int v2); + + // + + public void textAlign(int alignment); + + public float textAscent(); + + public float textDescent(); + + public void textFont(PFont which); + + public void textFont(PFont which, float size); + + public void textLeading(float leading); + + public void textMode(int mode); + + public void textSize(float size); + + public float textWidth(char c); + + public float textWidth(String s); + + // + + public void text(char c, float x, float y); + + public void text(char c, float x, float y, float z); // 3D + + public void text(String s, float x, float y); + + public void text(String s, float x, float y, float z); // 3D + + public void text(String s, float x, float y, float w, float h); + + public void text(String s, float x, float y, float w, float h, // 3D + float z); + + public void text(int num, float x, float y); + + public void text(int num, float x, float y, float z); // 3D + + public void text(float num, float x, float y); + + public void text(float num, float x, float y, float z); // 3D + + //protected void textImpl(char ch, float x, float y, float z); + + //protected void textImplObject(PImage glyph, + // float x1, float y1, float z1, + // float x2, float y2, float z2, + // int u2, int v2); + + //protected void textImplScreen(PImage glyph, + // int xx, int yy, //int x2, int y2, + // int w0, int h0); + + // + + public void translate(float tx, float ty); + + public void translate(float tx, float ty, float tz); // 3D + + public void rotate(float angle); + + public void rotateX(float angle); // 3D + + public void rotateY(float angle); // 3D + + public void rotateZ(float angle); // 3D + + public void rotate(float angle, float v0, float v1, float v2); // 3D + + public void scale(float s); + + public void scale(float sx, float sy); + + public void scale(float sx, float sy, float sz); // 3D + + // + + public void pushMatrix(); + + public void popMatrix(); + + public void resetMatrix(); + + public void applyMatrix(float n00, float n01, float n02, + float n10, float n11, float n12); + + 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); // 3D + + public void loadMatrix(); + + public void printMatrix(); + + // + + public void beginCamera(); // 3D + + public void endCamera(); // 3D + + public void camera(); // 3D + + public void camera(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ); // 3D + + public void printCamera(); // 3D + + // + + public void ortho(); // 3D + + public void ortho(float left, float right, // 3D + float bottom, float top, + float near, float far); + + public void perspective(); // 3D + + public void perspective(float fovy, float aspect, + float zNear, float zFar); // 3D + + public void frustum(float left, float right, float bottom, + float top, float znear, float zfar); // 3D + + public void printProjection(); // 3D + + // + + public float screenX(float x, float y); + + public float screenY(float x, float y); + + public float screenX(float x, float y, float z); // 3D + + public float screenY(float x, float y, float z); // 3D + + public float screenZ(float x, float y, float z); // 3D + + public float modelX(float x, float y, float z); // 3D + + public float modelY(float x, float y, float z); // 3D + + public float modelZ(float x, float y, float z); // 3D + + // + + public void colorMode(int mode); + + public void colorMode(int mode, float max); + + public void colorMode(int mode, + float maxX, float maxY, float maxZ); + + public void colorMode(int mode, + float maxX, float maxY, float maxZ, float maxA); + + // + + //protected void colorCalc(float gray); + + //protected void colorCalc(float gray, float alpha); + + //protected void colorCalc(float x, float y, float z); + + //protected void colorCalc(float x, float y, float z, float a); + + //protected void colorCalcARGB(int argb); + + // + + public void strokeWeight(float weight); + + public void strokeJoin(int join); + + public void strokeCap(int cap); + + public void noStroke(); + + public void stroke(int rgb); + + public void stroke(float gray); + + public void stroke(float gray, float alpha); + + public void stroke(float x, float y, float z); + + public void stroke(float x, float y, float z, float a); + + protected void colorStroke(); + + // + + public void noTint(); + + public void tint(int rgb); + + public void tint(float gray); + + public void tint(float gray, float alpha); + + public void tint(float x, float y, float z); + + public void tint(float x, float y, float z, float a); + + //protected void tintFromCalc(); + + // + + public void noFill(); + + public void fill(int rgb); + + public void fill(float gray); + + public void fill(float gray, float alpha); + + public void fill(float x, float y, float z); + + public void fill(float x, float y, float z, float a); + + //protected void fillFromCalc(); + + // + + public void ambient(int rgb); // 3D + + public void ambient(float gray); // 3D + + public void ambient(float x, float y, float z); // 3D + + //protected void ambientFromCalc(); // 3D + + // + + public void specular(int rgb); // 3D + + public void specular(float gray); // 3D + + public void specular(float gray, float alpha); // 3D + + public void specular(float x, float y, float z); // 3D + + public void specular(float x, float y, float z, float a); // 3D + + //protected void specularFromCalc(); // 3D + + public void shininess(float shine); // 3D + + // + + public void emissive(int rgb); // 3D + + public void emissive(float gray); // 3D + + public void emissive(float x, float y, float z); // 3D + + //protected void emissiveFromCalc(); + + // + + public void lights(); // 3D + + public void ambientLight(float red, float green, float blue); // 3D + + public void ambientLight(float red, float green, float blue, // 3D + float x, float y, float z); + + public void directionalLight(float red, float green, float blue, // 3D + float nx, float ny, float nz); + + public void pointLight(float red, float green, float blue, // 3D + float x, float y, float z); + + public void spotLight(float red, float green, float blue, // 3D + float x, float y, float z, + float nx, float ny, float nz, + float angle, float concentration); + + public void lightFalloff(float constant, + float linear, float quadratic); // 3D + + public void lightSpecular(float x, float y, float z); // 3D + + // + + public void background(int rgb); + + public void background(float gray); + + public void background(float x, float y, float z); + + public void background(PImage image); + + //protected void backgroundFromCalc(); + + public void clear(); + + // + + //public final int color(int gray) + + //public final int color(float gray) + + //public final int color(int gray, int alpha) + + //public final int color(float gray, float alpha) + + //public final int color(int x, int y, int z) + + //public final int color(float x, float y, float z) + + //public final int color(int x, int y, int z, int a) + + //public final int color(float x, float y, float z, float a) + + //public final float alpha(int what) + + //public final float red(int what) + + //public final float green(int what) + + //public final float blue(int what) + + //public final float hue(int what) + + //public final float saturation(int what) + + //public final float brightness(int what) + + // + + public void imageMode(int mode); + + public void smooth(); + + public void noSmooth(); + + + /////////////////////////////////////////////////////////// + + // all functions below this point require pixel buffer manipulation + // and generally are wrapped or handled internally with a call to + // loadPixels or updatePixels.. not supported for vector subclasses. + + + public void loadPixels(); + + public void updatePixels(); + + public void updatePixels(int x, int y, int c, int d); + + // + + public int get(int x, int y); + + public PImage get(int x, int y, int c, int d); + + public PImage get(); + + public void set(int x, int y, int argb); + + public void set(int x, int y, PImage image); + + //protected void setImpl(int dx, int dy, int sx, int sy, int sw, int sh, + // PImage src) { + + // + + public void mask(int alpha[]); + + public void mask(PImage alpha); + + // + + public void filter(int kind); + + public void filter(int kind, float param); + + // + + public void copy(int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2); + + public void copy(PImage src, + int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2); + + // + + public void blend(int sx, int sy, int dx, int dy, int mode); + + public void blend(PImage src, + int sx, int sy, int dx, int dy, int mode); + + public void blend(int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2, int mode); + + public void blend(PImage src, + int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2, int mode); + + // + + //static public boolean saveHeaderTIFF(OutputStream output, + // int width, int height); + + //static public boolean saveTIFF(OutputStream output, int pixels[], + // int width, int height); + + //static public boolean saveHeaderTGA(OutputStream output, + // int width, int height) { + + //static public boolean saveTGA(OutputStream output, int pixels[], + // int width, int height) { + + public void save(String filename); + +*/ diff --git a/core/PPolygon.java b/core/src/processing/core/PPolygon.java similarity index 96% rename from core/PPolygon.java rename to core/src/processing/core/PPolygon.java index 4f4ab8fb1..2a202499e 100644 --- a/core/PPolygon.java +++ b/core/src/processing/core/PPolygon.java @@ -1,755 +1,755 @@ -/* -*- 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]; - } - } -} +/* -*- 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 (y0y1) { - if (y2 SCREEN_HEIGHT) { - return; - } else if (yi0 < 0) { - yi0 = 0; - } - - y2 = y_array[o2]; - int yi2 = (int) (y2 + PIXEL_CENTER); - if (yi2 < 0) { - return; - } else if (yi2 > SCREEN_HEIGHT) { - yi2 = SCREEN_HEIGHT; - } - - // Does the poly actually cross a scanline? - if (yi2 > yi0) { - x0 = x_array[o0]; - x1 = x_array[o1]; - x2 = x_array[o2]; - - // get mid Y and clip it - y1 = y_array[o1]; - int yi1 = (int) (y1 + PIXEL_CENTER); - if (yi1 < 0) - yi1 = 0; - if (yi1 > SCREEN_HEIGHT) - yi1 = SCREEN_HEIGHT; - - // calculate deltas etc. - dx2 = x2 - x0; - dy0 = y1 - y0; - dy2 = y2 - y0; - xadd2 = dx2 / dy2; // xadd for "single" edge - temp = dy0 / dy2; - width = temp * dx2 + x0 - x1; - - // calculate alpha blend interpolation - if (INTERPOLATE_ALPHA) { - a0 = a_array[o0]; - a1 = a_array[o1]; - a2 = a_array[o2]; - da0 = a1-a0; - da2 = a2-a0; - iaadd = (int) ((temp * da2 - da0) / width); // alpha add - } - - // calculate intensity interpolation - if (INTERPOLATE_RGB) { - r0 = r_array[o0]; - r1 = r_array[o1]; - r2 = r_array[o2]; - - g0 = g_array[o0]; - g1 = g_array[o1]; - g2 = g_array[o2]; - - b0 = b_array[o0]; - b1 = b_array[o1]; - b2 = b_array[o2]; - - dr0 = r1-r0; - dg0 = g1-g0; - db0 = b1-b0; - - dr2 = r2-r0; - dg2 = g2-g0; - db2 = b2-b0; - - iradd = (int) ((temp * dr2 - dr0) / width); // r add - igadd = (int) ((temp * dg2 - dg0) / width); // g add - ibadd = (int) ((temp * db2 - db0) / width); // b add - } - - // calculate UV interpolation - if (INTERPOLATE_UV) { - u0 = u_array[o0]; - u1 = u_array[o1]; - u2 = u_array[o2]; - v0 = v_array[o0]; - v1 = v_array[o1]; - v2 = v_array[o2]; - du0 = u1-u0; - dv0 = v1-v0; - du2 = u2-u0; - dv2 = v2-v0; - iuadd = (int) ((temp * du2 - du0) / width); // u add - ivadd = (int) ((temp * dv2 - dv0) / width); // v add - } - - z0 = z_array[o0]; - z1 = z_array[o1]; - z2 = z_array[o2]; - dz0 = z1-z0; - dz2 = z2-z0; - izadd = (temp * dz2 - dz0) / width; - - // draw the upper poly segment if it's visible - if (yi1 > yi0) { - dta = (yi0 + PIXEL_CENTER) - y0; - xadd1 = (x1 - x0) / dy0; - - // we can determine which side is "single" side by comparing left/right edge adds - if (xadd2 > xadd1) { - xleft = x0 + dta * xadd1; - xrght = x0 + dta * xadd2; - zleftadd = dz0 / dy0; - zleft = dta*zleftadd+z0; - - // - if (INTERPOLATE_UV) { - uleftadd = du0 / dy0; - vleftadd = dv0 / dy0; - uleft = dta*uleftadd+u0; - vleft = dta*vleftadd+v0; - } - - // - if (INTERPOLATE_RGB) { - rleftadd = dr0 / dy0; - gleftadd = dg0 / dy0; - bleftadd = db0 / dy0; - rleft = dta*rleftadd+r0; - gleft = dta*gleftadd+g0; - bleft = dta*bleftadd+b0; - } - - // - if (INTERPOLATE_ALPHA) { - aleftadd = da0 / dy0; - aleft = dta*aleftadd+a0; - - if (m_drawFlags == R_ALPHA) { - drawsegment_plain_alpha(xadd1,xadd2, yi0,yi1); - } else if (m_drawFlags == (R_GOURAUD + R_ALPHA)) { - drawsegment_gouraud_alpha(xadd1,xadd2, yi0,yi1); - } else if (m_drawFlags == (R_TEXTURE8 + R_ALPHA)) { - drawsegment_texture8_alpha(xadd1,xadd2, yi0,yi1); - } else if (m_drawFlags == (R_TEXTURE24 + R_ALPHA)) { - drawsegment_texture24_alpha(xadd1,xadd2, yi0,yi1); - } else if (m_drawFlags == (R_TEXTURE32 + R_ALPHA)) { - drawsegment_texture32_alpha(xadd1,xadd2, yi0,yi1); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8 + R_ALPHA)) { - drawsegment_gouraud_texture8_alpha(xadd1,xadd2, yi0,yi1); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24 + R_ALPHA)) { - drawsegment_gouraud_texture24_alpha(xadd1,xadd2, yi0,yi1); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32 + R_ALPHA)) { - drawsegment_gouraud_texture32_alpha(xadd1,xadd2, yi0,yi1); - } - } else { - if (m_drawFlags == 0) { - drawsegment_plain(xadd1,xadd2, yi0,yi1); - } else if (m_drawFlags == R_GOURAUD) { - drawsegment_gouraud(xadd1,xadd2, yi0,yi1); - } else if (m_drawFlags == R_TEXTURE8) { - drawsegment_texture8(xadd1,xadd2, yi0,yi1); - } else if (m_drawFlags == R_TEXTURE24) { - drawsegment_texture24(xadd1,xadd2, yi0,yi1); - } else if (m_drawFlags == R_TEXTURE32) { - drawsegment_texture32(xadd1,xadd2, yi0,yi1); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8)) { - drawsegment_gouraud_texture8(xadd1,xadd2, yi0,yi1); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24)) { - drawsegment_gouraud_texture24(xadd1,xadd2, yi0,yi1); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32)) { - drawsegment_gouraud_texture32(xadd1,xadd2, yi0,yi1); - } - } - m_singleRight = true; - } else { - xleft = x0 + dta * xadd2; - xrght = x0 + dta * xadd1; - zleftadd = dz2 / dy2; - zleft = dta*zleftadd+z0; - // - if (INTERPOLATE_UV) { - uleftadd = du2 / dy2; - vleftadd = dv2 / dy2; - uleft = dta*uleftadd+u0; - vleft = dta*vleftadd+v0; - } - - // - if (INTERPOLATE_RGB) { - rleftadd = dr2 / dy2; - gleftadd = dg2 / dy2; - bleftadd = db2 / dy2; - rleft = dta*rleftadd+r0; - gleft = dta*gleftadd+g0; - bleft = dta*bleftadd+b0; - } - - - if (INTERPOLATE_ALPHA) { - aleftadd = da2 / dy2; - aleft = dta*aleftadd+a0; - - if (m_drawFlags == R_ALPHA) { - drawsegment_plain_alpha(xadd2, xadd1, yi0,yi1); - } else if (m_drawFlags == (R_GOURAUD + R_ALPHA)) { - drawsegment_gouraud_alpha(xadd2, xadd1, yi0,yi1); - } else if (m_drawFlags == (R_TEXTURE8 + R_ALPHA)) { - drawsegment_texture8_alpha(xadd2, xadd1, yi0,yi1); - } else if (m_drawFlags == (R_TEXTURE24 + R_ALPHA)) { - drawsegment_texture24_alpha(xadd2, xadd1, yi0,yi1); - } else if (m_drawFlags == (R_TEXTURE32 + R_ALPHA)) { - drawsegment_texture32_alpha(xadd2, xadd1, yi0,yi1); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8 + R_ALPHA)) { - drawsegment_gouraud_texture8_alpha(xadd2, xadd1, yi0,yi1); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24 + R_ALPHA)) { - drawsegment_gouraud_texture24_alpha(xadd2, xadd1, yi0,yi1); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32 + R_ALPHA)) { - drawsegment_gouraud_texture32_alpha(xadd2, xadd1, yi0,yi1); - } - } else { - if (m_drawFlags == 0) { - drawsegment_plain(xadd2, xadd1, yi0,yi1); - } else if (m_drawFlags == R_GOURAUD) { - drawsegment_gouraud(xadd2, xadd1, yi0,yi1); - } else if (m_drawFlags == R_TEXTURE8) { - drawsegment_texture8(xadd2, xadd1, yi0,yi1); - } else if (m_drawFlags == R_TEXTURE24) { - drawsegment_texture24(xadd2, xadd1, yi0,yi1); - } else if (m_drawFlags == R_TEXTURE32) { - drawsegment_texture32(xadd2, xadd1, yi0,yi1); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8)) { - drawsegment_gouraud_texture8(xadd2, xadd1, yi0,yi1); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24)) { - drawsegment_gouraud_texture24(xadd2, xadd1, yi0,yi1); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32)) { - drawsegment_gouraud_texture32(xadd2, xadd1, yi0,yi1); - } - } - m_singleRight = false; - } - - // if bottom segment height is zero, return - if (yi2 == yi1) - return; - - // calculate xadd 1 - dy1 = y2 - y1; - xadd1 = (x2 - x1) / dy1; - } else { - // top seg height was zero, calculate & clip single edge X - dy1 = y2 - y1; - xadd1 = (x2 - x1) / dy1; - - // which edge is left? - if (xadd2 < xadd1) { - xrght = ((yi1 + PIXEL_CENTER) - y0) * xadd2 + x0; - m_singleRight = true; - } else { - dta = (yi1 + PIXEL_CENTER) - y0; - xleft = dta * xadd2 + x0; - zleftadd = dz2 / dy2; - zleft = dta * zleftadd + z0; - - if (INTERPOLATE_UV) { - uleftadd = du2 / dy2; - vleftadd = dv2 / dy2; - uleft = dta * uleftadd + u0; - vleft = dta * vleftadd + v0; - } - - if (INTERPOLATE_RGB) { - rleftadd = dr2 / dy2; - gleftadd = dg2 / dy2; - bleftadd = db2 / dy2; - rleft = dta * rleftadd + r0; - gleft = dta * gleftadd + g0; - bleft = dta * bleftadd + b0; - } - - // - if (INTERPOLATE_ALPHA) { - aleftadd = da2 / dy2; - aleft = dta * aleftadd + a0; - } - m_singleRight = false; - } - } - - // draw the lower segment - if (m_singleRight) { - dta = (yi1 + PIXEL_CENTER) - y1; - xleft = dta * xadd1 + x1; - zleftadd = (z2 - z1) / dy1; - zleft = dta * zleftadd + z1; - - if (INTERPOLATE_UV) { - uleftadd = (u2 - u1) / dy1; - vleftadd = (v2 - v1) / dy1; - uleft = dta * uleftadd + u1; - vleft = dta * vleftadd + v1; - } - - if (INTERPOLATE_RGB) { - rleftadd = (r2 - r1) / dy1; - gleftadd = (g2 - g1) / dy1; - bleftadd = (b2 - b1) / dy1; - rleft = dta * rleftadd + r1; - gleft = dta * gleftadd + g1; - bleft = dta * bleftadd + b1; - } - - if (INTERPOLATE_ALPHA) { - aleftadd = (a2 - a1) / dy1; - aleft = dta * aleftadd + a1; - - if (m_drawFlags == R_ALPHA) { - drawsegment_plain_alpha(xadd1, xadd2, yi1,yi2); - } else if (m_drawFlags == (R_GOURAUD + R_ALPHA)) { - drawsegment_gouraud_alpha(xadd1, xadd2, yi1,yi2); - } else if (m_drawFlags == (R_TEXTURE8 + R_ALPHA)) { - drawsegment_texture8_alpha(xadd1, xadd2, yi1,yi2); - } else if (m_drawFlags == (R_TEXTURE24 + R_ALPHA)) { - drawsegment_texture24_alpha(xadd1, xadd2, yi1,yi2); - } else if (m_drawFlags == (R_TEXTURE32 + R_ALPHA)) { - drawsegment_texture32_alpha(xadd1, xadd2, yi1,yi2); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8 + R_ALPHA)) { - drawsegment_gouraud_texture8_alpha(xadd1, xadd2, yi1,yi2); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24 + R_ALPHA)) { - drawsegment_gouraud_texture24_alpha(xadd1, xadd2, yi1,yi2); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32 + R_ALPHA)) { - drawsegment_gouraud_texture32_alpha(xadd1, xadd2, yi1,yi2); - } - } else { - if (m_drawFlags == 0) { - drawsegment_plain(xadd1, xadd2, yi1,yi2); - } else if (m_drawFlags == R_GOURAUD) { - drawsegment_gouraud(xadd1, xadd2, yi1,yi2); - } else if (m_drawFlags == R_TEXTURE8) { - drawsegment_texture8(xadd1, xadd2, yi1,yi2); - } else if (m_drawFlags == R_TEXTURE24) { - drawsegment_texture24(xadd1, xadd2, yi1,yi2); - } else if (m_drawFlags == R_TEXTURE32) { - drawsegment_texture32(xadd1, xadd2, yi1,yi2); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8)) { - drawsegment_gouraud_texture8(xadd1, xadd2, yi1,yi2); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24)) { - drawsegment_gouraud_texture24(xadd1, xadd2, yi1,yi2); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32)) { - drawsegment_gouraud_texture32(xadd1, xadd2, yi1,yi2); - } - } - } else { - xrght = ((yi1 + PIXEL_CENTER)- y1) * xadd1 + x1; - - if (INTERPOLATE_ALPHA) { - if (m_drawFlags == R_ALPHA) { - drawsegment_plain_alpha(xadd2, xadd1, yi1,yi2); - } else if (m_drawFlags == (R_GOURAUD + R_ALPHA)) { - drawsegment_gouraud_alpha(xadd2, xadd1, yi1,yi2); - } else if (m_drawFlags == (R_TEXTURE8 + R_ALPHA)) { - drawsegment_texture8_alpha(xadd2, xadd1, yi1,yi2); - } else if (m_drawFlags == (R_TEXTURE24 + R_ALPHA)) { - drawsegment_texture24_alpha(xadd2, xadd1, yi1,yi2); - } else if (m_drawFlags == (R_TEXTURE32 + R_ALPHA)) { - drawsegment_texture32_alpha(xadd2, xadd1, yi1,yi2); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8 + R_ALPHA)) { - drawsegment_gouraud_texture8_alpha(xadd2, xadd1, yi1,yi2); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24 + R_ALPHA)) { - drawsegment_gouraud_texture24_alpha(xadd2, xadd1, yi1,yi2); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32 + R_ALPHA)) { - drawsegment_gouraud_texture32_alpha(xadd2, xadd1, yi1,yi2); - } - } else { - if (m_drawFlags == 0) { - drawsegment_plain(xadd2, xadd1, yi1,yi2); - } else if (m_drawFlags == R_GOURAUD) { - drawsegment_gouraud(xadd2, xadd1, yi1,yi2); - } else if (m_drawFlags == R_TEXTURE8) { - drawsegment_texture8(xadd2, xadd1, yi1,yi2); - } else if (m_drawFlags == R_TEXTURE24) { - drawsegment_texture24(xadd2, xadd1, yi1,yi2); - } else if (m_drawFlags == R_TEXTURE32) { - drawsegment_texture32(xadd2, xadd1, yi1,yi2); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8)) { - drawsegment_gouraud_texture8(xadd2, xadd1, yi1,yi2); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24)) { - drawsegment_gouraud_texture24(xadd2, xadd1, yi1,yi2); - } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32)) { - drawsegment_gouraud_texture32(xadd2, xadd1, yi1,yi2); - } - } - } - } - } - - - - /** - * Plain color - */ - private void drawsegment_plain - ( - float leftadd, - float rghtadd, - int ytop, - int ybottom - ) { - ytop*=SCREEN_WIDTH; - ybottom*=SCREEN_WIDTH; - int f = m_fill; - int p = m_index; - - while (ytop < ybottom) { - int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; - - int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - - float xdiff = (xstart + PIXEL_CENTER) - xleft; - float iz = izadd * xdiff + zleft; - xstart+=ytop; - xend+=ytop; - - for ( ; xstart < xend; xstart++ ) { - if (noDepthTest || (iz <= m_zbuffer[xstart])) { - m_zbuffer[xstart] = iz; - m_pixels[xstart] = f; - m_stencil[xstart] = p; - } - iz+=izadd; - } - - ytop+=SCREEN_WIDTH; - xleft+=leftadd; - xrght+=rghtadd; - zleft+=zleftadd; - } - } - - /** - * Plain color, interpolated alpha - */ - private void drawsegment_plain_alpha - ( - float leftadd, - float rghtadd, - int ytop, - int ybottom - ) { - ytop*=SCREEN_WIDTH; - ybottom*=SCREEN_WIDTH; - - int pr = m_fill & 0xFF0000; - int pg = m_fill & 0xFF00; - int pb = m_fill & 0xFF; - - int p = m_index; - float iaf = iaadd; - - while (ytop < ybottom) { - int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; - - int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - - float xdiff = (xstart + PIXEL_CENTER) - xleft; - float iz = izadd * xdiff + zleft; - int ia = (int) (iaf * xdiff + aleft); - xstart+=ytop; - xend+=ytop; - - for ( ; xstart < xend; xstart++ ) { - if (noDepthTest || (iz <= m_zbuffer[xstart])) { - //m_zbuffer[xstart] = iz; - - int alpha = ia >> 16; - int mr0 = m_pixels[xstart]; - int mg0 = mr0 & 0xFF00; - int mb0 = mr0 & 0xFF; - mr0 &= 0xFF0000; - - mr0 = mr0 + (((pr - mr0) * alpha) >> 8); - mg0 = mg0 + (((pg - mg0) * alpha) >> 8); - mb0 = mb0 + (((pb - mb0) * alpha) >> 8); - m_pixels[xstart] = (mr0 & 0xFF0000) | (mg0 & 0xFF00) | (mb0 & 0xFF); - - m_stencil[xstart] = p; - } - iz += izadd; - ia += iaadd; - } - ytop += SCREEN_WIDTH; - xleft += leftadd; - xrght += rghtadd; - zleft += zleftadd; - } - } - - - /** - * RGB gouraud - */ - private void drawsegment_gouraud - ( - float leftadd, - float rghtadd, - int ytop, - int ybottom - ) { - float irf = iradd; - float igf = igadd; - float ibf = ibadd; - - ytop*=SCREEN_WIDTH; - ybottom*=SCREEN_WIDTH; - int p = m_index; - - while (ytop < ybottom) { - int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; - - int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - - float xdiff = (xstart + PIXEL_CENTER) - xleft; - int ir = (int) (irf * xdiff + rleft); - int ig = (int) (igf * xdiff + gleft); - int ib = (int) (ibf * xdiff + bleft); - float iz = izadd * xdiff + zleft; - - xstart+=ytop; - xend+=ytop; - - for ( ; xstart < xend; xstart++ ) { - if (noDepthTest || (iz <= m_zbuffer[xstart])) { - m_zbuffer[xstart] = iz; - m_pixels[xstart]=((ir & 0xFF0000) | ((ig >> 8) & 0xFF00) | (ib >> 16)); - m_stencil[xstart] = p; - } - - // - ir+=iradd; - ig+=igadd; - ib+=ibadd; - iz+=izadd; - } - - ytop+=SCREEN_WIDTH; - xleft+=leftadd; - xrght+=rghtadd; - rleft+=rleftadd; - gleft+=gleftadd; - bleft+=bleftadd; - zleft+=zleftadd; - } - } - - - /** - * RGB gouraud + interpolated alpha - */ - private void drawsegment_gouraud_alpha - ( - float leftadd, - float rghtadd, - int ytop, - int ybottom - ) { - ytop*=SCREEN_WIDTH; - ybottom*=SCREEN_WIDTH; - int p = m_index; - - float irf = iradd; - float igf = igadd; - float ibf = ibadd; - float iaf = iaadd; - - while (ytop < ybottom) { - int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; - int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - float xdiff = (xstart + PIXEL_CENTER) - xleft; - - int ir = (int) (irf * xdiff + rleft); - int ig = (int) (igf * xdiff + gleft); - int ib = (int) (ibf * xdiff + bleft); - int ia = (int) (iaf * xdiff + aleft); - float iz = izadd * xdiff + zleft; - - xstart+=ytop; - xend+=ytop; - - for ( ; xstart < xend; xstart++ ) { - if (noDepthTest || (iz <= m_zbuffer[xstart])) { - //m_zbuffer[xstart] = iz; - - // - int red = (ir & 0xFF0000); - int grn = (ig >> 8) & 0xFF00; - int blu = (ib >> 16); - - // get buffer pixels - int bb = m_pixels[xstart]; - int br = (bb & 0xFF0000); // 0x00FF0000 - int bg = (bb & 0xFF00); // 0x0000FF00 - bb = (bb & 0xFF); // 0x000000FF - - // blend alpha - int al = ia >> 16; - - // - m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) | ((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); - m_stencil[xstart] = p; - } - - // - ir+=iradd; - ig+=igadd; - ib+=ibadd; - ia+=iaadd; - iz+=izadd; - } - - ytop+=SCREEN_WIDTH; - xleft+=leftadd; - xrght+=rghtadd; - rleft+=rleftadd; - gleft+=gleftadd; - bleft+=bleftadd; - aleft+=aleftadd; - zleft+=zleftadd; - } - } - - - /** - * 8-bit alpha texture - */ - private void drawsegment_texture8 - ( - float leftadd, - float rghtadd, - int ytop, - int ybottom - ) { - ytop*=SCREEN_WIDTH; - ybottom*=SCREEN_WIDTH; - int p = m_index; - - float iuf = iuadd; - float ivf = ivadd; - - int red = m_fill & 0xFF0000; - int grn = m_fill & 0xFF00; - int blu = m_fill & 0xFF; - - while (ytop < ybottom) { - int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; - - int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - - float xdiff = (xstart + PIXEL_CENTER) - xleft; - int iu = (int) (iuf * xdiff + uleft); - int iv = (int) (ivf * xdiff + vleft); - float iz = izadd * xdiff + zleft; - - xstart+=ytop; - xend+=ytop; - - for ( ; xstart < xend; xstart++ ) { - // try-catch just in case pixel offset it out of range - try - { - if (noDepthTest || (iz <= m_zbuffer[xstart])) { - //m_zbuffer[xstart] = iz; - - int al0; - if (m_bilinear) { - int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); - int iui = iu & 0xFFFF; - al0 = m_texture[ofs] & 0xFF; - int al1 = m_texture[ofs + 1] & 0xFF; - ofs+=TEX_WIDTH; - int al2 = m_texture[ofs] & 0xFF; - int al3 = m_texture[ofs + 1] & 0xFF; - al0 = al0 + (((al1-al0) * iui) >> 16); - al2 = al2 + (((al3-al2) * iui) >> 16); - al0 = al0 + (((al2-al0) * (iv & 0xFFFF)) >> 16); - } else { - al0 = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)] & 0xFF; - } - - int br = m_pixels[xstart]; - int bg = (br & 0xFF00); - int bb = (br & 0xFF); - br = (br & 0xFF0000); - m_pixels[xstart] = ((br + (((red - br) * al0) >> 8)) & 0xFF0000) | ((bg + (((grn - bg) * al0) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al0) >> 8)) & 0xFF); - m_stencil[xstart] = p; - } - } - catch (Exception e) { - } - iu+=iuadd; - iv+=ivadd; - iz+=izadd; - } - - ytop+=SCREEN_WIDTH; - - xleft+=leftadd; - xrght+=rghtadd; - uleft+=uleftadd; - vleft+=vleftadd; - zleft+=zleftadd; - } - } - - - /** - * 8-bit texutre + alpha - */ - private void drawsegment_texture8_alpha - ( - float leftadd, - float rghtadd, - int ytop, - int ybottom - ) { - ytop*=SCREEN_WIDTH; - ybottom*=SCREEN_WIDTH; - int p = m_index; - - float iuf = iuadd; - float ivf = ivadd; - float iaf = iaadd; - - int red = m_fill & 0xFF0000; - int grn = m_fill & 0xFF00; - int blu = m_fill & 0xFF; - - while (ytop < ybottom) { - int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; - - int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - - float xdiff = (xstart + PIXEL_CENTER) - xleft; - int iu = (int) (iuf * xdiff + uleft); - int iv = (int) (ivf * xdiff + vleft); - int ia = (int) (iaf * xdiff + aleft); - float iz = izadd * xdiff + zleft; - - xstart+=ytop; - xend+=ytop; - - for ( ; xstart < xend; xstart++ ) { - // try-catch just in case pixel offset it out of range - try - { - if (noDepthTest || (iz <= m_zbuffer[xstart])) { - //m_zbuffer[xstart] = iz; - - int al0; - if (m_bilinear) { - int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); - int iui = iu & 0xFFFF; - al0 = m_texture[ofs] & 0xFF; - int al1 = m_texture[ofs + 1] & 0xFF; - ofs+=TEX_WIDTH; - int al2 = m_texture[ofs] & 0xFF; - int al3 = m_texture[ofs + 1] & 0xFF; - al0 = al0 + (((al1-al0) * iui) >> 16); - al2 = al2 + (((al3-al2) * iui) >> 16); - al0 = al0 + (((al2-al0) * (iv & 0xFFFF)) >> 16); - } else { - al0 = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)] & 0xFF; - } - al0 = (al0 * (ia >> 16)) >> 8; - - int br = m_pixels[xstart]; - int bg = (br & 0xFF00); - int bb = (br & 0xFF); - br = (br & 0xFF0000); - m_pixels[xstart] = ((br + (((red - br) * al0) >> 8)) & 0xFF0000) | ((bg + (((grn - bg) * al0) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al0) >> 8)) & 0xFF); - m_stencil[xstart] = p; - } - } - catch (Exception e) { - } - iu+=iuadd; - iv+=ivadd; - iz+=izadd; - ia+=iaadd; - } - - ytop+=SCREEN_WIDTH; - xleft+=leftadd; - xrght+=rghtadd; - uleft+=uleftadd; - vleft+=vleftadd; - zleft+=zleftadd; - aleft+=aleftadd; - } - } - - - /** - * Plain 24-bit texutre - */ - private void drawsegment_texture24 - ( - float leftadd, - float rghtadd, - int ytop, - int ybottom - ) { - ytop*=SCREEN_WIDTH; - ybottom*=SCREEN_WIDTH; - int p = m_index; - - float iuf = iuadd; - float ivf = ivadd; - - while (ytop < ybottom) { - int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; - - int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - - float xdiff = (xstart + PIXEL_CENTER) - xleft; - int iu = (int) (iuf * xdiff + uleft); - int iv = (int) (ivf * xdiff + vleft); - float iz = izadd * xdiff + zleft; - - xstart+=ytop; - xend+=ytop; - - for ( ; xstart < xend; xstart++ ) { - // try-catch just in case pixel offset it out of range - try - { - if (noDepthTest || (iz <= m_zbuffer[xstart])) { - m_zbuffer[xstart] = iz; - if (m_bilinear) { - int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); - int iui = (iu & 0xFFFF) >> 9; - int ivi = (iv & 0xFFFF) >> 9; - - // get texture pixels - int pix0 = m_texture[ofs]; - int pix1 = m_texture[ofs + 1]; - ofs+=TEX_WIDTH; - int pix2 = m_texture[ofs]; - int pix3 = m_texture[ofs + 1]; - - // red - int red0 = (pix0 & 0xFF0000); - int red2 = (pix2 & 0xFF0000); - int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7); - int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7); - int red = up + (((dn-up) * ivi) >> 7); - - // grn - red0 = (pix0 & 0xFF00); - red2 = (pix2 & 0xFF00); - up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7); - dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7); - int grn = up + (((dn-up) * ivi) >> 7); - - // blu - red0 = (pix0 & 0xFF); - red2 = (pix2 & 0xFF); - up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7); - dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); - int blu = up + (((dn-up) * ivi) >> 7); - - // - m_pixels[xstart] = (red & 0xFF0000) | (grn & 0xFF00) | (blu & 0xFF); - } else { - m_pixels[xstart] = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; - } - m_stencil[xstart] = p; - } - } - catch (Exception e) { - - } - iu+=iuadd; - iv+=ivadd; - iz+=izadd; - } - - ytop+=SCREEN_WIDTH; - xleft+=leftadd; - xrght+=rghtadd; - uleft+=uleftadd; - vleft+=vleftadd; - zleft+=zleftadd; - } - } - - /** - * Alpha 24-bit texutre - */ - private void drawsegment_texture24_alpha - ( - float leftadd, - float rghtadd, - int ytop, - int ybottom - ) { - ytop*=SCREEN_WIDTH; - ybottom*=SCREEN_WIDTH; - int p = m_index; - - float iuf = iuadd; - float ivf = ivadd; - float iaf = iaadd; - - while (ytop < ybottom) { - int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; - - int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - - float xdiff = (xstart + PIXEL_CENTER) - xleft; - int iu = (int) (iuf * xdiff + uleft); - int iv = (int) (ivf * xdiff + vleft); - int ia = (int) (iaf * xdiff + aleft); - float iz = izadd * xdiff + zleft; - - xstart+=ytop; - xend+=ytop; - - for ( ; xstart < xend; xstart++ ) { - // try-catch just in case pixel offset it out of range - try - { - if (noDepthTest || (iz <= m_zbuffer[xstart])) { - //m_zbuffer[xstart] = iz; - - // get alpha - int al = ia >> 16; - - if (m_bilinear) { - int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); - int iui = (iu & 0xFFFF) >> 9; - int ivi = (iv & 0xFFFF) >> 9; - - // get texture pixels - int pix0 = m_texture[ofs]; - int pix1 = m_texture[ofs + 1]; - ofs+=TEX_WIDTH; - int pix2 = m_texture[ofs]; - int pix3 = m_texture[ofs + 1]; - - // red - int red0 = (pix0 & 0xFF0000); - int red2 = (pix2 & 0xFF0000); - int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7); - int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7); - int red = up + (((dn-up) * ivi) >> 7); - - // grn - red0 = (pix0 & 0xFF00); - red2 = (pix2 & 0xFF00); - up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7); - dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7); - int grn = up + (((dn-up) * ivi) >> 7); - - // blu - red0 = (pix0 & 0xFF); - red2 = (pix2 & 0xFF); - up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7); - dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); - int blu = up + (((dn-up) * ivi) >> 7); - - // get buffer pixels - int bb = m_pixels[xstart]; - int br = (bb & 0xFF0000); // 0x00FF0000 - int bg = (bb & 0xFF00); // 0x0000FF00 - bb = (bb & 0xFF); // 0x000000FF - m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |( (bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); - } else { - int red = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; - int grn = red & 0xFF00; - int blu = red & 0xFF; - red&=0xFF0000; - - // get buffer pixels - int bb = m_pixels[xstart]; - int br = (bb & 0xFF0000); // 0x00FF0000 - int bg = (bb & 0xFF00); // 0x0000FF00 - bb = (bb & 0xFF); // 0x000000FF - m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); - } - m_stencil[xstart] = p; - } - } - catch (Exception e) { - } - iu+=iuadd; - iv+=ivadd; - ia+=iaadd; - iz+=izadd; - } - - ytop+=SCREEN_WIDTH; - - xleft+=leftadd; - xrght+=rghtadd; - uleft+=uleftadd; - vleft+=vleftadd; - zleft+=zleftadd; - aleft+=aleftadd; - } - } - - /** - * Plain 32-bit texutre - */ - private void drawsegment_texture32 - ( - float leftadd, - float rghtadd, - int ytop, - int ybottom - ) { - ytop*=SCREEN_WIDTH; - ybottom*=SCREEN_WIDTH; - int p = m_index; - - float iuf = iuadd; - float ivf = ivadd; - - while (ytop < ybottom) { - int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; - - int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - - float xdiff = (xstart + PIXEL_CENTER) - xleft; - int iu = (int) (iuf * xdiff + uleft); - int iv = (int) (ivf * xdiff + vleft); - float iz = izadd * xdiff + zleft; - - xstart+=ytop; - xend+=ytop; - - for ( ; xstart < xend; xstart++ ) { - // try-catch just in case pixel offset it out of range - try - { - if (noDepthTest || (iz <= m_zbuffer[xstart])) { - //m_zbuffer[xstart] = iz; - - if (m_bilinear) { - int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); - int iui = (iu & 0xFFFF) >> 9; - int ivi = (iv & 0xFFFF) >> 9; - - // get texture pixels - int pix0 = m_texture[ofs]; - int pix1 = m_texture[ofs + 1]; - ofs+=TEX_WIDTH; - int pix2 = m_texture[ofs]; - int pix3 = m_texture[ofs + 1]; - - // red - int red0 = (pix0 & 0xFF0000); - int red2 = (pix2 & 0xFF0000); - int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7); - int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7); - int red = up + (((dn-up) * ivi) >> 7); - - // grn - red0 = (pix0 & 0xFF00); - red2 = (pix2 & 0xFF00); - up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7); - dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7); - int grn = up + (((dn-up) * ivi) >> 7); - - // blu - red0 = (pix0 & 0xFF); - red2 = (pix2 & 0xFF); - up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7); - dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); - int blu = up + (((dn-up) * ivi) >> 7); - - // alpha - pix0>>>=24; - pix2>>>=24; - up = pix0 + ((((pix1 >>> 24) - pix0) * iui) >> 7); - dn = pix2 + ((((pix3 >>> 24) - pix2) * iui) >> 7); - int al = up + (((dn-up) * ivi) >> 7); - - // get buffer pixels - int bb = m_pixels[xstart]; - int br = (bb & 0xFF0000); // 0x00FF0000 - int bg = (bb & 0xFF00); // 0x0000FF00 - bb = (bb & 0xFF); // 0x000000FF - m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); - } else { - int red = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; - int al = red >>> 24; - int grn = red & 0xFF00; - int blu = red & 0xFF; - red&=0xFF0000; - - // get buffer pixels - int bb = m_pixels[xstart]; - int br = (bb & 0xFF0000); // 0x00FF0000 - int bg = (bb & 0xFF00); // 0x0000FF00 - bb = (bb & 0xFF); // 0x000000FF - m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); - } - m_stencil[xstart] = p; - } - } - catch (Exception e) { - } - iu+=iuadd; - iv+=ivadd; - iz+=izadd; - } - - ytop+=SCREEN_WIDTH; - - xleft+=leftadd; - xrght+=rghtadd; - uleft+=uleftadd; - vleft+=vleftadd; - zleft+=zleftadd; - aleft+=aleftadd; - } - - - } - - /** - * Alpha 24-bit texutre - */ - private void drawsegment_texture32_alpha - ( - float leftadd, - float rghtadd, - int ytop, - int ybottom - ) { - - ytop*=SCREEN_WIDTH; - ybottom*=SCREEN_WIDTH; - int p = m_index; - - float iuf = iuadd; - float ivf = ivadd; - float iaf = iaadd; - - while (ytop < ybottom) { - int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; - - int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - - float xdiff = (xstart + PIXEL_CENTER) - xleft; - int iu = (int) (iuf * xdiff + uleft); - int iv = (int) (ivf * xdiff + vleft); - int ia = (int) (iaf * xdiff + aleft); - float iz = izadd * xdiff + zleft; - - xstart+=ytop; - xend+=ytop; - - for ( ; xstart < xend; xstart++ ) { - // try-catch just in case pixel offset it out of range - try - { - if (noDepthTest || (iz <= m_zbuffer[xstart])) { - //m_zbuffer[xstart] = iz; - - // get alpha - int al = ia >> 16; - - if (m_bilinear) { - int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); - int iui = (iu & 0xFFFF) >> 9; - int ivi = (iv & 0xFFFF) >> 9; - - // get texture pixels - int pix0 = m_texture[ofs]; - int pix1 = m_texture[ofs + 1]; - ofs+=TEX_WIDTH; - int pix2 = m_texture[ofs]; - int pix3 = m_texture[ofs + 1]; - - // red - int red0 = (pix0 & 0xFF0000); - int red2 = (pix2 & 0xFF0000); - int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7); - int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7); - int red = up + (((dn-up) * ivi) >> 7); - - // grn - red0 = (pix0 & 0xFF00); - red2 = (pix2 & 0xFF00); - up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7); - dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7); - int grn = up + (((dn-up) * ivi) >> 7); - - // blu - red0 = (pix0 & 0xFF); - red2 = (pix2 & 0xFF); - up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7); - dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); - int blu = up + (((dn-up) * ivi) >> 7); - - // alpha - pix0>>>=24; - pix2>>>=24; - up = pix0 + ((((pix1 >>> 24) - pix0) * iui) >> 7); - dn = pix2 + ((((pix3 >>> 24) - pix2) * iui) >> 7); - al = al * (up + (((dn-up) * ivi) >> 7)) >> 8; - - // get buffer pixels - int bb = m_pixels[xstart]; - int br = (bb & 0xFF0000); // 0x00FF0000 - int bg = (bb & 0xFF00); // 0x0000FF00 - bb = (bb & 0xFF); // 0x000000FF - m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); - } else { - int red = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; - al = al * (red >>> 24) >> 8; - int grn = red & 0xFF00; - int blu = red & 0xFF; - red&=0xFF0000; - - // get buffer pixels - int bb = m_pixels[xstart]; - int br = (bb & 0xFF0000); // 0x00FF0000 - int bg = (bb & 0xFF00); // 0x0000FF00 - bb = (bb & 0xFF); // 0x000000FF - m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); - } - m_stencil[xstart] = p; - } - } - catch (Exception e) { - } - iu+=iuadd; - iv+=ivadd; - ia+=iaadd; - iz+=izadd; - } - - ytop+=SCREEN_WIDTH; - - xleft+=leftadd; - xrght+=rghtadd; - uleft+=uleftadd; - vleft+=vleftadd; - zleft+=zleftadd; - aleft+=aleftadd; - } - - - } - - - /** - * Gouraud blended with 8-bit alpha texture - */ - private void drawsegment_gouraud_texture8 - ( - float leftadd, - float rghtadd, - int ytop, - int ybottom - ) { - ytop*=SCREEN_WIDTH; - ybottom*=SCREEN_WIDTH; - int p = m_index; - - float iuf = iuadd; - float ivf = ivadd; - float irf = iradd; - float igf = igadd; - float ibf = ibadd; - - while (ytop < ybottom) { - int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; - int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - float xdiff = (xstart + PIXEL_CENTER) - xleft; - - int iu = (int) (iuf * xdiff + uleft); - int iv = (int) (ivf * xdiff + vleft); - int ir = (int) (irf * xdiff + rleft); - int ig = (int) (igf * xdiff + gleft); - int ib = (int) (ibf * xdiff + bleft); - float iz = izadd * xdiff + zleft; - - xstart+=ytop; - xend+=ytop; - - for ( ; xstart < xend; xstart++ ) { - try - { - if (noDepthTest || (iz <= m_zbuffer[xstart])) { - //m_zbuffer[xstart] = iz; - - int al0; - if (m_bilinear) { - int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); - int iui = iu & 0xFFFF; - al0 = m_texture[ofs] & 0xFF; - int al1 = m_texture[ofs + 1] & 0xFF; - ofs+=TEX_WIDTH; - int al2 = m_texture[ofs] & 0xFF; - int al3 = m_texture[ofs + 1] & 0xFF; - al0 = al0 + (((al1-al0) * iui) >> 16); - al2 = al2 + (((al3-al2) * iui) >> 16); - al0 = al0 + (((al2-al0) * (iv & 0xFFFF)) >> 16); - } else { - al0 = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)] & 0xFF; - } - - // get RGB colors - int red = ir & 0xFF0000; - int grn = (ig >> 8) & 0xFF00; - int blu = (ib >> 16); - - // get buffer pixels - int bb = m_pixels[xstart]; - int br = (bb & 0xFF0000); // 0x00FF0000 - int bg = (bb & 0xFF00); // 0x0000FF00 - bb = (bb & 0xFF); // 0x000000FF - m_pixels[xstart] = ((br + (((red - br) * al0) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al0) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al0) >> 8)) & 0xFF); - - // write stencil - m_stencil[xstart] = p; - } - } - catch (Exception e) { - - } - - // - iu+=iuadd; - iv+=ivadd; - ir+=iradd; - ig+=igadd; - ib+=ibadd; - iz+=izadd; - } - - ytop+=SCREEN_WIDTH; - xleft+=leftadd; - xrght+=rghtadd; - - uleft+=uleftadd; - vleft+=vleftadd; - rleft+=rleftadd; - gleft+=gleftadd; - bleft+=bleftadd; - zleft+=zleftadd; - } - } - - - /** - * Texture multiplied with gouraud - */ - private void drawsegment_gouraud_texture8_alpha - ( - float leftadd, - float rghtadd, - int ytop, - int ybottom - ) { - ytop*=SCREEN_WIDTH; - ybottom*=SCREEN_WIDTH; - int p = m_index; - - float iuf = iuadd; - float ivf = ivadd; - float irf = iradd; - float igf = igadd; - float ibf = ibadd; - float iaf = iaadd; - - while (ytop < ybottom) { - int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; - int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - float xdiff = (xstart + PIXEL_CENTER) - xleft; - - int iu = (int) (iuf * xdiff + uleft); - int iv = (int) (ivf * xdiff + vleft); - int ir = (int) (irf * xdiff + rleft); - int ig = (int) (igf * xdiff + gleft); - int ib = (int) (ibf * xdiff + bleft); - int ia = (int) (iaf * xdiff + aleft); - float iz = izadd * xdiff + zleft; - - xstart+=ytop; - xend+=ytop; - - for ( ; xstart < xend; xstart++ ) { - try - { - if (noDepthTest || (iz <= m_zbuffer[xstart])) { - //m_zbuffer[xstart] = iz; - - int al0; - if (m_bilinear) { - int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); - int iui = iu & 0xFFFF; - al0 = m_texture[ofs] & 0xFF; - int al1 = m_texture[ofs + 1] & 0xFF; - ofs+=TEX_WIDTH; - int al2 = m_texture[ofs] & 0xFF; - int al3 = m_texture[ofs + 1] & 0xFF; - al0 = al0 + (((al1-al0) * iui) >> 16); - al2 = al2 + (((al3-al2) * iui) >> 16); - al0 = al0 + (((al2-al0) * (iv & 0xFFFF)) >> 16); - } else { - al0 = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)] & 0xFF; - } - al0 = (al0 * (ia >> 16)) >> 8; - - // get RGB colors - int red = ir & 0xFF0000; - int grn = (ig >> 8) & 0xFF00; - int blu = (ib >> 16); - - // get buffer pixels - int bb = m_pixels[xstart]; - int br = (bb & 0xFF0000); // 0x00FF0000 - int bg = (bb & 0xFF00); // 0x0000FF00 - bb = (bb & 0xFF); // 0x000000FF - m_pixels[xstart] = ((br + (((red - br) * al0) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al0) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al0) >> 8)) & 0xFF); - - // write stencil - m_stencil[xstart] = p; - } - } - catch (Exception e) { - - } - - // - iu+=iuadd; - iv+=ivadd; - ir+=iradd; - ig+=igadd; - ib+=ibadd; - ia+=iaadd; - iz+=izadd; - } - - ytop+=SCREEN_WIDTH; - xleft+=leftadd; - xrght+=rghtadd; - uleft+=uleftadd; - vleft+=vleftadd; - rleft+=rleftadd; - gleft+=gleftadd; - bleft+=bleftadd; - aleft+=aleftadd; - zleft+=zleftadd; - } - } - - - /** - * Texture multiplied with gouraud - */ - private void drawsegment_gouraud_texture24 - ( - float leftadd, - float rghtadd, - int ytop, - int ybottom - ) { - ytop*=SCREEN_WIDTH; - ybottom*=SCREEN_WIDTH; - int p = m_index; - - float iuf = iuadd; - float ivf = ivadd; - float irf = iradd; - float igf = igadd; - float ibf = ibadd; - - while (ytop < ybottom) { - int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; - int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - float xdiff = (xstart + PIXEL_CENTER) - xleft; - - int iu = (int) (iuf * xdiff + uleft); - int iv = (int) (ivf * xdiff + vleft); - int ir = (int) (irf * xdiff + rleft); - int ig = (int) (igf * xdiff + gleft); - int ib = (int) (ibf * xdiff + bleft); - float iz = izadd * xdiff + zleft; - - xstart+=ytop; - xend+=ytop; - - for ( ; xstart < xend; xstart++ ) { - try - { - if (noDepthTest || (iz <= m_zbuffer[xstart])) { - m_zbuffer[xstart] = iz; - - int red; - int grn; - int blu; - - if (m_bilinear) { - int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); - int iui = (iu & 0xFFFF) >> 9; - int ivi = (iv & 0xFFFF) >> 9; - - // get texture pixels - int pix0 = m_texture[ofs]; - int pix1 = m_texture[ofs + 1]; - ofs+=TEX_WIDTH; - int pix2 = m_texture[ofs]; - int pix3 = m_texture[ofs + 1]; - - // red - int red0 = (pix0 & 0xFF0000); - int red2 = (pix2 & 0xFF0000); - int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7); - int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7); - red = up + (((dn-up) * ivi) >> 7); - - // grn - red0 = (pix0 & 0xFF00); - red2 = (pix2 & 0xFF00); - up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7); - dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7); - grn = up + (((dn-up) * ivi) >> 7); - - // blu - red0 = (pix0 & 0xFF); - red2 = (pix2 & 0xFF); - up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7); - dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); - blu = up + (((dn-up) * ivi) >> 7); - } else { - // get texture pixel color - blu = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; - red = (blu & 0xFF0000); - grn = (blu & 0xFF00); - blu = blu & 0xFF; - } - - // - int r = (ir >> 16); - int g = (ig >> 16); - int b = (ib >> 16); - - // - m_pixels[xstart] = ( ((red * r) & 0xFF000000) | ((grn * g) & 0xFF0000) | (blu * b) ) >> 8; - m_stencil[xstart] = p; - } - } - catch (Exception e) { - } - - // - iu+=iuadd; - iv+=ivadd; - ir+=iradd; - ig+=igadd; - ib+=ibadd; - iz+=izadd; - } - - ytop+=SCREEN_WIDTH; - xleft+=leftadd; - xrght+=rghtadd; - uleft+=uleftadd; - vleft+=vleftadd; - rleft+=rleftadd; - gleft+=gleftadd; - bleft+=bleftadd; - zleft+=zleftadd; - } - } - - - /** - * Gouraud*texture blended with interpolating alpha - */ - private void drawsegment_gouraud_texture24_alpha - ( - float leftadd, - float rghtadd, - int ytop, - int ybottom - ) { - ytop*=SCREEN_WIDTH; - ybottom*=SCREEN_WIDTH; - int p = m_index; - - float iuf = iuadd; - float ivf = ivadd; - float irf = iradd; - float igf = igadd; - float ibf = ibadd; - float iaf = iaadd; - - while (ytop < ybottom) { - int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; - int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - float xdiff = (xstart + PIXEL_CENTER) - xleft; - - int iu = (int) (iuf * xdiff + uleft); - int iv = (int) (ivf * xdiff + vleft); - int ir = (int) (irf * xdiff + rleft); - int ig = (int) (igf * xdiff + gleft); - int ib = (int) (ibf * xdiff + bleft); - int ia = (int) (iaf * xdiff + aleft); - float iz = izadd * xdiff + zleft; - - xstart+=ytop; - xend+=ytop; - - for ( ;xstart < xend; xstart++ ) { - // get texture pixel color - try - { - //if (iz < m_zbuffer[xstart]) { - if (noDepthTest || (iz <= m_zbuffer[xstart])) { // [fry 041114] - //m_zbuffer[xstart] = iz; - - // blend - int al = ia >> 16; - - int red; - int grn; - int blu; - - if (m_bilinear) { - int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); - int iui = (iu & 0xFFFF) >> 9; - int ivi = (iv & 0xFFFF) >> 9; - - // get texture pixels - int pix0 = m_texture[ofs]; - int pix1 = m_texture[ofs + 1]; - ofs+=TEX_WIDTH; - int pix2 = m_texture[ofs]; - int pix3 = m_texture[ofs + 1]; - - // red - int red0 = (pix0 & 0xFF0000); - int red2 = (pix2 & 0xFF0000); - int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7); - int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7); - red = (up + (((dn-up) * ivi) >> 7)) >> 16; - - // grn - red0 = (pix0 & 0xFF00); - red2 = (pix2 & 0xFF00); - up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7); - dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7); - grn = (up + (((dn-up) * ivi) >> 7)) >> 8; - - // blu - red0 = (pix0 & 0xFF); - red2 = (pix2 & 0xFF); - up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7); - dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); - blu = up + (((dn-up) * ivi) >> 7); - } else { - blu = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; - red = (blu & 0xFF0000) >> 16; // 0 - 255 - grn = (blu & 0xFF00) >> 8; // 0 - 255 - blu = (blu & 0xFF); // 0 - 255 - } - - // multiply with gouraud color - red = (red * ir) >>> 8; // 0x00FF???? - grn = (grn * ig) >>> 16; // 0x0000FF?? - blu = (blu * ib) >>> 24; // 0x000000FF - - // get buffer pixels - int bb = m_pixels[xstart]; - int br = (bb & 0xFF0000); // 0x00FF0000 - int bg = (bb & 0xFF00); // 0x0000FF00 - bb = (bb & 0xFF); // 0x000000FF - - // - m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); - m_stencil[xstart] = p; - } - } - catch (Exception e) { - } - - // - iu+=iuadd; - iv+=ivadd; - ir+=iradd; - ig+=igadd; - ib+=ibadd; - ia+=iaadd; - iz+=izadd; - } - - ytop+=SCREEN_WIDTH; - xleft+=leftadd; - xrght+=rghtadd; - uleft+=uleftadd; - vleft+=vleftadd; - rleft+=rleftadd; - gleft+=gleftadd; - bleft+=bleftadd; - aleft+=aleftadd; - zleft+=zleftadd; - } - } - - - /** - * Gouraud*texture blended with interpolating alpha - */ - private void drawsegment_gouraud_texture32 - ( - float leftadd, - float rghtadd, - int ytop, - int ybottom - ) { - ytop*=SCREEN_WIDTH; - ybottom*=SCREEN_WIDTH; - //int p = m_index; - - float iuf = iuadd; - float ivf = ivadd; - float irf = iradd; - float igf = igadd; - float ibf = ibadd; - - while (ytop < ybottom) { - int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; - int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - float xdiff = (xstart + PIXEL_CENTER) - xleft; - - int iu = (int) (iuf * xdiff + uleft); - int iv = (int) (ivf * xdiff + vleft); - int ir = (int) (irf * xdiff + rleft); - int ig = (int) (igf * xdiff + gleft); - int ib = (int) (ibf * xdiff + bleft); - float iz = izadd * xdiff + zleft; - - xstart+=ytop; - xend+=ytop; - - for ( ; xstart < xend; xstart++ ) { - try - { - if (noDepthTest || (iz <= m_zbuffer[xstart])) { - //m_zbuffer[xstart] = iz; - - int red; - int grn; - int blu; - int al; - - if (m_bilinear) { - int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); - int iui = (iu & 0xFFFF) >> 9; - int ivi = (iv & 0xFFFF) >> 9; - - // get texture pixels - int pix0 = m_texture[ofs]; - int pix1 = m_texture[ofs + 1]; - ofs+=TEX_WIDTH; - int pix2 = m_texture[ofs]; - int pix3 = m_texture[ofs + 1]; - - // red - int red0 = (pix0 & 0xFF0000); - int red2 = (pix2 & 0xFF0000); - int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7); - int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7); - red = (up + (((dn-up) * ivi) >> 7)) >> 16; - - // grn - red0 = (pix0 & 0xFF00); - red2 = (pix2 & 0xFF00); - up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7); - dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7); - grn = (up + (((dn-up) * ivi) >> 7)) >> 8; - - // blu - red0 = (pix0 & 0xFF); - red2 = (pix2 & 0xFF); - up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7); - dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); - blu = up + (((dn-up) * ivi) >> 7); - - // alpha - pix0>>>=24; - pix2>>>=24; - up = pix0 + ((((pix1 >>> 24) - pix0) * iui) >> 7); - dn = pix2 + ((((pix3 >>> 24) - pix2) * iui) >> 7); - al = up + (((dn-up) * ivi) >> 7); - } else { - // get texture pixel color - blu = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; - al = (blu >>> 24); - red = (blu & 0xFF0000) >> 16; - grn = (blu & 0xFF00) >> 8; - blu = blu & 0xFF; - } - - // multiply with gouraud color - red = (red * ir) >>> 8; // 0x00FF???? - grn = (grn * ig) >>> 16; // 0x0000FF?? - blu = (blu * ib) >>> 24; // 0x000000FF - - // get buffer pixels - int bb = m_pixels[xstart]; - int br = (bb & 0xFF0000); // 0x00FF0000 - int bg = (bb & 0xFF00); // 0x0000FF00 - bb = (bb & 0xFF); // 0x000000FF - - // - m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); - } - } - catch (Exception e) { - } - - // - iu+=iuadd; - iv+=ivadd; - ir+=iradd; - ig+=igadd; - ib+=ibadd; - iz+=izadd; - } - - ytop+=SCREEN_WIDTH; - xleft+=leftadd; - xrght+=rghtadd; - uleft+=uleftadd; - vleft+=vleftadd; - rleft+=rleftadd; - gleft+=gleftadd; - bleft+=bleftadd; - zleft+=zleftadd; - } - } - - - /** - * Gouraud*texture blended with interpolating alpha - */ - private void drawsegment_gouraud_texture32_alpha - ( - float leftadd, - float rghtadd, - int ytop, - int ybottom - ) { - ytop*=SCREEN_WIDTH; - ybottom*=SCREEN_WIDTH; - int p = m_index; - - float iuf = iuadd; - float ivf = ivadd; - float irf = iradd; - float igf = igadd; - float ibf = ibadd; - float iaf = iaadd; - - while (ytop < ybottom) { - int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; - int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - float xdiff = (xstart + PIXEL_CENTER) - xleft; - - int iu = (int) (iuf * xdiff + uleft); - int iv = (int) (ivf * xdiff + vleft); - int ir = (int) (irf * xdiff + rleft); - int ig = (int) (igf * xdiff + gleft); - int ib = (int) (ibf * xdiff + bleft); - int ia = (int) (iaf * xdiff + aleft); - float iz = izadd * xdiff + zleft; - - xstart+=ytop; - xend+=ytop; - - for ( ;xstart < xend; xstart++ ) { - // get texture pixel color - try - { - //if (iz < m_zbuffer[xstart]) { - if (noDepthTest || (iz <= m_zbuffer[xstart])) { // [fry 041114] - //m_zbuffer[xstart] = iz; - - // blend - int al = ia >> 16; - - int red; - int grn; - int blu; - - if (m_bilinear) { - int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); - int iui = (iu & 0xFFFF) >> 9; - int ivi = (iv & 0xFFFF) >> 9; - - // get texture pixels - int pix0 = m_texture[ofs]; - int pix1 = m_texture[ofs + 1]; - ofs+=TEX_WIDTH; - int pix2 = m_texture[ofs]; - int pix3 = m_texture[ofs + 1]; - - // red - int red0 = (pix0 & 0xFF0000); - int red2 = (pix2 & 0xFF0000); - int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7); - int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7); - red = (up + (((dn-up) * ivi) >> 7)) >> 16; - - // grn - red0 = (pix0 & 0xFF00); - red2 = (pix2 & 0xFF00); - up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7); - dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7); - grn = (up + (((dn-up) * ivi) >> 7)) >> 8; - - // blu - red0 = (pix0 & 0xFF); - red2 = (pix2 & 0xFF); - up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7); - dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); - blu = up + (((dn-up) * ivi) >> 7); - - // alpha - pix0>>>=24; - pix2>>>=24; - up = pix0 + ((((pix1 >>> 24) - pix0) * iui) >> 7); - dn = pix2 + ((((pix3 >>> 24) - pix2) * iui) >> 7); - al = al * (up + (((dn-up) * ivi) >> 7)) >> 8; - } else { - blu = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; - al = al * (blu >>> 24) >> 8; - red = (blu & 0xFF0000) >> 16; // 0 - 255 - grn = (blu & 0xFF00) >> 8; // 0 - 255 - blu = (blu & 0xFF); // 0 - 255 - } - - // multiply with gouraud color - red = (red * ir) >>> 8; // 0x00FF???? - grn = (grn * ig) >>> 16; // 0x0000FF?? - blu = (blu * ib) >>> 24; // 0x000000FF - - // get buffer pixels - int bb = m_pixels[xstart]; - int br = (bb & 0xFF0000); // 0x00FF0000 - int bg = (bb & 0xFF00); // 0x0000FF00 - bb = (bb & 0xFF); // 0x000000FF - - // - m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); - m_stencil[xstart] = p; - } - } - catch (Exception e) { - } - - // - iu+=iuadd; - iv+=ivadd; - ir+=iradd; - ig+=igadd; - ib+=ibadd; - ia+=iaadd; - iz+=izadd; - } - - ytop+=SCREEN_WIDTH; - xleft+=leftadd; - xrght+=rghtadd; - uleft+=uleftadd; - vleft+=vleftadd; - rleft+=rleftadd; - gleft+=gleftadd; - bleft+=bleftadd; - aleft+=aleftadd; - zleft+=zleftadd; - } - } -} +/* -*- 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 (y0y1) { + if (y2 SCREEN_HEIGHT) { + return; + } else if (yi0 < 0) { + yi0 = 0; + } + + y2 = y_array[o2]; + int yi2 = (int) (y2 + PIXEL_CENTER); + if (yi2 < 0) { + return; + } else if (yi2 > SCREEN_HEIGHT) { + yi2 = SCREEN_HEIGHT; + } + + // Does the poly actually cross a scanline? + if (yi2 > yi0) { + x0 = x_array[o0]; + x1 = x_array[o1]; + x2 = x_array[o2]; + + // get mid Y and clip it + y1 = y_array[o1]; + int yi1 = (int) (y1 + PIXEL_CENTER); + if (yi1 < 0) + yi1 = 0; + if (yi1 > SCREEN_HEIGHT) + yi1 = SCREEN_HEIGHT; + + // calculate deltas etc. + dx2 = x2 - x0; + dy0 = y1 - y0; + dy2 = y2 - y0; + xadd2 = dx2 / dy2; // xadd for "single" edge + temp = dy0 / dy2; + width = temp * dx2 + x0 - x1; + + // calculate alpha blend interpolation + if (INTERPOLATE_ALPHA) { + a0 = a_array[o0]; + a1 = a_array[o1]; + a2 = a_array[o2]; + da0 = a1-a0; + da2 = a2-a0; + iaadd = (int) ((temp * da2 - da0) / width); // alpha add + } + + // calculate intensity interpolation + if (INTERPOLATE_RGB) { + r0 = r_array[o0]; + r1 = r_array[o1]; + r2 = r_array[o2]; + + g0 = g_array[o0]; + g1 = g_array[o1]; + g2 = g_array[o2]; + + b0 = b_array[o0]; + b1 = b_array[o1]; + b2 = b_array[o2]; + + dr0 = r1-r0; + dg0 = g1-g0; + db0 = b1-b0; + + dr2 = r2-r0; + dg2 = g2-g0; + db2 = b2-b0; + + iradd = (int) ((temp * dr2 - dr0) / width); // r add + igadd = (int) ((temp * dg2 - dg0) / width); // g add + ibadd = (int) ((temp * db2 - db0) / width); // b add + } + + // calculate UV interpolation + if (INTERPOLATE_UV) { + u0 = u_array[o0]; + u1 = u_array[o1]; + u2 = u_array[o2]; + v0 = v_array[o0]; + v1 = v_array[o1]; + v2 = v_array[o2]; + du0 = u1-u0; + dv0 = v1-v0; + du2 = u2-u0; + dv2 = v2-v0; + iuadd = (int) ((temp * du2 - du0) / width); // u add + ivadd = (int) ((temp * dv2 - dv0) / width); // v add + } + + z0 = z_array[o0]; + z1 = z_array[o1]; + z2 = z_array[o2]; + dz0 = z1-z0; + dz2 = z2-z0; + izadd = (temp * dz2 - dz0) / width; + + // draw the upper poly segment if it's visible + if (yi1 > yi0) { + dta = (yi0 + PIXEL_CENTER) - y0; + xadd1 = (x1 - x0) / dy0; + + // we can determine which side is "single" side by comparing left/right edge adds + if (xadd2 > xadd1) { + xleft = x0 + dta * xadd1; + xrght = x0 + dta * xadd2; + zleftadd = dz0 / dy0; + zleft = dta*zleftadd+z0; + + // + if (INTERPOLATE_UV) { + uleftadd = du0 / dy0; + vleftadd = dv0 / dy0; + uleft = dta*uleftadd+u0; + vleft = dta*vleftadd+v0; + } + + // + if (INTERPOLATE_RGB) { + rleftadd = dr0 / dy0; + gleftadd = dg0 / dy0; + bleftadd = db0 / dy0; + rleft = dta*rleftadd+r0; + gleft = dta*gleftadd+g0; + bleft = dta*bleftadd+b0; + } + + // + if (INTERPOLATE_ALPHA) { + aleftadd = da0 / dy0; + aleft = dta*aleftadd+a0; + + if (m_drawFlags == R_ALPHA) { + drawsegment_plain_alpha(xadd1,xadd2, yi0,yi1); + } else if (m_drawFlags == (R_GOURAUD + R_ALPHA)) { + drawsegment_gouraud_alpha(xadd1,xadd2, yi0,yi1); + } else if (m_drawFlags == (R_TEXTURE8 + R_ALPHA)) { + drawsegment_texture8_alpha(xadd1,xadd2, yi0,yi1); + } else if (m_drawFlags == (R_TEXTURE24 + R_ALPHA)) { + drawsegment_texture24_alpha(xadd1,xadd2, yi0,yi1); + } else if (m_drawFlags == (R_TEXTURE32 + R_ALPHA)) { + drawsegment_texture32_alpha(xadd1,xadd2, yi0,yi1); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8 + R_ALPHA)) { + drawsegment_gouraud_texture8_alpha(xadd1,xadd2, yi0,yi1); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24 + R_ALPHA)) { + drawsegment_gouraud_texture24_alpha(xadd1,xadd2, yi0,yi1); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32 + R_ALPHA)) { + drawsegment_gouraud_texture32_alpha(xadd1,xadd2, yi0,yi1); + } + } else { + if (m_drawFlags == 0) { + drawsegment_plain(xadd1,xadd2, yi0,yi1); + } else if (m_drawFlags == R_GOURAUD) { + drawsegment_gouraud(xadd1,xadd2, yi0,yi1); + } else if (m_drawFlags == R_TEXTURE8) { + drawsegment_texture8(xadd1,xadd2, yi0,yi1); + } else if (m_drawFlags == R_TEXTURE24) { + drawsegment_texture24(xadd1,xadd2, yi0,yi1); + } else if (m_drawFlags == R_TEXTURE32) { + drawsegment_texture32(xadd1,xadd2, yi0,yi1); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8)) { + drawsegment_gouraud_texture8(xadd1,xadd2, yi0,yi1); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24)) { + drawsegment_gouraud_texture24(xadd1,xadd2, yi0,yi1); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32)) { + drawsegment_gouraud_texture32(xadd1,xadd2, yi0,yi1); + } + } + m_singleRight = true; + } else { + xleft = x0 + dta * xadd2; + xrght = x0 + dta * xadd1; + zleftadd = dz2 / dy2; + zleft = dta*zleftadd+z0; + // + if (INTERPOLATE_UV) { + uleftadd = du2 / dy2; + vleftadd = dv2 / dy2; + uleft = dta*uleftadd+u0; + vleft = dta*vleftadd+v0; + } + + // + if (INTERPOLATE_RGB) { + rleftadd = dr2 / dy2; + gleftadd = dg2 / dy2; + bleftadd = db2 / dy2; + rleft = dta*rleftadd+r0; + gleft = dta*gleftadd+g0; + bleft = dta*bleftadd+b0; + } + + + if (INTERPOLATE_ALPHA) { + aleftadd = da2 / dy2; + aleft = dta*aleftadd+a0; + + if (m_drawFlags == R_ALPHA) { + drawsegment_plain_alpha(xadd2, xadd1, yi0,yi1); + } else if (m_drawFlags == (R_GOURAUD + R_ALPHA)) { + drawsegment_gouraud_alpha(xadd2, xadd1, yi0,yi1); + } else if (m_drawFlags == (R_TEXTURE8 + R_ALPHA)) { + drawsegment_texture8_alpha(xadd2, xadd1, yi0,yi1); + } else if (m_drawFlags == (R_TEXTURE24 + R_ALPHA)) { + drawsegment_texture24_alpha(xadd2, xadd1, yi0,yi1); + } else if (m_drawFlags == (R_TEXTURE32 + R_ALPHA)) { + drawsegment_texture32_alpha(xadd2, xadd1, yi0,yi1); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8 + R_ALPHA)) { + drawsegment_gouraud_texture8_alpha(xadd2, xadd1, yi0,yi1); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24 + R_ALPHA)) { + drawsegment_gouraud_texture24_alpha(xadd2, xadd1, yi0,yi1); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32 + R_ALPHA)) { + drawsegment_gouraud_texture32_alpha(xadd2, xadd1, yi0,yi1); + } + } else { + if (m_drawFlags == 0) { + drawsegment_plain(xadd2, xadd1, yi0,yi1); + } else if (m_drawFlags == R_GOURAUD) { + drawsegment_gouraud(xadd2, xadd1, yi0,yi1); + } else if (m_drawFlags == R_TEXTURE8) { + drawsegment_texture8(xadd2, xadd1, yi0,yi1); + } else if (m_drawFlags == R_TEXTURE24) { + drawsegment_texture24(xadd2, xadd1, yi0,yi1); + } else if (m_drawFlags == R_TEXTURE32) { + drawsegment_texture32(xadd2, xadd1, yi0,yi1); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8)) { + drawsegment_gouraud_texture8(xadd2, xadd1, yi0,yi1); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24)) { + drawsegment_gouraud_texture24(xadd2, xadd1, yi0,yi1); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32)) { + drawsegment_gouraud_texture32(xadd2, xadd1, yi0,yi1); + } + } + m_singleRight = false; + } + + // if bottom segment height is zero, return + if (yi2 == yi1) + return; + + // calculate xadd 1 + dy1 = y2 - y1; + xadd1 = (x2 - x1) / dy1; + } else { + // top seg height was zero, calculate & clip single edge X + dy1 = y2 - y1; + xadd1 = (x2 - x1) / dy1; + + // which edge is left? + if (xadd2 < xadd1) { + xrght = ((yi1 + PIXEL_CENTER) - y0) * xadd2 + x0; + m_singleRight = true; + } else { + dta = (yi1 + PIXEL_CENTER) - y0; + xleft = dta * xadd2 + x0; + zleftadd = dz2 / dy2; + zleft = dta * zleftadd + z0; + + if (INTERPOLATE_UV) { + uleftadd = du2 / dy2; + vleftadd = dv2 / dy2; + uleft = dta * uleftadd + u0; + vleft = dta * vleftadd + v0; + } + + if (INTERPOLATE_RGB) { + rleftadd = dr2 / dy2; + gleftadd = dg2 / dy2; + bleftadd = db2 / dy2; + rleft = dta * rleftadd + r0; + gleft = dta * gleftadd + g0; + bleft = dta * bleftadd + b0; + } + + // + if (INTERPOLATE_ALPHA) { + aleftadd = da2 / dy2; + aleft = dta * aleftadd + a0; + } + m_singleRight = false; + } + } + + // draw the lower segment + if (m_singleRight) { + dta = (yi1 + PIXEL_CENTER) - y1; + xleft = dta * xadd1 + x1; + zleftadd = (z2 - z1) / dy1; + zleft = dta * zleftadd + z1; + + if (INTERPOLATE_UV) { + uleftadd = (u2 - u1) / dy1; + vleftadd = (v2 - v1) / dy1; + uleft = dta * uleftadd + u1; + vleft = dta * vleftadd + v1; + } + + if (INTERPOLATE_RGB) { + rleftadd = (r2 - r1) / dy1; + gleftadd = (g2 - g1) / dy1; + bleftadd = (b2 - b1) / dy1; + rleft = dta * rleftadd + r1; + gleft = dta * gleftadd + g1; + bleft = dta * bleftadd + b1; + } + + if (INTERPOLATE_ALPHA) { + aleftadd = (a2 - a1) / dy1; + aleft = dta * aleftadd + a1; + + if (m_drawFlags == R_ALPHA) { + drawsegment_plain_alpha(xadd1, xadd2, yi1,yi2); + } else if (m_drawFlags == (R_GOURAUD + R_ALPHA)) { + drawsegment_gouraud_alpha(xadd1, xadd2, yi1,yi2); + } else if (m_drawFlags == (R_TEXTURE8 + R_ALPHA)) { + drawsegment_texture8_alpha(xadd1, xadd2, yi1,yi2); + } else if (m_drawFlags == (R_TEXTURE24 + R_ALPHA)) { + drawsegment_texture24_alpha(xadd1, xadd2, yi1,yi2); + } else if (m_drawFlags == (R_TEXTURE32 + R_ALPHA)) { + drawsegment_texture32_alpha(xadd1, xadd2, yi1,yi2); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8 + R_ALPHA)) { + drawsegment_gouraud_texture8_alpha(xadd1, xadd2, yi1,yi2); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24 + R_ALPHA)) { + drawsegment_gouraud_texture24_alpha(xadd1, xadd2, yi1,yi2); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32 + R_ALPHA)) { + drawsegment_gouraud_texture32_alpha(xadd1, xadd2, yi1,yi2); + } + } else { + if (m_drawFlags == 0) { + drawsegment_plain(xadd1, xadd2, yi1,yi2); + } else if (m_drawFlags == R_GOURAUD) { + drawsegment_gouraud(xadd1, xadd2, yi1,yi2); + } else if (m_drawFlags == R_TEXTURE8) { + drawsegment_texture8(xadd1, xadd2, yi1,yi2); + } else if (m_drawFlags == R_TEXTURE24) { + drawsegment_texture24(xadd1, xadd2, yi1,yi2); + } else if (m_drawFlags == R_TEXTURE32) { + drawsegment_texture32(xadd1, xadd2, yi1,yi2); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8)) { + drawsegment_gouraud_texture8(xadd1, xadd2, yi1,yi2); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24)) { + drawsegment_gouraud_texture24(xadd1, xadd2, yi1,yi2); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32)) { + drawsegment_gouraud_texture32(xadd1, xadd2, yi1,yi2); + } + } + } else { + xrght = ((yi1 + PIXEL_CENTER)- y1) * xadd1 + x1; + + if (INTERPOLATE_ALPHA) { + if (m_drawFlags == R_ALPHA) { + drawsegment_plain_alpha(xadd2, xadd1, yi1,yi2); + } else if (m_drawFlags == (R_GOURAUD + R_ALPHA)) { + drawsegment_gouraud_alpha(xadd2, xadd1, yi1,yi2); + } else if (m_drawFlags == (R_TEXTURE8 + R_ALPHA)) { + drawsegment_texture8_alpha(xadd2, xadd1, yi1,yi2); + } else if (m_drawFlags == (R_TEXTURE24 + R_ALPHA)) { + drawsegment_texture24_alpha(xadd2, xadd1, yi1,yi2); + } else if (m_drawFlags == (R_TEXTURE32 + R_ALPHA)) { + drawsegment_texture32_alpha(xadd2, xadd1, yi1,yi2); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8 + R_ALPHA)) { + drawsegment_gouraud_texture8_alpha(xadd2, xadd1, yi1,yi2); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24 + R_ALPHA)) { + drawsegment_gouraud_texture24_alpha(xadd2, xadd1, yi1,yi2); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32 + R_ALPHA)) { + drawsegment_gouraud_texture32_alpha(xadd2, xadd1, yi1,yi2); + } + } else { + if (m_drawFlags == 0) { + drawsegment_plain(xadd2, xadd1, yi1,yi2); + } else if (m_drawFlags == R_GOURAUD) { + drawsegment_gouraud(xadd2, xadd1, yi1,yi2); + } else if (m_drawFlags == R_TEXTURE8) { + drawsegment_texture8(xadd2, xadd1, yi1,yi2); + } else if (m_drawFlags == R_TEXTURE24) { + drawsegment_texture24(xadd2, xadd1, yi1,yi2); + } else if (m_drawFlags == R_TEXTURE32) { + drawsegment_texture32(xadd2, xadd1, yi1,yi2); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE8)) { + drawsegment_gouraud_texture8(xadd2, xadd1, yi1,yi2); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE24)) { + drawsegment_gouraud_texture24(xadd2, xadd1, yi1,yi2); + } else if (m_drawFlags == (R_GOURAUD + R_TEXTURE32)) { + drawsegment_gouraud_texture32(xadd2, xadd1, yi1,yi2); + } + } + } + } + } + + + + /** + * Plain color + */ + private void drawsegment_plain + ( + float leftadd, + float rghtadd, + int ytop, + int ybottom + ) { + ytop*=SCREEN_WIDTH; + ybottom*=SCREEN_WIDTH; + int f = m_fill; + int p = m_index; + + while (ytop < ybottom) { + int xstart = (int) (xleft + PIXEL_CENTER); + if (xstart < 0) + xstart = 0; + + int xend = (int) (xrght + PIXEL_CENTER); + if (xend > SCREEN_WIDTH) + xend = SCREEN_WIDTH; + + float xdiff = (xstart + PIXEL_CENTER) - xleft; + float iz = izadd * xdiff + zleft; + xstart+=ytop; + xend+=ytop; + + for ( ; xstart < xend; xstart++ ) { + if (noDepthTest || (iz <= m_zbuffer[xstart])) { + m_zbuffer[xstart] = iz; + m_pixels[xstart] = f; + m_stencil[xstart] = p; + } + iz+=izadd; + } + + ytop+=SCREEN_WIDTH; + xleft+=leftadd; + xrght+=rghtadd; + zleft+=zleftadd; + } + } + + /** + * Plain color, interpolated alpha + */ + private void drawsegment_plain_alpha + ( + float leftadd, + float rghtadd, + int ytop, + int ybottom + ) { + ytop*=SCREEN_WIDTH; + ybottom*=SCREEN_WIDTH; + + int pr = m_fill & 0xFF0000; + int pg = m_fill & 0xFF00; + int pb = m_fill & 0xFF; + + int p = m_index; + float iaf = iaadd; + + while (ytop < ybottom) { + int xstart = (int) (xleft + PIXEL_CENTER); + if (xstart < 0) + xstart = 0; + + int xend = (int) (xrght + PIXEL_CENTER); + if (xend > SCREEN_WIDTH) + xend = SCREEN_WIDTH; + + float xdiff = (xstart + PIXEL_CENTER) - xleft; + float iz = izadd * xdiff + zleft; + int ia = (int) (iaf * xdiff + aleft); + xstart+=ytop; + xend+=ytop; + + for ( ; xstart < xend; xstart++ ) { + if (noDepthTest || (iz <= m_zbuffer[xstart])) { + //m_zbuffer[xstart] = iz; + + int alpha = ia >> 16; + int mr0 = m_pixels[xstart]; + int mg0 = mr0 & 0xFF00; + int mb0 = mr0 & 0xFF; + mr0 &= 0xFF0000; + + mr0 = mr0 + (((pr - mr0) * alpha) >> 8); + mg0 = mg0 + (((pg - mg0) * alpha) >> 8); + mb0 = mb0 + (((pb - mb0) * alpha) >> 8); + m_pixels[xstart] = (mr0 & 0xFF0000) | (mg0 & 0xFF00) | (mb0 & 0xFF); + + m_stencil[xstart] = p; + } + iz += izadd; + ia += iaadd; + } + ytop += SCREEN_WIDTH; + xleft += leftadd; + xrght += rghtadd; + zleft += zleftadd; + } + } + + + /** + * RGB gouraud + */ + private void drawsegment_gouraud + ( + float leftadd, + float rghtadd, + int ytop, + int ybottom + ) { + float irf = iradd; + float igf = igadd; + float ibf = ibadd; + + ytop*=SCREEN_WIDTH; + ybottom*=SCREEN_WIDTH; + int p = m_index; + + while (ytop < ybottom) { + int xstart = (int) (xleft + PIXEL_CENTER); + if (xstart < 0) + xstart = 0; + + int xend = (int) (xrght + PIXEL_CENTER); + if (xend > SCREEN_WIDTH) + xend = SCREEN_WIDTH; + + float xdiff = (xstart + PIXEL_CENTER) - xleft; + int ir = (int) (irf * xdiff + rleft); + int ig = (int) (igf * xdiff + gleft); + int ib = (int) (ibf * xdiff + bleft); + float iz = izadd * xdiff + zleft; + + xstart+=ytop; + xend+=ytop; + + for ( ; xstart < xend; xstart++ ) { + if (noDepthTest || (iz <= m_zbuffer[xstart])) { + m_zbuffer[xstart] = iz; + m_pixels[xstart]=((ir & 0xFF0000) | ((ig >> 8) & 0xFF00) | (ib >> 16)); + m_stencil[xstart] = p; + } + + // + ir+=iradd; + ig+=igadd; + ib+=ibadd; + iz+=izadd; + } + + ytop+=SCREEN_WIDTH; + xleft+=leftadd; + xrght+=rghtadd; + rleft+=rleftadd; + gleft+=gleftadd; + bleft+=bleftadd; + zleft+=zleftadd; + } + } + + + /** + * RGB gouraud + interpolated alpha + */ + private void drawsegment_gouraud_alpha + ( + float leftadd, + float rghtadd, + int ytop, + int ybottom + ) { + ytop*=SCREEN_WIDTH; + ybottom*=SCREEN_WIDTH; + int p = m_index; + + float irf = iradd; + float igf = igadd; + float ibf = ibadd; + float iaf = iaadd; + + while (ytop < ybottom) { + int xstart = (int) (xleft + PIXEL_CENTER); + if (xstart < 0) + xstart = 0; + int xend = (int) (xrght + PIXEL_CENTER); + if (xend > SCREEN_WIDTH) + xend = SCREEN_WIDTH; + float xdiff = (xstart + PIXEL_CENTER) - xleft; + + int ir = (int) (irf * xdiff + rleft); + int ig = (int) (igf * xdiff + gleft); + int ib = (int) (ibf * xdiff + bleft); + int ia = (int) (iaf * xdiff + aleft); + float iz = izadd * xdiff + zleft; + + xstart+=ytop; + xend+=ytop; + + for ( ; xstart < xend; xstart++ ) { + if (noDepthTest || (iz <= m_zbuffer[xstart])) { + //m_zbuffer[xstart] = iz; + + // + int red = (ir & 0xFF0000); + int grn = (ig >> 8) & 0xFF00; + int blu = (ib >> 16); + + // get buffer pixels + int bb = m_pixels[xstart]; + int br = (bb & 0xFF0000); // 0x00FF0000 + int bg = (bb & 0xFF00); // 0x0000FF00 + bb = (bb & 0xFF); // 0x000000FF + + // blend alpha + int al = ia >> 16; + + // + m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) | ((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); + m_stencil[xstart] = p; + } + + // + ir+=iradd; + ig+=igadd; + ib+=ibadd; + ia+=iaadd; + iz+=izadd; + } + + ytop+=SCREEN_WIDTH; + xleft+=leftadd; + xrght+=rghtadd; + rleft+=rleftadd; + gleft+=gleftadd; + bleft+=bleftadd; + aleft+=aleftadd; + zleft+=zleftadd; + } + } + + + /** + * 8-bit alpha texture + */ + private void drawsegment_texture8 + ( + float leftadd, + float rghtadd, + int ytop, + int ybottom + ) { + ytop*=SCREEN_WIDTH; + ybottom*=SCREEN_WIDTH; + int p = m_index; + + float iuf = iuadd; + float ivf = ivadd; + + int red = m_fill & 0xFF0000; + int grn = m_fill & 0xFF00; + int blu = m_fill & 0xFF; + + while (ytop < ybottom) { + int xstart = (int) (xleft + PIXEL_CENTER); + if (xstart < 0) + xstart = 0; + + int xend = (int) (xrght + PIXEL_CENTER); + if (xend > SCREEN_WIDTH) + xend = SCREEN_WIDTH; + + float xdiff = (xstart + PIXEL_CENTER) - xleft; + int iu = (int) (iuf * xdiff + uleft); + int iv = (int) (ivf * xdiff + vleft); + float iz = izadd * xdiff + zleft; + + xstart+=ytop; + xend+=ytop; + + for ( ; xstart < xend; xstart++ ) { + // try-catch just in case pixel offset it out of range + try + { + if (noDepthTest || (iz <= m_zbuffer[xstart])) { + //m_zbuffer[xstart] = iz; + + int al0; + if (m_bilinear) { + int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); + int iui = iu & 0xFFFF; + al0 = m_texture[ofs] & 0xFF; + int al1 = m_texture[ofs + 1] & 0xFF; + ofs+=TEX_WIDTH; + int al2 = m_texture[ofs] & 0xFF; + int al3 = m_texture[ofs + 1] & 0xFF; + al0 = al0 + (((al1-al0) * iui) >> 16); + al2 = al2 + (((al3-al2) * iui) >> 16); + al0 = al0 + (((al2-al0) * (iv & 0xFFFF)) >> 16); + } else { + al0 = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)] & 0xFF; + } + + int br = m_pixels[xstart]; + int bg = (br & 0xFF00); + int bb = (br & 0xFF); + br = (br & 0xFF0000); + m_pixels[xstart] = ((br + (((red - br) * al0) >> 8)) & 0xFF0000) | ((bg + (((grn - bg) * al0) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al0) >> 8)) & 0xFF); + m_stencil[xstart] = p; + } + } + catch (Exception e) { + } + iu+=iuadd; + iv+=ivadd; + iz+=izadd; + } + + ytop+=SCREEN_WIDTH; + + xleft+=leftadd; + xrght+=rghtadd; + uleft+=uleftadd; + vleft+=vleftadd; + zleft+=zleftadd; + } + } + + + /** + * 8-bit texutre + alpha + */ + private void drawsegment_texture8_alpha + ( + float leftadd, + float rghtadd, + int ytop, + int ybottom + ) { + ytop*=SCREEN_WIDTH; + ybottom*=SCREEN_WIDTH; + int p = m_index; + + float iuf = iuadd; + float ivf = ivadd; + float iaf = iaadd; + + int red = m_fill & 0xFF0000; + int grn = m_fill & 0xFF00; + int blu = m_fill & 0xFF; + + while (ytop < ybottom) { + int xstart = (int) (xleft + PIXEL_CENTER); + if (xstart < 0) + xstart = 0; + + int xend = (int) (xrght + PIXEL_CENTER); + if (xend > SCREEN_WIDTH) + xend = SCREEN_WIDTH; + + float xdiff = (xstart + PIXEL_CENTER) - xleft; + int iu = (int) (iuf * xdiff + uleft); + int iv = (int) (ivf * xdiff + vleft); + int ia = (int) (iaf * xdiff + aleft); + float iz = izadd * xdiff + zleft; + + xstart+=ytop; + xend+=ytop; + + for ( ; xstart < xend; xstart++ ) { + // try-catch just in case pixel offset it out of range + try + { + if (noDepthTest || (iz <= m_zbuffer[xstart])) { + //m_zbuffer[xstart] = iz; + + int al0; + if (m_bilinear) { + int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); + int iui = iu & 0xFFFF; + al0 = m_texture[ofs] & 0xFF; + int al1 = m_texture[ofs + 1] & 0xFF; + ofs+=TEX_WIDTH; + int al2 = m_texture[ofs] & 0xFF; + int al3 = m_texture[ofs + 1] & 0xFF; + al0 = al0 + (((al1-al0) * iui) >> 16); + al2 = al2 + (((al3-al2) * iui) >> 16); + al0 = al0 + (((al2-al0) * (iv & 0xFFFF)) >> 16); + } else { + al0 = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)] & 0xFF; + } + al0 = (al0 * (ia >> 16)) >> 8; + + int br = m_pixels[xstart]; + int bg = (br & 0xFF00); + int bb = (br & 0xFF); + br = (br & 0xFF0000); + m_pixels[xstart] = ((br + (((red - br) * al0) >> 8)) & 0xFF0000) | ((bg + (((grn - bg) * al0) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al0) >> 8)) & 0xFF); + m_stencil[xstart] = p; + } + } + catch (Exception e) { + } + iu+=iuadd; + iv+=ivadd; + iz+=izadd; + ia+=iaadd; + } + + ytop+=SCREEN_WIDTH; + xleft+=leftadd; + xrght+=rghtadd; + uleft+=uleftadd; + vleft+=vleftadd; + zleft+=zleftadd; + aleft+=aleftadd; + } + } + + + /** + * Plain 24-bit texutre + */ + private void drawsegment_texture24 + ( + float leftadd, + float rghtadd, + int ytop, + int ybottom + ) { + ytop*=SCREEN_WIDTH; + ybottom*=SCREEN_WIDTH; + int p = m_index; + + float iuf = iuadd; + float ivf = ivadd; + + while (ytop < ybottom) { + int xstart = (int) (xleft + PIXEL_CENTER); + if (xstart < 0) + xstart = 0; + + int xend = (int) (xrght + PIXEL_CENTER); + if (xend > SCREEN_WIDTH) + xend = SCREEN_WIDTH; + + float xdiff = (xstart + PIXEL_CENTER) - xleft; + int iu = (int) (iuf * xdiff + uleft); + int iv = (int) (ivf * xdiff + vleft); + float iz = izadd * xdiff + zleft; + + xstart+=ytop; + xend+=ytop; + + for ( ; xstart < xend; xstart++ ) { + // try-catch just in case pixel offset it out of range + try + { + if (noDepthTest || (iz <= m_zbuffer[xstart])) { + m_zbuffer[xstart] = iz; + if (m_bilinear) { + int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); + int iui = (iu & 0xFFFF) >> 9; + int ivi = (iv & 0xFFFF) >> 9; + + // get texture pixels + int pix0 = m_texture[ofs]; + int pix1 = m_texture[ofs + 1]; + ofs+=TEX_WIDTH; + int pix2 = m_texture[ofs]; + int pix3 = m_texture[ofs + 1]; + + // red + int red0 = (pix0 & 0xFF0000); + int red2 = (pix2 & 0xFF0000); + int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7); + int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7); + int red = up + (((dn-up) * ivi) >> 7); + + // grn + red0 = (pix0 & 0xFF00); + red2 = (pix2 & 0xFF00); + up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7); + dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7); + int grn = up + (((dn-up) * ivi) >> 7); + + // blu + red0 = (pix0 & 0xFF); + red2 = (pix2 & 0xFF); + up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7); + dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); + int blu = up + (((dn-up) * ivi) >> 7); + + // + m_pixels[xstart] = (red & 0xFF0000) | (grn & 0xFF00) | (blu & 0xFF); + } else { + m_pixels[xstart] = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; + } + m_stencil[xstart] = p; + } + } + catch (Exception e) { + + } + iu+=iuadd; + iv+=ivadd; + iz+=izadd; + } + + ytop+=SCREEN_WIDTH; + xleft+=leftadd; + xrght+=rghtadd; + uleft+=uleftadd; + vleft+=vleftadd; + zleft+=zleftadd; + } + } + + /** + * Alpha 24-bit texutre + */ + private void drawsegment_texture24_alpha + ( + float leftadd, + float rghtadd, + int ytop, + int ybottom + ) { + ytop*=SCREEN_WIDTH; + ybottom*=SCREEN_WIDTH; + int p = m_index; + + float iuf = iuadd; + float ivf = ivadd; + float iaf = iaadd; + + while (ytop < ybottom) { + int xstart = (int) (xleft + PIXEL_CENTER); + if (xstart < 0) + xstart = 0; + + int xend = (int) (xrght + PIXEL_CENTER); + if (xend > SCREEN_WIDTH) + xend = SCREEN_WIDTH; + + float xdiff = (xstart + PIXEL_CENTER) - xleft; + int iu = (int) (iuf * xdiff + uleft); + int iv = (int) (ivf * xdiff + vleft); + int ia = (int) (iaf * xdiff + aleft); + float iz = izadd * xdiff + zleft; + + xstart+=ytop; + xend+=ytop; + + for ( ; xstart < xend; xstart++ ) { + // try-catch just in case pixel offset it out of range + try + { + if (noDepthTest || (iz <= m_zbuffer[xstart])) { + //m_zbuffer[xstart] = iz; + + // get alpha + int al = ia >> 16; + + if (m_bilinear) { + int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); + int iui = (iu & 0xFFFF) >> 9; + int ivi = (iv & 0xFFFF) >> 9; + + // get texture pixels + int pix0 = m_texture[ofs]; + int pix1 = m_texture[ofs + 1]; + ofs+=TEX_WIDTH; + int pix2 = m_texture[ofs]; + int pix3 = m_texture[ofs + 1]; + + // red + int red0 = (pix0 & 0xFF0000); + int red2 = (pix2 & 0xFF0000); + int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7); + int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7); + int red = up + (((dn-up) * ivi) >> 7); + + // grn + red0 = (pix0 & 0xFF00); + red2 = (pix2 & 0xFF00); + up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7); + dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7); + int grn = up + (((dn-up) * ivi) >> 7); + + // blu + red0 = (pix0 & 0xFF); + red2 = (pix2 & 0xFF); + up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7); + dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); + int blu = up + (((dn-up) * ivi) >> 7); + + // get buffer pixels + int bb = m_pixels[xstart]; + int br = (bb & 0xFF0000); // 0x00FF0000 + int bg = (bb & 0xFF00); // 0x0000FF00 + bb = (bb & 0xFF); // 0x000000FF + m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |( (bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); + } else { + int red = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; + int grn = red & 0xFF00; + int blu = red & 0xFF; + red&=0xFF0000; + + // get buffer pixels + int bb = m_pixels[xstart]; + int br = (bb & 0xFF0000); // 0x00FF0000 + int bg = (bb & 0xFF00); // 0x0000FF00 + bb = (bb & 0xFF); // 0x000000FF + m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); + } + m_stencil[xstart] = p; + } + } + catch (Exception e) { + } + iu+=iuadd; + iv+=ivadd; + ia+=iaadd; + iz+=izadd; + } + + ytop+=SCREEN_WIDTH; + + xleft+=leftadd; + xrght+=rghtadd; + uleft+=uleftadd; + vleft+=vleftadd; + zleft+=zleftadd; + aleft+=aleftadd; + } + } + + /** + * Plain 32-bit texutre + */ + private void drawsegment_texture32 + ( + float leftadd, + float rghtadd, + int ytop, + int ybottom + ) { + ytop*=SCREEN_WIDTH; + ybottom*=SCREEN_WIDTH; + int p = m_index; + + float iuf = iuadd; + float ivf = ivadd; + + while (ytop < ybottom) { + int xstart = (int) (xleft + PIXEL_CENTER); + if (xstart < 0) + xstart = 0; + + int xend = (int) (xrght + PIXEL_CENTER); + if (xend > SCREEN_WIDTH) + xend = SCREEN_WIDTH; + + float xdiff = (xstart + PIXEL_CENTER) - xleft; + int iu = (int) (iuf * xdiff + uleft); + int iv = (int) (ivf * xdiff + vleft); + float iz = izadd * xdiff + zleft; + + xstart+=ytop; + xend+=ytop; + + for ( ; xstart < xend; xstart++ ) { + // try-catch just in case pixel offset it out of range + try + { + if (noDepthTest || (iz <= m_zbuffer[xstart])) { + //m_zbuffer[xstart] = iz; + + if (m_bilinear) { + int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); + int iui = (iu & 0xFFFF) >> 9; + int ivi = (iv & 0xFFFF) >> 9; + + // get texture pixels + int pix0 = m_texture[ofs]; + int pix1 = m_texture[ofs + 1]; + ofs+=TEX_WIDTH; + int pix2 = m_texture[ofs]; + int pix3 = m_texture[ofs + 1]; + + // red + int red0 = (pix0 & 0xFF0000); + int red2 = (pix2 & 0xFF0000); + int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7); + int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7); + int red = up + (((dn-up) * ivi) >> 7); + + // grn + red0 = (pix0 & 0xFF00); + red2 = (pix2 & 0xFF00); + up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7); + dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7); + int grn = up + (((dn-up) * ivi) >> 7); + + // blu + red0 = (pix0 & 0xFF); + red2 = (pix2 & 0xFF); + up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7); + dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); + int blu = up + (((dn-up) * ivi) >> 7); + + // alpha + pix0>>>=24; + pix2>>>=24; + up = pix0 + ((((pix1 >>> 24) - pix0) * iui) >> 7); + dn = pix2 + ((((pix3 >>> 24) - pix2) * iui) >> 7); + int al = up + (((dn-up) * ivi) >> 7); + + // get buffer pixels + int bb = m_pixels[xstart]; + int br = (bb & 0xFF0000); // 0x00FF0000 + int bg = (bb & 0xFF00); // 0x0000FF00 + bb = (bb & 0xFF); // 0x000000FF + m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); + } else { + int red = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; + int al = red >>> 24; + int grn = red & 0xFF00; + int blu = red & 0xFF; + red&=0xFF0000; + + // get buffer pixels + int bb = m_pixels[xstart]; + int br = (bb & 0xFF0000); // 0x00FF0000 + int bg = (bb & 0xFF00); // 0x0000FF00 + bb = (bb & 0xFF); // 0x000000FF + m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); + } + m_stencil[xstart] = p; + } + } + catch (Exception e) { + } + iu+=iuadd; + iv+=ivadd; + iz+=izadd; + } + + ytop+=SCREEN_WIDTH; + + xleft+=leftadd; + xrght+=rghtadd; + uleft+=uleftadd; + vleft+=vleftadd; + zleft+=zleftadd; + aleft+=aleftadd; + } + + + } + + /** + * Alpha 24-bit texutre + */ + private void drawsegment_texture32_alpha + ( + float leftadd, + float rghtadd, + int ytop, + int ybottom + ) { + + ytop*=SCREEN_WIDTH; + ybottom*=SCREEN_WIDTH; + int p = m_index; + + float iuf = iuadd; + float ivf = ivadd; + float iaf = iaadd; + + while (ytop < ybottom) { + int xstart = (int) (xleft + PIXEL_CENTER); + if (xstart < 0) + xstart = 0; + + int xend = (int) (xrght + PIXEL_CENTER); + if (xend > SCREEN_WIDTH) + xend = SCREEN_WIDTH; + + float xdiff = (xstart + PIXEL_CENTER) - xleft; + int iu = (int) (iuf * xdiff + uleft); + int iv = (int) (ivf * xdiff + vleft); + int ia = (int) (iaf * xdiff + aleft); + float iz = izadd * xdiff + zleft; + + xstart+=ytop; + xend+=ytop; + + for ( ; xstart < xend; xstart++ ) { + // try-catch just in case pixel offset it out of range + try + { + if (noDepthTest || (iz <= m_zbuffer[xstart])) { + //m_zbuffer[xstart] = iz; + + // get alpha + int al = ia >> 16; + + if (m_bilinear) { + int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); + int iui = (iu & 0xFFFF) >> 9; + int ivi = (iv & 0xFFFF) >> 9; + + // get texture pixels + int pix0 = m_texture[ofs]; + int pix1 = m_texture[ofs + 1]; + ofs+=TEX_WIDTH; + int pix2 = m_texture[ofs]; + int pix3 = m_texture[ofs + 1]; + + // red + int red0 = (pix0 & 0xFF0000); + int red2 = (pix2 & 0xFF0000); + int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7); + int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7); + int red = up + (((dn-up) * ivi) >> 7); + + // grn + red0 = (pix0 & 0xFF00); + red2 = (pix2 & 0xFF00); + up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7); + dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7); + int grn = up + (((dn-up) * ivi) >> 7); + + // blu + red0 = (pix0 & 0xFF); + red2 = (pix2 & 0xFF); + up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7); + dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); + int blu = up + (((dn-up) * ivi) >> 7); + + // alpha + pix0>>>=24; + pix2>>>=24; + up = pix0 + ((((pix1 >>> 24) - pix0) * iui) >> 7); + dn = pix2 + ((((pix3 >>> 24) - pix2) * iui) >> 7); + al = al * (up + (((dn-up) * ivi) >> 7)) >> 8; + + // get buffer pixels + int bb = m_pixels[xstart]; + int br = (bb & 0xFF0000); // 0x00FF0000 + int bg = (bb & 0xFF00); // 0x0000FF00 + bb = (bb & 0xFF); // 0x000000FF + m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); + } else { + int red = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; + al = al * (red >>> 24) >> 8; + int grn = red & 0xFF00; + int blu = red & 0xFF; + red&=0xFF0000; + + // get buffer pixels + int bb = m_pixels[xstart]; + int br = (bb & 0xFF0000); // 0x00FF0000 + int bg = (bb & 0xFF00); // 0x0000FF00 + bb = (bb & 0xFF); // 0x000000FF + m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); + } + m_stencil[xstart] = p; + } + } + catch (Exception e) { + } + iu+=iuadd; + iv+=ivadd; + ia+=iaadd; + iz+=izadd; + } + + ytop+=SCREEN_WIDTH; + + xleft+=leftadd; + xrght+=rghtadd; + uleft+=uleftadd; + vleft+=vleftadd; + zleft+=zleftadd; + aleft+=aleftadd; + } + + + } + + + /** + * Gouraud blended with 8-bit alpha texture + */ + private void drawsegment_gouraud_texture8 + ( + float leftadd, + float rghtadd, + int ytop, + int ybottom + ) { + ytop*=SCREEN_WIDTH; + ybottom*=SCREEN_WIDTH; + int p = m_index; + + float iuf = iuadd; + float ivf = ivadd; + float irf = iradd; + float igf = igadd; + float ibf = ibadd; + + while (ytop < ybottom) { + int xstart = (int) (xleft + PIXEL_CENTER); + if (xstart < 0) + xstart = 0; + int xend = (int) (xrght + PIXEL_CENTER); + if (xend > SCREEN_WIDTH) + xend = SCREEN_WIDTH; + float xdiff = (xstart + PIXEL_CENTER) - xleft; + + int iu = (int) (iuf * xdiff + uleft); + int iv = (int) (ivf * xdiff + vleft); + int ir = (int) (irf * xdiff + rleft); + int ig = (int) (igf * xdiff + gleft); + int ib = (int) (ibf * xdiff + bleft); + float iz = izadd * xdiff + zleft; + + xstart+=ytop; + xend+=ytop; + + for ( ; xstart < xend; xstart++ ) { + try + { + if (noDepthTest || (iz <= m_zbuffer[xstart])) { + //m_zbuffer[xstart] = iz; + + int al0; + if (m_bilinear) { + int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); + int iui = iu & 0xFFFF; + al0 = m_texture[ofs] & 0xFF; + int al1 = m_texture[ofs + 1] & 0xFF; + ofs+=TEX_WIDTH; + int al2 = m_texture[ofs] & 0xFF; + int al3 = m_texture[ofs + 1] & 0xFF; + al0 = al0 + (((al1-al0) * iui) >> 16); + al2 = al2 + (((al3-al2) * iui) >> 16); + al0 = al0 + (((al2-al0) * (iv & 0xFFFF)) >> 16); + } else { + al0 = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)] & 0xFF; + } + + // get RGB colors + int red = ir & 0xFF0000; + int grn = (ig >> 8) & 0xFF00; + int blu = (ib >> 16); + + // get buffer pixels + int bb = m_pixels[xstart]; + int br = (bb & 0xFF0000); // 0x00FF0000 + int bg = (bb & 0xFF00); // 0x0000FF00 + bb = (bb & 0xFF); // 0x000000FF + m_pixels[xstart] = ((br + (((red - br) * al0) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al0) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al0) >> 8)) & 0xFF); + + // write stencil + m_stencil[xstart] = p; + } + } + catch (Exception e) { + + } + + // + iu+=iuadd; + iv+=ivadd; + ir+=iradd; + ig+=igadd; + ib+=ibadd; + iz+=izadd; + } + + ytop+=SCREEN_WIDTH; + xleft+=leftadd; + xrght+=rghtadd; + + uleft+=uleftadd; + vleft+=vleftadd; + rleft+=rleftadd; + gleft+=gleftadd; + bleft+=bleftadd; + zleft+=zleftadd; + } + } + + + /** + * Texture multiplied with gouraud + */ + private void drawsegment_gouraud_texture8_alpha + ( + float leftadd, + float rghtadd, + int ytop, + int ybottom + ) { + ytop*=SCREEN_WIDTH; + ybottom*=SCREEN_WIDTH; + int p = m_index; + + float iuf = iuadd; + float ivf = ivadd; + float irf = iradd; + float igf = igadd; + float ibf = ibadd; + float iaf = iaadd; + + while (ytop < ybottom) { + int xstart = (int) (xleft + PIXEL_CENTER); + if (xstart < 0) + xstart = 0; + int xend = (int) (xrght + PIXEL_CENTER); + if (xend > SCREEN_WIDTH) + xend = SCREEN_WIDTH; + float xdiff = (xstart + PIXEL_CENTER) - xleft; + + int iu = (int) (iuf * xdiff + uleft); + int iv = (int) (ivf * xdiff + vleft); + int ir = (int) (irf * xdiff + rleft); + int ig = (int) (igf * xdiff + gleft); + int ib = (int) (ibf * xdiff + bleft); + int ia = (int) (iaf * xdiff + aleft); + float iz = izadd * xdiff + zleft; + + xstart+=ytop; + xend+=ytop; + + for ( ; xstart < xend; xstart++ ) { + try + { + if (noDepthTest || (iz <= m_zbuffer[xstart])) { + //m_zbuffer[xstart] = iz; + + int al0; + if (m_bilinear) { + int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); + int iui = iu & 0xFFFF; + al0 = m_texture[ofs] & 0xFF; + int al1 = m_texture[ofs + 1] & 0xFF; + ofs+=TEX_WIDTH; + int al2 = m_texture[ofs] & 0xFF; + int al3 = m_texture[ofs + 1] & 0xFF; + al0 = al0 + (((al1-al0) * iui) >> 16); + al2 = al2 + (((al3-al2) * iui) >> 16); + al0 = al0 + (((al2-al0) * (iv & 0xFFFF)) >> 16); + } else { + al0 = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)] & 0xFF; + } + al0 = (al0 * (ia >> 16)) >> 8; + + // get RGB colors + int red = ir & 0xFF0000; + int grn = (ig >> 8) & 0xFF00; + int blu = (ib >> 16); + + // get buffer pixels + int bb = m_pixels[xstart]; + int br = (bb & 0xFF0000); // 0x00FF0000 + int bg = (bb & 0xFF00); // 0x0000FF00 + bb = (bb & 0xFF); // 0x000000FF + m_pixels[xstart] = ((br + (((red - br) * al0) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al0) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al0) >> 8)) & 0xFF); + + // write stencil + m_stencil[xstart] = p; + } + } + catch (Exception e) { + + } + + // + iu+=iuadd; + iv+=ivadd; + ir+=iradd; + ig+=igadd; + ib+=ibadd; + ia+=iaadd; + iz+=izadd; + } + + ytop+=SCREEN_WIDTH; + xleft+=leftadd; + xrght+=rghtadd; + uleft+=uleftadd; + vleft+=vleftadd; + rleft+=rleftadd; + gleft+=gleftadd; + bleft+=bleftadd; + aleft+=aleftadd; + zleft+=zleftadd; + } + } + + + /** + * Texture multiplied with gouraud + */ + private void drawsegment_gouraud_texture24 + ( + float leftadd, + float rghtadd, + int ytop, + int ybottom + ) { + ytop*=SCREEN_WIDTH; + ybottom*=SCREEN_WIDTH; + int p = m_index; + + float iuf = iuadd; + float ivf = ivadd; + float irf = iradd; + float igf = igadd; + float ibf = ibadd; + + while (ytop < ybottom) { + int xstart = (int) (xleft + PIXEL_CENTER); + if (xstart < 0) + xstart = 0; + int xend = (int) (xrght + PIXEL_CENTER); + if (xend > SCREEN_WIDTH) + xend = SCREEN_WIDTH; + float xdiff = (xstart + PIXEL_CENTER) - xleft; + + int iu = (int) (iuf * xdiff + uleft); + int iv = (int) (ivf * xdiff + vleft); + int ir = (int) (irf * xdiff + rleft); + int ig = (int) (igf * xdiff + gleft); + int ib = (int) (ibf * xdiff + bleft); + float iz = izadd * xdiff + zleft; + + xstart+=ytop; + xend+=ytop; + + for ( ; xstart < xend; xstart++ ) { + try + { + if (noDepthTest || (iz <= m_zbuffer[xstart])) { + m_zbuffer[xstart] = iz; + + int red; + int grn; + int blu; + + if (m_bilinear) { + int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); + int iui = (iu & 0xFFFF) >> 9; + int ivi = (iv & 0xFFFF) >> 9; + + // get texture pixels + int pix0 = m_texture[ofs]; + int pix1 = m_texture[ofs + 1]; + ofs+=TEX_WIDTH; + int pix2 = m_texture[ofs]; + int pix3 = m_texture[ofs + 1]; + + // red + int red0 = (pix0 & 0xFF0000); + int red2 = (pix2 & 0xFF0000); + int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7); + int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7); + red = up + (((dn-up) * ivi) >> 7); + + // grn + red0 = (pix0 & 0xFF00); + red2 = (pix2 & 0xFF00); + up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7); + dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7); + grn = up + (((dn-up) * ivi) >> 7); + + // blu + red0 = (pix0 & 0xFF); + red2 = (pix2 & 0xFF); + up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7); + dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); + blu = up + (((dn-up) * ivi) >> 7); + } else { + // get texture pixel color + blu = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; + red = (blu & 0xFF0000); + grn = (blu & 0xFF00); + blu = blu & 0xFF; + } + + // + int r = (ir >> 16); + int g = (ig >> 16); + int b = (ib >> 16); + + // + m_pixels[xstart] = ( ((red * r) & 0xFF000000) | ((grn * g) & 0xFF0000) | (blu * b) ) >> 8; + m_stencil[xstart] = p; + } + } + catch (Exception e) { + } + + // + iu+=iuadd; + iv+=ivadd; + ir+=iradd; + ig+=igadd; + ib+=ibadd; + iz+=izadd; + } + + ytop+=SCREEN_WIDTH; + xleft+=leftadd; + xrght+=rghtadd; + uleft+=uleftadd; + vleft+=vleftadd; + rleft+=rleftadd; + gleft+=gleftadd; + bleft+=bleftadd; + zleft+=zleftadd; + } + } + + + /** + * Gouraud*texture blended with interpolating alpha + */ + private void drawsegment_gouraud_texture24_alpha + ( + float leftadd, + float rghtadd, + int ytop, + int ybottom + ) { + ytop*=SCREEN_WIDTH; + ybottom*=SCREEN_WIDTH; + int p = m_index; + + float iuf = iuadd; + float ivf = ivadd; + float irf = iradd; + float igf = igadd; + float ibf = ibadd; + float iaf = iaadd; + + while (ytop < ybottom) { + int xstart = (int) (xleft + PIXEL_CENTER); + if (xstart < 0) + xstart = 0; + int xend = (int) (xrght + PIXEL_CENTER); + if (xend > SCREEN_WIDTH) + xend = SCREEN_WIDTH; + float xdiff = (xstart + PIXEL_CENTER) - xleft; + + int iu = (int) (iuf * xdiff + uleft); + int iv = (int) (ivf * xdiff + vleft); + int ir = (int) (irf * xdiff + rleft); + int ig = (int) (igf * xdiff + gleft); + int ib = (int) (ibf * xdiff + bleft); + int ia = (int) (iaf * xdiff + aleft); + float iz = izadd * xdiff + zleft; + + xstart+=ytop; + xend+=ytop; + + for ( ;xstart < xend; xstart++ ) { + // get texture pixel color + try + { + //if (iz < m_zbuffer[xstart]) { + if (noDepthTest || (iz <= m_zbuffer[xstart])) { // [fry 041114] + //m_zbuffer[xstart] = iz; + + // blend + int al = ia >> 16; + + int red; + int grn; + int blu; + + if (m_bilinear) { + int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); + int iui = (iu & 0xFFFF) >> 9; + int ivi = (iv & 0xFFFF) >> 9; + + // get texture pixels + int pix0 = m_texture[ofs]; + int pix1 = m_texture[ofs + 1]; + ofs+=TEX_WIDTH; + int pix2 = m_texture[ofs]; + int pix3 = m_texture[ofs + 1]; + + // red + int red0 = (pix0 & 0xFF0000); + int red2 = (pix2 & 0xFF0000); + int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7); + int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7); + red = (up + (((dn-up) * ivi) >> 7)) >> 16; + + // grn + red0 = (pix0 & 0xFF00); + red2 = (pix2 & 0xFF00); + up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7); + dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7); + grn = (up + (((dn-up) * ivi) >> 7)) >> 8; + + // blu + red0 = (pix0 & 0xFF); + red2 = (pix2 & 0xFF); + up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7); + dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); + blu = up + (((dn-up) * ivi) >> 7); + } else { + blu = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; + red = (blu & 0xFF0000) >> 16; // 0 - 255 + grn = (blu & 0xFF00) >> 8; // 0 - 255 + blu = (blu & 0xFF); // 0 - 255 + } + + // multiply with gouraud color + red = (red * ir) >>> 8; // 0x00FF???? + grn = (grn * ig) >>> 16; // 0x0000FF?? + blu = (blu * ib) >>> 24; // 0x000000FF + + // get buffer pixels + int bb = m_pixels[xstart]; + int br = (bb & 0xFF0000); // 0x00FF0000 + int bg = (bb & 0xFF00); // 0x0000FF00 + bb = (bb & 0xFF); // 0x000000FF + + // + m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); + m_stencil[xstart] = p; + } + } + catch (Exception e) { + } + + // + iu+=iuadd; + iv+=ivadd; + ir+=iradd; + ig+=igadd; + ib+=ibadd; + ia+=iaadd; + iz+=izadd; + } + + ytop+=SCREEN_WIDTH; + xleft+=leftadd; + xrght+=rghtadd; + uleft+=uleftadd; + vleft+=vleftadd; + rleft+=rleftadd; + gleft+=gleftadd; + bleft+=bleftadd; + aleft+=aleftadd; + zleft+=zleftadd; + } + } + + + /** + * Gouraud*texture blended with interpolating alpha + */ + private void drawsegment_gouraud_texture32 + ( + float leftadd, + float rghtadd, + int ytop, + int ybottom + ) { + ytop*=SCREEN_WIDTH; + ybottom*=SCREEN_WIDTH; + //int p = m_index; + + float iuf = iuadd; + float ivf = ivadd; + float irf = iradd; + float igf = igadd; + float ibf = ibadd; + + while (ytop < ybottom) { + int xstart = (int) (xleft + PIXEL_CENTER); + if (xstart < 0) + xstart = 0; + int xend = (int) (xrght + PIXEL_CENTER); + if (xend > SCREEN_WIDTH) + xend = SCREEN_WIDTH; + float xdiff = (xstart + PIXEL_CENTER) - xleft; + + int iu = (int) (iuf * xdiff + uleft); + int iv = (int) (ivf * xdiff + vleft); + int ir = (int) (irf * xdiff + rleft); + int ig = (int) (igf * xdiff + gleft); + int ib = (int) (ibf * xdiff + bleft); + float iz = izadd * xdiff + zleft; + + xstart+=ytop; + xend+=ytop; + + for ( ; xstart < xend; xstart++ ) { + try + { + if (noDepthTest || (iz <= m_zbuffer[xstart])) { + //m_zbuffer[xstart] = iz; + + int red; + int grn; + int blu; + int al; + + if (m_bilinear) { + int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); + int iui = (iu & 0xFFFF) >> 9; + int ivi = (iv & 0xFFFF) >> 9; + + // get texture pixels + int pix0 = m_texture[ofs]; + int pix1 = m_texture[ofs + 1]; + ofs+=TEX_WIDTH; + int pix2 = m_texture[ofs]; + int pix3 = m_texture[ofs + 1]; + + // red + int red0 = (pix0 & 0xFF0000); + int red2 = (pix2 & 0xFF0000); + int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7); + int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7); + red = (up + (((dn-up) * ivi) >> 7)) >> 16; + + // grn + red0 = (pix0 & 0xFF00); + red2 = (pix2 & 0xFF00); + up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7); + dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7); + grn = (up + (((dn-up) * ivi) >> 7)) >> 8; + + // blu + red0 = (pix0 & 0xFF); + red2 = (pix2 & 0xFF); + up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7); + dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); + blu = up + (((dn-up) * ivi) >> 7); + + // alpha + pix0>>>=24; + pix2>>>=24; + up = pix0 + ((((pix1 >>> 24) - pix0) * iui) >> 7); + dn = pix2 + ((((pix3 >>> 24) - pix2) * iui) >> 7); + al = up + (((dn-up) * ivi) >> 7); + } else { + // get texture pixel color + blu = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; + al = (blu >>> 24); + red = (blu & 0xFF0000) >> 16; + grn = (blu & 0xFF00) >> 8; + blu = blu & 0xFF; + } + + // multiply with gouraud color + red = (red * ir) >>> 8; // 0x00FF???? + grn = (grn * ig) >>> 16; // 0x0000FF?? + blu = (blu * ib) >>> 24; // 0x000000FF + + // get buffer pixels + int bb = m_pixels[xstart]; + int br = (bb & 0xFF0000); // 0x00FF0000 + int bg = (bb & 0xFF00); // 0x0000FF00 + bb = (bb & 0xFF); // 0x000000FF + + // + m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); + } + } + catch (Exception e) { + } + + // + iu+=iuadd; + iv+=ivadd; + ir+=iradd; + ig+=igadd; + ib+=ibadd; + iz+=izadd; + } + + ytop+=SCREEN_WIDTH; + xleft+=leftadd; + xrght+=rghtadd; + uleft+=uleftadd; + vleft+=vleftadd; + rleft+=rleftadd; + gleft+=gleftadd; + bleft+=bleftadd; + zleft+=zleftadd; + } + } + + + /** + * Gouraud*texture blended with interpolating alpha + */ + private void drawsegment_gouraud_texture32_alpha + ( + float leftadd, + float rghtadd, + int ytop, + int ybottom + ) { + ytop*=SCREEN_WIDTH; + ybottom*=SCREEN_WIDTH; + int p = m_index; + + float iuf = iuadd; + float ivf = ivadd; + float irf = iradd; + float igf = igadd; + float ibf = ibadd; + float iaf = iaadd; + + while (ytop < ybottom) { + int xstart = (int) (xleft + PIXEL_CENTER); + if (xstart < 0) + xstart = 0; + int xend = (int) (xrght + PIXEL_CENTER); + if (xend > SCREEN_WIDTH) + xend = SCREEN_WIDTH; + float xdiff = (xstart + PIXEL_CENTER) - xleft; + + int iu = (int) (iuf * xdiff + uleft); + int iv = (int) (ivf * xdiff + vleft); + int ir = (int) (irf * xdiff + rleft); + int ig = (int) (igf * xdiff + gleft); + int ib = (int) (ibf * xdiff + bleft); + int ia = (int) (iaf * xdiff + aleft); + float iz = izadd * xdiff + zleft; + + xstart+=ytop; + xend+=ytop; + + for ( ;xstart < xend; xstart++ ) { + // get texture pixel color + try + { + //if (iz < m_zbuffer[xstart]) { + if (noDepthTest || (iz <= m_zbuffer[xstart])) { // [fry 041114] + //m_zbuffer[xstart] = iz; + + // blend + int al = ia >> 16; + + int red; + int grn; + int blu; + + if (m_bilinear) { + int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); + int iui = (iu & 0xFFFF) >> 9; + int ivi = (iv & 0xFFFF) >> 9; + + // get texture pixels + int pix0 = m_texture[ofs]; + int pix1 = m_texture[ofs + 1]; + ofs+=TEX_WIDTH; + int pix2 = m_texture[ofs]; + int pix3 = m_texture[ofs + 1]; + + // red + int red0 = (pix0 & 0xFF0000); + int red2 = (pix2 & 0xFF0000); + int up = red0 + ((((pix1 & 0xFF0000) - red0) * iui) >> 7); + int dn = red2 + ((((pix3 & 0xFF0000) - red2) * iui) >> 7); + red = (up + (((dn-up) * ivi) >> 7)) >> 16; + + // grn + red0 = (pix0 & 0xFF00); + red2 = (pix2 & 0xFF00); + up = red0 + ((((pix1 & 0xFF00) - red0) * iui) >> 7); + dn = red2 + ((((pix3 & 0xFF00) - red2) * iui) >> 7); + grn = (up + (((dn-up) * ivi) >> 7)) >> 8; + + // blu + red0 = (pix0 & 0xFF); + red2 = (pix2 & 0xFF); + up = red0 + ((((pix1 & 0xFF) - red0) * iui) >> 7); + dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); + blu = up + (((dn-up) * ivi) >> 7); + + // alpha + pix0>>>=24; + pix2>>>=24; + up = pix0 + ((((pix1 >>> 24) - pix0) * iui) >> 7); + dn = pix2 + ((((pix3 >>> 24) - pix2) * iui) >> 7); + al = al * (up + (((dn-up) * ivi) >> 7)) >> 8; + } else { + blu = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; + al = al * (blu >>> 24) >> 8; + red = (blu & 0xFF0000) >> 16; // 0 - 255 + grn = (blu & 0xFF00) >> 8; // 0 - 255 + blu = (blu & 0xFF); // 0 - 255 + } + + // multiply with gouraud color + red = (red * ir) >>> 8; // 0x00FF???? + grn = (grn * ig) >>> 16; // 0x0000FF?? + blu = (blu * ib) >>> 24; // 0x000000FF + + // get buffer pixels + int bb = m_pixels[xstart]; + int br = (bb & 0xFF0000); // 0x00FF0000 + int bg = (bb & 0xFF00); // 0x0000FF00 + bb = (bb & 0xFF); // 0x000000FF + + // + m_pixels[xstart] = ((br + (((red - br) * al) >> 8)) & 0xFF0000) |((bg + (((grn - bg) * al) >> 8)) & 0xFF00) | ((bb + (((blu - bb) * al) >> 8)) & 0xFF); + m_stencil[xstart] = p; + } + } + catch (Exception e) { + } + + // + iu+=iuadd; + iv+=ivadd; + ir+=iradd; + ig+=igadd; + ib+=ibadd; + ia+=iaadd; + iz+=izadd; + } + + ytop+=SCREEN_WIDTH; + xleft+=leftadd; + xrght+=rghtadd; + uleft+=uleftadd; + vleft+=vleftadd; + rleft+=rleftadd; + gleft+=gleftadd; + bleft+=bleftadd; + aleft+=aleftadd; + zleft+=zleftadd; + } + } +} diff --git a/dxf/RawDXF.java b/dxf/src/processing/dxf/RawDXF.java similarity index 100% rename from dxf/RawDXF.java rename to dxf/src/processing/dxf/RawDXF.java diff --git a/net/Client.java b/net/src/processing/net/Client.java similarity index 100% rename from net/Client.java rename to net/src/processing/net/Client.java diff --git a/net/Server.java b/net/src/processing/net/Server.java similarity index 100% rename from net/Server.java rename to net/src/processing/net/Server.java diff --git a/opengl/PGraphicsGL.java b/opengl/src/processing/opengl/PGraphicsGL.java similarity index 100% rename from opengl/PGraphicsGL.java rename to opengl/src/processing/opengl/PGraphicsGL.java diff --git a/pdf/PGraphicsPDF.java b/pdf/src/processing/pdf/PGraphicsPDF.java similarity index 100% rename from pdf/PGraphicsPDF.java rename to pdf/src/processing/pdf/PGraphicsPDF.java diff --git a/serial/Serial.java b/serial/src/processing/serial/Serial.java similarity index 100% rename from serial/Serial.java rename to serial/src/processing/serial/Serial.java diff --git a/video/Capture.java b/video/src/processing/video/Capture.java similarity index 100% rename from video/Capture.java rename to video/src/processing/video/Capture.java diff --git a/video/Movie.java b/video/src/processing/video/Movie.java similarity index 100% rename from video/Movie.java rename to video/src/processing/video/Movie.java