From 0cb256c8e5a8a6468489cbbfdefc1ec266001c0d Mon Sep 17 00:00:00 2001 From: benfry Date: Wed, 7 Jul 2004 21:58:47 +0000 Subject: [PATCH] initial checkin for new class/package for "core" --- core/PApplet.java | 3630 +++++++++++++++++++++++++ core/PConstants.java | 300 +++ core/PFont.java | 709 +++++ core/PGraphics.java | 6017 ++++++++++++++++++++++++++++++++++++++++++ core/PImage.java | 1000 +++++++ core/PLibrary.java | 44 + core/PLine.java | 1426 ++++++++++ core/PPolygon.java | 712 +++++ core/PTriangle.java | 2537 ++++++++++++++++++ core/done.txt | 66 + core/license.txt | 456 ++++ core/todo.txt | 420 +++ 12 files changed, 17317 insertions(+) create mode 100644 core/PApplet.java create mode 100644 core/PConstants.java create mode 100644 core/PFont.java create mode 100644 core/PGraphics.java create mode 100644 core/PImage.java create mode 100644 core/PLibrary.java create mode 100644 core/PLine.java create mode 100644 core/PPolygon.java create mode 100644 core/PTriangle.java create mode 100644 core/done.txt create mode 100644 core/license.txt create mode 100644 core/todo.txt diff --git a/core/PApplet.java b/core/PApplet.java new file mode 100644 index 000000000..876d44fbd --- /dev/null +++ b/core/PApplet.java @@ -0,0 +1,3630 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + BApplet - applet base class for the bagel engine + Part of the Processing project - http://processing.org + + Copyright (c) 2001-03 + Ben Fry, Massachusetts Institute of Technology and + Casey Reas, Interaction Design Institute Ivrea + + 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 +*/ + +import java.applet.*; +import java.awt.*; +import java.awt.event.*; +import java.awt.image.*; +import java.io.*; +import java.lang.reflect.*; +import java.net.URL; +import java.text.*; +import java.util.*; + +#ifdef SERIAL +#ifndef RXTX +import javax.comm.*; +#else +// rxtx uses package gnu.io, but all the class names +// are the same as those used by javax.comm +import gnu.io.*; +#endif +#endif + + +public class BApplet extends Applet + implements BConstants, Runnable, + MouseListener, MouseMotionListener, KeyListener +#ifdef SERIAL + , SerialPortEventListener +#endif +{ + //static final String JDK_VERSION = + static final double jdkVersion = + toDouble(System.getProperty("java.version").substring(0,3)); + /* + static private boolean isOneTwoOrBetter() + { + return VERSION_MAJOR.equals("1.2") || VERSION_MAJOR.equals("1.3") + || VERSION_MAJOR.equals("1.4") || VERSION_MAJOR.equals("1.5"); + } + */ + + public BGraphics g; + + static final boolean THREAD_DEBUG = true; + + // java/memimgsrc specific + public int pixels[]; + //DirectColorModel cm; + //MemoryImageSource mis; + //Image image; + // end java/memimgsrc specific + + public int mouseX, mouseY; + public int pmouseX, pmouseY; + boolean firstMouseEvent; + + public boolean mousePressed; + public MouseEvent mouseEvent; + //boolean mousePressedBriefly; // internal only + + public int key; + //public int keyCode; + public boolean keyPressed; + public KeyEvent keyEvent; + //boolean keyPressedBriefly; // internal only + + long millisOffset; + + // getting the frame rate + protected float fps = 10; + protected long fpsLastMillis = 0; + + // setting the frame rate + protected long fpsLastDelayTime = 0; + protected float fpsTarget = 0; + + boolean drawMethod; + boolean loopMethod; + + // true if inside the loop method + boolean insideLoop; + + // used for mouse tracking so that pmouseX doesn't get + // updated too many times while still inside the loop + // instead, it's updated only before/after the loop() + int qmouseX, qmouseY; + + // queue for whether to call the simple mouseDragged or + // mouseMoved functions. these are called after beginFrame + // but before loop() is called itself, to avoid problems + // in synchronization. + boolean qmouseDragged; + boolean qmouseMoved; + + // used to set pmouseX/Y to mouseX/Y the first time + // mouseX/Y are used, otherwise pmouseX/Y would always + // be zero making a big jump. yech. + boolean firstFrame; + + // current frame number (could this be used to replace firstFrame?) + public int frame; + + boolean finished; + boolean drawn; + Thread thread; + + Exception exception; // the last exception thrown + + static final int DEFAULT_WIDTH = 100; + static final int DEFAULT_HEIGHT = 100; + int width, height; + +#ifdef LIBRARIES + int libraryCount; + BLibrary libraries[]; +#endif + + // this text isn't seen unless BApplet is used on its + // own and someone takes advantage of leechErr.. not likely + static final String LEECH_WAKEUP = "Error while running applet."; + PrintStream leechErr; + + // message to send if attached as an external vm + static final String EXTERNAL_FLAG = "--external="; + static final char EXTERNAL_EXACT_LOCATION = 'e'; + static final char EXTERNAL_STOP = 's'; + static final String EXTERNAL_QUIT = "__QUIT__"; + static final String EXTERNAL_MOVE = "__MOVE__"; + boolean externalRuntime; + + + public void init() { + //checkParams(); + + // can/may be resized later + //g = new BGraphics(DEFAULT_WIDTH, DEFAULT_HEIGHT); + initg(); + + addMouseListener(this); + addMouseMotionListener(this); + addKeyListener(this); + //timing = true; + millisOffset = System.currentTimeMillis(); + + finished = false; // just for clarity + drawn = false; + firstFrame = true; + + // this will be cleared by loop() if it is not overridden + drawMethod = true; + loopMethod = true; + firstMouseEvent = true; + + /* + // call setup for changed params + try { + setup(); + } catch (NullPointerException e) { + //e.printStackTrace(); + // this is probably because someone didn't call size() first + size(DEFAULT_WIDTH, DEFAULT_HEIGHT); + setup(); + } + + // do actual setup calls + if (g == null) { + // if programmer hasn't added a special graphics + // object, then setup a standard one + size(DEFAULT_WIDTH, DEFAULT_HEIGHT); + } + */ + + // 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; + g.applet = this; + +#ifdef SONIC + // init sound engine + // check if sonic() was called + if (!sonicInit) { + // sound not initiated + //print("no sound\n"); + this.length = 0; + + } else if (sonic == null) { + beginSound(); + } +#endif + } + + + // override for subclasses (i.e. opengl) + // so that init() doesn't have to be replicated + public void initg() { + g = new BGraphics(DEFAULT_WIDTH, DEFAULT_HEIGHT); + } + + + public void start() { + //System.out.println("BApplet.start"); + + thread = new Thread(this); + thread.start(); + +#ifdef SONIC + // start sound engine thread + // TODO: it starts too quickly, causes sound corruption at start + if (sonic != null) { + sonic.start(this); + } +#endif + } + + + // maybe start should also be used as the method for kicking + // the thread on, instead of doing it inside paint() + public void stop() { + if (thread != null) { + //thread.stop(); + thread = null; + } + + // DEPRECATED as of jdk 1.2.. ugh.. will this work? + /* + // kill off any associated threads + Thread threads[] = new Thread[Thread.activeCount()]; + Thread.enumerate(threads); + for (int i = 0; i < threads.length; i++) { + // sometimes these get killed off before i can get 'em + if (threads[i] == null) continue; + + if (threads[i].getName().indexOf("Thread-") == 0) { + //System.out.println("stopping " + threads[i].getName()); + threads[i].stop(); + } + } + */ + +#ifdef LIBRARIES + for (int i = 0; i < libraryCount; i++) { + libraries[i].stop(); + } +#endif + + // maybe these will have better luck here +#ifdef SERIAL + endSerial(); +#endif + +#ifdef NETWORK + endNet(); +#endif + +#ifdef VIDEO + endVideo(); +#endif + +#ifdef SONIC + endSound(); +#endif + } + + + public Dimension getPreferredSize() { + //println("getting pref'd size " + width + " " + height); + return new Dimension(width, height); + } + + + // ------------------------------------------------------------ + + + void setup() { + } + + + void draw() { + drawMethod = false; + } + + + void loop() { + loopMethod = false; + } + + + // ------------------------------------------------------------ + + + public void size(int iwidth, int iheight) { + //if (g != null) return; // would this ever happen? is this a good idea? + g.resize(iwidth, iheight); + + this.pixels = g.pixels; + this.width = g.width; + this.height = g.height; + + //allocate(); + + // set this here, and if not inside browser, getDocumentBase() + // will fail with a NullPointerException, and cause applet to + // be set to null. might be a better way to deal with that, but.. + g.applet = this; + + // do all the defaults down here, because + // subclasses need to go through this function + // NOT TRUE, SO REMOVING + //g.lighting = false; + } + + + /* + public void allocate() { + cm = new DirectColorModel(32, 0x00ff0000, 0x0000ff00, 0x000000ff); + + g = new BGraphics(width, height); + pixels = g.pixels; + + // because of a java bug.. unless pixels are registered as + // opaque before their first run, the memimgsrc will flicker + // and run very slowly (as it tries to do broken awt 1.1 alpha) + for (int i = 0; i < pixels.length; i++) pixels[i] = 0xffffffff; + + // setup MemoryImageSource + mis = new MemoryImageSource(width, height, pixels, 0, width); + mis.setFullBufferUpdates(true); // maybe this will help ipaq? + mis.setAnimated(true); + image = Toolkit.getDefaultToolkit().createImage(mis); + } + */ + + + boolean updated = false; + + public void update() { + if (firstFrame) firstFrame = false; + + if (THREAD_DEBUG) println(" 3a update() internal " + firstFrame); + /* + println("update() internal"); + Graphics graphics = this.getGraphics(); + //println("inside update " + graphics); + if (graphics != null) paint(graphics); + */ + //println("new update()"); + repaint(); + if (THREAD_DEBUG) println(" 3b update() internal " + firstFrame); + getToolkit().sync(); // force repaint now (proper method) + if (THREAD_DEBUG) println(" 3c update() internal " + firstFrame); + } + + public void update(Graphics screen) { + if (THREAD_DEBUG) println(" 4 update() external"); + paint(screen); + } + + synchronized public void paint(Graphics screen) { + if (THREAD_DEBUG) println(" 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; + + // 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(" 5b enter paint sync"); + + synchronized (g) { + //System.out.println("5b paint has sync"); + //Exception e = new Exception(); + //e.printStackTrace(); + + // moving this into BGraphics caused weird sluggishness on win2k + //g.mis.newPixels(pixels, g.cm, 0, width); // must call this + + // make sure the screen is visible and usable + if (g != null) { + screen.drawImage(g.image, 0, 0, null); + } + //System.out.println(" 6 exit paint"); + } + updated = true; + } + + + public void run() { + try { + while ((Thread.currentThread() == thread) && !finished) { + updated = false; + + if (BApplet.THREAD_DEBUG) println("nextFrame()"); + nextFrame(); + + // 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 { + //Thread.yield(); + // windows doesn't like 'yield', so have to sleep at least + // for some small amount of time. + if (BApplet.THREAD_DEBUG) System.out.println("gonna sleep"); + Thread.sleep(1); // sleep to make OS happy + if (BApplet.THREAD_DEBUG) System.out.println("outta sleep"); + } catch (InterruptedException e) { } + } + } + } catch (Exception e) { + // formerly in kjcapplet, now just checks to see + // if someone wants to leech off errors + + // note that this will not catch errors inside setup() + // those are caught by the PdeRuntime + + finished = true; + + if (leechErr != null) { + // if draw() mode, make sure that ui stops waiting + // and the run button quits out + leechErr.println(LEECH_WAKEUP); + e.printStackTrace(leechErr); + + } else { + System.err.println(LEECH_WAKEUP); + e.printStackTrace(); + } + } + } + + + public void nextFrame() { + mouseX = qmouseX; // only if updated? + mouseY = qmouseY; + + if (fpsTarget != 0) framerate_delay(); + + // attempt to draw a static image using draw() + if (!drawn) { + //synchronized (g.image) { + synchronized (g) { + // always do this once. empty if not overridden + g.beginFrame(); + draw(); + + if (drawMethod) { + g.endFrame(); + update(); + finished = true; + } + drawn = true; + } // end synch + } + + // if not a static app, run the loop + if (!drawMethod) { + synchronized (g) { + g.beginFrame(); + //mouseUpdated = false; + insideLoop = true; + loop(); + insideLoop = false; + + // 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. + if (qmouseMoved) { + mouseMoved(); + qmouseMoved = false; + } + if (qmouseDragged) { + mouseDragged(); + qmouseDragged = false; + } + g.endFrame(); + } // end synch + update(); + } + + // takedown + if (!loopMethod) { + finished = true; + } + + pmouseX = mouseX; + pmouseY = mouseY; + + // internal frame counter + frame++; + } + + + // ------------------------------------------------------------ + + + void mouseClicked() { } + + public void mouseClicked(MouseEvent e) { // can this be removed? + mouseClicked(); + } + + // needs 'public' otherwise kjc assumes private + void mousePressed() { } // for beginners + + // this needn't set mouseX/Y + // since mouseMoved will have already set it + public void mousePressed(MouseEvent e) { + mouseEvent = e; + mousePressed = true; + mousePressed(); + } + + void mouseReleased() { } // for beginners + + // this needn't set mouseX/Y + // since mouseReleased will have already set it + public void mouseReleased(MouseEvent e) { + mouseEvent = e; + mousePressed = false; + mouseReleased(); + } + + public void mouseEntered(MouseEvent e) { } + + public void mouseExited(MouseEvent e) { } + + void mouseDragged() { } // for beginners + + public void mouseDragged(MouseEvent e) { + mouseEvent = e; + qmouseX = e.getX(); + qmouseY = e.getY(); + if (firstMouseEvent) { + // set valid coordinates for pmouseX/Y so that they don't + // default to zero which would make them draw from the corner. + pmouseX = qmouseX; + pmouseY = qmouseY; + firstMouseEvent = false; + } + mousePressed = true; + + // call mouseDragged() on next trip through loop + qmouseDragged = true; + //mouseDragged(); + } + + void mouseMoved() { } // for beginners + + public void mouseMoved(MouseEvent e) { + mouseEvent = e; + qmouseX = e.getX(); + qmouseY = e.getY(); + if (firstMouseEvent) { + // set valid coordinates for pmouseX/Y so that they don't + // default to zero which would make them draw from the corner. + pmouseX = qmouseX; + pmouseY = qmouseY; + firstMouseEvent = false; + } + mousePressed = false; + + // call mouseMoved() on next trip through loop + qmouseMoved = true; + //mouseMoved(); + } + + + // ------------------------------------------------------------ + + + // if the key is a coded key, i.e. up/down/ctrl/shift/alt + // the 'key' comes through as 0xffff (65535) + // to make this simpler for beginners, just set the + // value for key as the code, so that the code looks like: + // keyPressed() { if (key == UP) { ... } } + // instead of + // keyPressed() { if (keyCode = 0xffff) { if (key == UP) { .. } } } + // or something else more difficult + + void keyTyped() { } + + public void keyTyped(KeyEvent e) { + keyEvent = e; + key = e.getKeyChar(); + if (key == 0xffff) key = e.getKeyCode(); + //keyCode = e.getKeyCode(); + keyTyped(); + } + + void keyPressed() { } + + public void keyPressed(KeyEvent e) { + keyEvent = e; + keyPressed = true; + key = e.getKeyChar(); + if (key == 0xffff) key = e.getKeyCode(); + //keyCode = e.getKeyCode(); + keyPressed(); + } + + void keyReleased() { } + + public void keyReleased(KeyEvent e) { + keyEvent = e; + keyPressed = false; + key = e.getKeyChar(); + if (key == 0xffff) key = e.getKeyCode(); + //keyCode = e.getKeyCode(); + keyReleased(); + } + + + // ------------------------------------------------------------ + + // getting the time + + + public int millis() { + return (int) (System.currentTimeMillis() - millisOffset); + } + + static public int second() { + return Calendar.getInstance().get(Calendar.SECOND); + } + + static public int minute() { + return Calendar.getInstance().get(Calendar.MINUTE); + } + + static public int hour() { + return Calendar.getInstance().get(Calendar.HOUR_OF_DAY); + } + + // if users want day of week or day of year, + // they can add their own functions + static public int day() { + return Calendar.getInstance().get(Calendar.DAY_OF_MONTH); + } + + static public int month() { + // months are number 0..11 so change to colloquial 1..12 + return Calendar.getInstance().get(Calendar.MONTH) + 1; + } + + static public int year() { + return Calendar.getInstance().get(Calendar.YEAR); + } + + + // ------------------------------------------------------------ + + // controlling time and playing god + + + /** + * I'm not sure if this is even helpful anymore. + */ + public void delay(int napTime) { + if (firstFrame) return; + if (napTime > 0) { + try { + Thread.sleep(napTime); + } catch (InterruptedException e) { } + } + } + + + public float framerate() { + if (fpsLastMillis != 0) { + float elapsed = (float) (System.currentTimeMillis() - fpsLastMillis); + if (elapsed != 0) { + fps = (fps * 0.9f) + ((1.0f / (elapsed / 1000.0f)) * 0.1f); + } + } + fpsLastMillis = System.currentTimeMillis(); + return fps; + } + + + public void framerate(float fpsTarget) { + this.fpsTarget = fpsTarget; + } + + protected void framerate_delay() { + if (fpsLastDelayTime == 0) { + fpsLastDelayTime = System.currentTimeMillis(); + return; + } + + long timeToLeave = fpsLastDelayTime + (long)(1000.0f / fpsTarget); + int napTime = (int) (timeToLeave - System.currentTimeMillis()); + fpsLastDelayTime = timeToLeave; + delay(napTime); + } + + + // ------------------------------------------------------------ + + + /** + * Is the applet online or not? This can be used to test how the + * applet should behave since online situations are different. + */ + public boolean online() { + try { + getAppletContext(); + } catch (NullPointerException e) { + return false; + } + return true; + } + + + /** + * Get a param from the web page, or (eventually) + * from a properties file. + */ + public String param(String what) { + if (online()) { + return getParameter(what); + + } else { + System.err.println("param() only works inside a web browser"); + } + return null; + } + + + /** + * Show status in the status bar of a web browser, or in the + * System.out console. Eventually this might show status in the + * p5 environment itself, rather than relying on the console. + */ + public void status(String what) { + if (online()) { + showStatus(what); + + } else { + System.out.println(what); // something more interesting? + } + } + + + /** + * Link to an external page without all the muss. Currently + * only works for applets, but eventually should be implemented + * for applications as well, using code from PdeBase. + */ + void link(String here) { + if (!online()) { + System.err.println("Can't open " + here); + System.err.println("link() only works inside a web browser"); + return; + } + + try { + getAppletContext().showDocument(new URL(here)); + + } catch (Exception e) { + System.err.println("Could not open " + here); + e.printStackTrace(); + } + } + + void link(String here, String there) { + if (!online()) { + System.err.println("Can't open " + here); + System.err.println("link() only works inside a web browser"); + return; + } + + try { + getAppletContext().showDocument(new URL(here), there); + + } catch (Exception e) { + System.err.println("Could not open " + here); + e.printStackTrace(); + } + } + + + public void die(String what) { + if (online()) { + System.err.println("i'm dead.. " + what); + + } else { + System.err.println(what); + System.exit(1); + } + } + + + public void die(String what, Exception e) { + e.printStackTrace(); + die(what); + } + + + // ------------------------------------------------------------ + + // client/server net stuff + +#ifdef NETWORK + + BServer server; + BClient client; + String net; + int net_mode = CLIENT; //0 Sever 1 Client default CLIENT + + + public void beginNet(String host, int port) { //client + net_mode = CLIENT; + client = new BClient(this, host, port); + client.start(); + } + + + public void beginNet(int port) { //server + net_mode = SERVER; + server = new BServer(this, port); + server.start(); + } + + + void netEvent() { // client message + } + + void netEvent(int event) { // server message + } + + + public void netWrite(String message) { + if (net_mode == CLIENT) { + client.writeData(message); + } else if (net_mode == SERVER) { + server.broadcast(message); + } + } + + + public void endNet() { + if (net_mode == CLIENT) { + if (client != null) client.destroy(); + } else if (net_mode == SERVER) { + if (server != null) server.destroy(); + } + } + +#endif + + + // ------------------------------------------------------------ + + // SONIC + +#ifdef SONIC + + BSonic sonic; + + static final int DEFAULT_LENGTH = 512; + + public int samples[]; + public int length = -1; // frame length + public int frequency = -1; // playback frequency + + public boolean sonicInit = false; + + public void beginSound() { + // iniate sonic without sound post processing, soundEvent does not work + if (sonicInit) return; + + sonicInit = true; + + if (length == -1) { + this.length = 0; // size is zero, we are no going to use it + } + + this.sonic = new BSonic(); // start sonic without a samples buffer + } + + public void beginSound(int l) { + // iniate sonic with sound post processing + if (sonicInit) return; + + sonicInit = true; + + if (l <= 0) { + l = DEFAULT_LENGTH; + } + + this.length = l; // set length of samples buffer (frame) + + frequency = BSonic.SAMPLING_RATE; + this.samples = new int[length]; // create samples buffer + this.sonic = new BSonic(samples); // initiate sonic with this samples buffer + } + + public void endSound() { + // kill sound thread if not killed already + if (sonic != null) { + sonic.stop(); + } + } + + public BSound loadSound(String filename) { + if (sonic == null || sonicInit == false) { + //start sonic in lite mode + beginSound(); + } + + BSound sound = sonic.loadSound(filename); + + if (sound == null) { + // error loading, assign empty sound and continue + sound = new BSound(1); + } + return sound; + } + + public BSound generate(int form, int freq) { + if (sonic == null) { + //start sonic in lite mode + beginSound(); + } + + return sonic.generate(form, freq); + } + + public BSound microphone() { + if (sonic == null) { + //start sonic in lite mode + beginSound(); + } + + return sonic.microphone(); + } + + void soundEvent() { } + + public void volume(float v) { + BSonic.volume(v); + } + + public void length(int l) { + // do not change block size once BSonic is created! + if (sonic != null) { + return; + } + + this.length = l; + } + + void frequency(int f) { + // do not change block size once BSonic is created! + if (sonic != null) { + return; + } + // mixer frequency cannot be changed for now + //this.frequency = f; + } + + + // sound properties + + public void volume(BSound sound, float v) { + sound.volume(v); + } + + public void speed(BSound sound,float s) { + sound.speed(s); + } + + public void jump(BSound sound, int s) { + sound.jump(s); + } + + // sound playback + + public void play(BSound sound) { + if (sonic == null) { + //start sonic in lite mode + beginSound(); + } + sonic.play(sound); + } + + public void repeat(BSound sound) { + if (sonic == null) { + //start sonic in lite mode + beginSound(); + } + sonic.repeat(sound); + } + + public void play(BSound sound, int in, int out) { + if (sonic == null) { + //start sonic in lite mode + beginSound(); + } + sonic.play(sound, in, out); + } + + public void repeat(BSound sound, int in, int out) { + if (sonic == null) { + //start sonic in lite mode + beginSound(); + } + sonic.repeat(sound, in, out); + } + + + public void pause(BSound sound) { + if (sonic == null) { + //start sonic in lite mode + beginSound(); + } + sonic.pause(sound); + } + + public void stop(BSound sound) { + if (sonicInit == false) { + //start sonic in lite mode + beginSound(); + } + sonic.stop(sound); + } + +#endif + + + // ------------------------------------------------------------ + + // VIDEO INPUT + +#ifdef VIDEO + + BVideo videoInput; //to be incorporated in BApplet + //int vpixels[]; //pixel array + //BImage vimage; //image containing the actual frame + //int vwidth, vheight; //video resolution + BImage video; + // does this need to be available? + // naming is awkward, and is only requested by user + // could make users keep track of it themselves + //int vfps; + + + public void beginVideo(int vfps) { + beginVideo(0, 0, vfps); + } + + + public void beginVideo(int vwidth, int vheight, int vfps) { + //int[] vpixels = new int[vwidth * vheight]; + //video = new BImage(vpixels, vwidth, vheight, RGB); + + try { + if ((vwidth != 0) && (vheight != 0)) { + // if set, open to a specific size + videoInput = new BVideo(this, vwidth, vheight, vfps, false); + } else { + // if not, open to the default size + videoInput = new BVideo(this, 320, 240, vfps, false); + } + videoInput.start(); + while (videoInput.image == null) { + try { + Thread.sleep(5); + } catch (InterruptedException e) { } + //println("waiting"); + } + video = videoInput.image; + + // hopefully this catches a ClassNotFoundException + // or whatever might come up on runtime if qtjava not available + } catch (Exception e) { + System.err.println("beginVideo() failed.."); + e.printStackTrace(); + } + } + + + public void beginVideo(String filename) { + videoInput = new BVideo(this, filename); + videoInput.play = true; + videoInput.loop = true; + videoInput.start(); + while (videoInput.image == null) { + try { + Thread.sleep(5); + } catch (InterruptedException e) { } + //println("waiting"); + } + video = videoInput.image; + } + + + public void endVideo() { + if (videoInput != null) { + //videoInput.stop(); + videoInput.dispose(); + } + } + + + void videoEvent() { + // weird.. not sure why this would be here.. + //videoInput.getPixelArray(vimage.pixels); + //videoEvent(); + } + + BVideo loadVideo(String requestFile) { + BVideo t = new BVideo(this, requestFile); + return t; + } + + void play(BVideo v) { + v.play(); + } + + void stop(BVideo v) { + v.stop(); + } + + void repeat(BVideo v) { + v.repeat(); + } + + void pause(BVideo v) { + v.pause(); + } + + void image(BVideo v, float x, float y) { + image(v.image, x, y); + } +#endif + + + // ------------------------------------------------------------ + + // SERIAL PORT ACTION + + +#ifdef SERIAL + // properties can be passed in for default values + // otherwise defaults to 9600 N81 + + // these could be made static, which might be a solution + // for the classloading problem.. because if code ran again, + // the static class would have an object that could be closed + + static SerialPort serialPort; + static InputStream serialInput; + static OutputStream serialOutput; + int serial; // last byte of data received + + private String serial_port = "COM1"; + private int serial_rate = 9600; + private char serial_parity = 'N'; + private int serial_databits = 8; + private float serial_stopbits = 1; + + + public void serialProperties(Properties props) { + //System.out.println("setting serial properties"); + serial_port = props.getProperty("serial.port", serial_port); + serial_rate = Integer.parseInt(props.getProperty("serial.rate", "9600")); + serial_parity = props.getProperty("serial.parity", "N").charAt(0); + serial_databits = Integer.parseInt(props.getProperty("serial.databits", "8")); + serial_stopbits = new Float(props.getProperty("serial.stopbits", "1")).floatValue(); + } + + + // opens using the defaults found in pde.properties + public void beginSerial() { + beginSerial(serial_port, serial_rate, + serial_parity, serial_databits, serial_stopbits); + //beginSerial(PdeApplet.getInteger("serial.rate")); + } + + // opens using default port from pde.properties, + public void beginSerial(int rate) { + beginSerial(serial_port, rate, + serial_parity, serial_databits, serial_stopbits); + } + + public void beginSerial(String port, int rate) { + beginSerial(port, rate, + serial_parity, serial_databits, serial_stopbits); + } + + public void beginSerial(String port) { + beginSerial(port, serial_rate, + serial_parity, serial_databits, serial_stopbits); + //beginSerial(PdeApplet.getInteger("serial.rate")); + } + + public void beginSerial(String port, int rate, + char iparity, int databits, float istopbits) { + if (serialPort != null) serialPort.close(); + + int parity = SerialPort.PARITY_NONE; + if (iparity == 'E') parity = SerialPort.PARITY_EVEN; + if (iparity == 'O') parity = SerialPort.PARITY_ODD; + + int stopbits = SerialPort.STOPBITS_1; + if (istopbits == 1.5f) stopbits = SerialPort.STOPBITS_1_5; + if (istopbits == 2) stopbits = SerialPort.STOPBITS_2; + + //ortname, rate, SerialPort.PARITY_NONE, + // SerialPort.DATABITS_8, SerialPort.STOPBITS_1); + try { + Enumeration portList = CommPortIdentifier.getPortIdentifiers(); + while (portList.hasMoreElements()) { + CommPortIdentifier portId = + (CommPortIdentifier) portList.nextElement(); + + if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { + if (portId.getName().equals(port)) { + serialPort = (SerialPort)portId.open("bagel applet serial", 2000); + serialInput = serialPort.getInputStream(); + serialOutput = serialPort.getOutputStream(); + serialPort.setSerialPortParams(rate, databits, stopbits, parity); + serialPort.addEventListener(this); + serialPort.notifyOnDataAvailable(true); + } + } + } + //if (serialPort == null) { + //System.err.println("could not find a serial port named " + port); + //} + } catch (Exception e) { + exception = e; + e.printStackTrace(); + serialPort = null; + serialInput = null; + serialOutput = null; + } + //System.out.println("done with beginserial"); + } + + + public void endSerial() { + try { + // do io streams need to be closed first? + if (serialInput != null) serialInput.close(); + if (serialOutput != null) serialOutput.close(); + } catch (Exception e) { + e.printStackTrace(); + } + serialInput = null; + serialOutput = null; + + try { + // close the port + if (serialPort != null) serialPort.close(); + //serialPort = null; // this doesn't seem to help, but maybe on win? + } catch (Exception e) { + e.printStackTrace(); + } + serialPort = null; + } + + + // needs 'public' otherwise kjc assumes private + void serialEvent() { } + + public void serialEvent(SerialPortEvent serialEvent) { + //System.out.println(serialEvent); + if (serialEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) { + try { + while (serialInput.available() > 0) { + serial = serialInput.read(); + //serialEvent = true; + serialEvent(); + } + } catch (IOException e) { + System.err.println("problem reading from the serial port"); + } + } + } + + + public void serialWrite(int what) { + //if (serialPort != null) { + try { + serialOutput.write(what & 0xff); + serialOutput.flush(); // hmm, not sure if a good idea + + } catch (Exception e) { // null pointer or serial port dead + //} else { + System.err.println("serial port not working"); + e.printStackTrace(); + } + } + + public void serialWrite(byte bytes[]) { + //if (serialPort != null) { + try { + serialOutput.write(bytes); + serialOutput.flush(); // hmm, not sure if a good idea + + } catch (Exception e) { // null pointer or serial port dead + //} else { + System.err.println("serial port not working"); + e.printStackTrace(); + } + } +#endif + + + // ------------------------------------------------------------ + + // SCREEN GRABASS + + + /** + * grab an image of what's currently in the drawing area. + * best used just before endFrame() at the end of your loop(). + * only creates .tif or .tga images, so if extension isn't specified + * it defaults to writing a tiff. + */ + public void saveFrame() { + if (online()) { + System.err.println("Can't use saveFrame() when running in a browser."); + return; + } + + save("screen-" + nf(frame, 4) + ".tif"); + } + + + /** + * Save the current frame as a .tif or .tga image. + * + * The String passed in can contain a series of # signs + * that will be replaced with the screengrab number. + * + * i.e. saveFrame("blah-####.tif"); + * // saves a numbered tiff image, replacing the + * // # signs with zeros and the frame number + */ + public void saveFrame(String what) { + if (online()) { + System.err.println("Can't use saveFrame() when running in a browser."); + return; + } + + int first = what.indexOf('#'); + int last = what.lastIndexOf('#'); + + if (first == -1) { + save(what); + + } else { + String prefix = what.substring(0, first); + int count = last - first + 1; + String suffix = what.substring(last + 1); + + save(prefix + nf(frame, count) + suffix); + } + } + + + // ------------------------------------------------------------ + + // CURSOR, base code contributed by amit pitaru + + + int cursor_type = ARROW; // cursor type + boolean cursor_visible = true; // cursor visibility flag + BImage invisible_cursor; + + + /** + * Set the cursor type + */ + void cursor(int _cursor_type) { + //if (cursor_visible && _cursor_type != cursor_type) { + setCursor(Cursor.getPredefinedCursor(_cursor_type)); + //} + cursor_visible = true; + cursor_type = _cursor_type; + } + + + /** + * Set a custom cursor to an image with a specific hotspot. + * Only works with JDK 1.2 and later. + * Currently seems to be broken on Java 1.4 for Mac OS X + */ + void cursor(BImage image, int hotspotX, int hotspotY) { + //if (!isOneTwoOrBetter()) { + if (jdkVersion < 1.2) { + System.err.println("cursor() error: Java 1.2 or higher is " + + "required to set cursors"); + System.err.println(" (You're using version " + + nf((float) jdkVersion, 1, 1) + ")"); + return; + } + + // don't set this as cursor type, instead use cursor_type + // to save the last cursor used in case cursor() is called + //cursor_type = Cursor.CUSTOM_CURSOR; + Image jimage = + createImage(new MemoryImageSource(image.width, image.height, + image.pixels, 0, image.width)); + + Toolkit tk = Toolkit.getDefaultToolkit(); + Point hotspot = new Point(hotspotX, hotspotY); + try { + Method mCustomCursor = + Toolkit.class.getMethod("createCustomCursor", + new Class[] { Image.class, + Point.class, + String.class, }); + Cursor cursor = + (Cursor)mCustomCursor.invoke(Toolkit.getDefaultToolkit(), + new Object[] { jimage, + hotspot, + "no cursor" }); + setCursor(cursor); + cursor_visible = true; + + } catch (NoSuchMethodError e) { + System.out.println("cursor() is not available on " + + nf((float)jdkVersion, 1, 1)); + } catch (IndexOutOfBoundsException e) { + System.err.println("cursor() error: the hotspot " + hotspot + + " is out of bounds for the given image."); + } catch (Exception e) { + System.err.println(e); + } + } + + + /** + * Show the cursor after noCursor() was called. + * Notice that the program remembers the last set cursor type + */ + 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. + */ + void noCursor() { + if (!cursor_visible) return; // don't hide if already hidden. + + if (invisible_cursor == null) { + //invisible_cursor = new BImage(new int[32*32], 32, 32, RGBA); + invisible_cursor = new BImage(new int[16*16], 16, 16, RGBA); + } + // 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(boolean what) { + System.out.print(what); + System.out.flush(); + } + + static public void print(char what) { + System.out.print(what); + System.out.flush(); + } + + static public void print(int what) { + System.out.print(what); + System.out.flush(); + } + + static public void print(float what) { + System.out.print(what); + System.out.flush(); + } + + static public void print(double what) { + System.out.print(what); + System.out.flush(); + } + + static public void print(String what) { + System.out.print(what); + System.out.flush(); + } + + static public void print(Object what) { + System.out.print(what.toString()); + System.out.flush(); + } + + static public void println(boolean what) { + print(what); System.out.println(); + } + + static public void println(char what) { + print(what); System.out.println(); + } + + static public void println(int what) { + print(what); System.out.println(); + } + + static public void println(float what) { + print(what); System.out.println(); + } + + static public void println(double what) { + print(what); System.out.println(); + } + + static public void println(String what) { + print(what); System.out.println(); + } + + static public void println(Object what) { + System.out.println(what.toString()); + } + + static public void println() { + System.out.println(); + } + + + // ------------------------------------------------------------ + + // math stuff for convenience + + + 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 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 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); + } + + + static public final float sin(float angle) { + return (float)Math.sin(angle); + } + + static public final float cos(float angle) { + return (float)Math.cos(angle); + } + + static public final float tan(float angle) { + return (float)Math.tan(angle); + } + + static public final float atan2(float a, float 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 float ceil(float what) { + return (float) Math.ceil(what); + } + + static public final float floor(float what) { + return (float) Math.floor(what); + } + + static public final float round(float what) { + return Math.round(what); + } + + + static public final float 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)); + } + + + static Random internalRandom; + + /** + * Return a random number in the range [0, howbig) + * (0 is inclusive, non-inclusive of howbig) + */ + static 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) + * (inclusive of howsmall, non-inclusive of 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?) + */ + static public final float random(float howsmall, float howbig) { + if (howsmall >= howbig) return howsmall; + + float diff = howbig - howsmall; + return random(diff) + howsmall; + + /* + if (internalRandom == null) internalRandom = new Random(); + + float diff = howbig - howsmall; + //return howsmall + (float)Math.random() * diff; + float value = 0; + do { + //value = howsmall + (float)Math.random() * diff; + value = howsmall + internalRandom.nextFloat() * diff; + } while (value == howbig); // don't allow inclusive + return value; + */ + } + + + /* + Computes the Perlin noise function value at the point (x, y, z). + + [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 + + @param x x coordinate + @param y y coordinate + @param z z coordinate + @return the noise function value at (x, y, z) + */ + + 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; + } + + + // ------------------------------------------------------------ + + // utilities for dealing with arrays + + + 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; + } + + + /* + + // this appears to be worthless, because it will always + // throw a ClassCastException + + // maybe this could be done with reflection? + + static public Object[] expand(Object list[]) { + return expand(list, list.length << 1); + } + + static public Object[] expand(Object list[], int newSize) { + Object temp[] = new Object[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 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) { + return subset(list, start, list.length - start); + } + + static public Object[] subset(Object list[], int start, int count) { + Object output[] = new Object[count]; + System.arraycopy(list, start, output, 0, count); + return output; + } + + + // ------------------------------------------------------------ + + // utilities for dealing with 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. + * + * 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 BConstants: + * + * 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; + } + + + /** + * Wrapper for tedious Integer.parseInt function + */ + static public int toInt(String what) { + return toInt(what, 0); + } + + /** + * Wrapper for tedious Integer.parseInt function + */ + static public int toInt(String what, int otherwise) { + try { + return Integer.parseInt(what); + } catch (NumberFormatException e) { } + + return otherwise; + } + + + /** + * 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; + } + + + /** + * Wrapper for tedious new Float(string).floatValue(). + */ + static public float toFloat(String what) { + //return new Float(what).floatValue(); + return toFloat(what, Float.NaN); + } + + + /** + * Wrapper for tedious new Float(string).floatValue(). + */ + static public float toFloat(String what, float otherwise) { + //return new Float(what).floatValue(); + try { + return new Float(what).floatValue(); + } catch (NumberFormatException e) { } + + return otherwise; + } + + + /** + * Convert an array of Strings into an array of floats. + * See the documentation for toInt(). + */ + static public float[] toFloat(String what[]) { + return toFloat(what, 0); + } + + /** + * Convert an array of Strings into an array of floats. + * See the documentation for toInt(). + */ + static 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; + } + + + /** + * Wrapper for tedious Long.parseLong(). + */ + static public long toLong(String what) { + return Long.parseLong(what); + } + + + /** + * Convert an array of Strings into an array of longs. + * See the documentation for toInt(). + */ + static public long[] toLong(String what[]) { + return toLong(what, 0); + } + + /** + * Convert an array of Strings into an array of longs. + * See the documentation for toInt(). + */ + static public long[] toLong(String what[], int missing) { + long output[] = new long[what.length]; + for (int i = 0; i < what.length; i++) { + try { + output[i] = Long.parseLong(what[i]); + } catch (NumberFormatException e) { + output[i] = missing; + } + } + return output; + } + + + /** + * Wrapper for tedious new Double(string).doubleValue() + */ + static public double toDouble(String what) { + return new Double(what).doubleValue(); + } + + + /** + * Convert an array of Strings into an array of doubles. + * See the documentation for toInt(). + */ + static public double[] toDouble(String what[]) { + return toDouble(what, 0); + } + + /** + * Convert an array of Strings into an array of doubles. + * See the documentation for toInt(). + */ + static public double[] toDouble(String what[], double missing) { + double output[] = new double[what.length]; + for (int i = 0; i < what.length; i++) { + try { + output[i] = new Double(what[i]).doubleValue(); + } catch (NumberFormatException e) { + output[i] = missing; + } + } + return output; + } + + + // ------------------------------------------------------------ + + // number formatting - floats + + + static private NumberFormat float_nf; + static private int float_nf_left, float_nf_right; + + static public String[] nf(float num[], int left, int right) { + String formatted[] = new String[num.length]; + for (int i = 0; i < formatted.length; i++) { + formatted[i] = nf(num[i], left, right); + } + return formatted; + } + + static public String nf(float num, int left, int right) { + if ((float_nf != null) && + (float_nf_left == left) && (float_nf_right == right)) { + return float_nf.format(num); + } + + float_nf = NumberFormat.getInstance(); + float_nf.setGroupingUsed(false); // no commas + + if (left != 0) float_nf.setMinimumIntegerDigits(left); + if (right != 0) { + float_nf.setMinimumFractionDigits(right); + float_nf.setMaximumFractionDigits(right); + } + float_nf_left = left; + float_nf_right = right; + return float_nf.format(num); + } + + + /** + * Number formatter that takes into account whether the number + * has a sign (positive, negative, etc) in front of it. + */ + static public String[] nfs(float num[], int left, int right) { + String formatted[] = new String[num.length]; + for (int i = 0; i < formatted.length; i++) { + formatted[i] = nfs(num[i], left, right); + } + return formatted; + } + + static public String nfs(float num, int left, int right) { + return (num < 0) ? nf(num, left, right) : (' ' + nf(num, left, right)); + } + + + static public String[] nfp(float num[], int left, int right) { + String formatted[] = new String[num.length]; + for (int i = 0; i < formatted.length; i++) { + formatted[i] = nfp(num[i], left, right); + } + return formatted; + } + + static public String nfp(float num, int left, int right) { + return (num < 0) ? nf(num, left, right) : ('+' + nf(num, left, right)); + } + + + // ------------------------------------------------------------ + + // number formatting - integers + + + /** + * Integer number formatter. + */ + static private NumberFormat int_nf; + static private int int_nf_digits; + + static public String[] nf(int num[], int digits) { + String formatted[] = new String[num.length]; + for (int i = 0; i < formatted.length; i++) { + formatted[i] = nf(num[i], digits); + } + return formatted; + } + + static public String nf(int num, int digits) { + if ((int_nf != null) && (int_nf_digits == digits)) { + return int_nf.format(num); + } + + int_nf = NumberFormat.getInstance(); + int_nf.setGroupingUsed(false); // no commas + int_nf.setMinimumIntegerDigits(digits); + int_nf_digits = digits; + return int_nf.format(num); + } + + + /** + * number format signed (or space) + * Formats a number but leaves a blank space in the front + * when it's positive so that it can be properly aligned with + * numbers that have a negative sign in front of them. + */ + static public String nfs(int num, int digits) { + return (num < 0) ? nf(num, digits) : (' ' + nf(num, digits)); + } + + static public String[] nfs(int num[], int digits) { + String formatted[] = new String[num.length]; + for (int i = 0; i < formatted.length; i++) { + formatted[i] = nfs(num[i], digits); + } + return formatted; + } + + // + + /** + * number format positive (or plus) + * Formats a number, always placing a - or + sign + * in the front when it's negative or positive. + */ + static public String nfp(int num, int digits) { + return (num < 0) ? nf(num, digits) : ('+' + nf(num, digits)); + } + + static public String[] nfp(int num[], int digits) { + String formatted[] = new String[num.length]; + for (int i = 0; i < formatted.length; i++) { + formatted[i] = nfp(num[i], digits); + } + return formatted; + } + + + // ------------------------------------------------------------ + + // run as application + + + public Stopper stopper; + + class Stopper implements Runnable { + //BApplet parent; + Thread thread; + + public Stopper() { + //this.parent = parent; + thread = new Thread(this); + thread.start(); + } + + public void run() { + while (thread != null) { + try { + //System.out.println("Stopper: blocking for input"); + int anything = System.in.read(); + //System.out.println("Stopper: got input " + anything); + + if (anything == EXTERNAL_STOP) { + finished = true; + //parent.finished = true; // kill parent + //parent = null; + thread = null; // kill self + } + } catch (IOException e) { + // not tested (needed?) but seems correct + finished = true; + thread = null; + //System.err.println("Stopper: error on reading"); + //e.printStackTrace(); + } + + try { + Thread.sleep(250); + } catch (InterruptedException e) { } + } + } + } + + public void setupExternal(Frame frame) { + externalRuntime = true; + stopper = new Stopper(); + + frame.addComponentListener(new ComponentAdapter() { + public void componentMoved(ComponentEvent e) { + //System.out.println(e); + Point where = ((Frame) e.getSource()).getLocation(); + //System.out.println(e); + System.err.println(BApplet.EXTERNAL_MOVE + " " + + where.x + " " + where.y); + System.err.flush(); + } + }); + + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + //if (externalRuntime) { + System.err.println(BApplet.EXTERNAL_QUIT); + System.err.flush(); // important + //} + System.exit(0); + } + }); + } + + + static public void main(String args[]) { + if (args.length < 1) { + System.err.println("error: BApplet "); + System.exit(1); + } + + try { + //boolean locationFound = false; + //externalRuntime = false; + boolean external = false; + //String str = "--external="; + if (args[0].indexOf(EXTERNAL_FLAG) == 0) external = true; + + Frame frame = new Frame(); + frame.pack(); // maybe get insets + Class c = Class.forName(args[external ? 1 : 0]); + BApplet applet = (BApplet) c.newInstance(); + applet.init(); + applet.start(); + + Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + + //String str = "--location="; + + if (external) { + String argh = args[0].substring(EXTERNAL_FLAG.length()); + boolean exact = argh.charAt(0) == EXTERNAL_EXACT_LOCATION; + if (exact) argh = argh.substring(1); + int location[] = toInt(split(argh, ',')); + + int x1 = location[0] - 20; + int y1 = location[1]; + + Insets insets = frame.getInsets(); // pack() first? + //System.out.println(insets); + + int minW = 120; + int minH = 120; + int windowW = + Math.max(applet.width, minW) + insets.left + insets.right; + int windowH = + Math.max(applet.height, minH) + insets.top + insets.bottom; + + if (x1 - windowW > 10) { // if it fits to the left of the window + frame.setBounds(x1 - windowW, y1, windowW, windowH); + + } else { + // if it fits inside the editor window, + // offset slightly from upper lefthand corner + // so that it's plunked inside the text area + x1 = location[0] + 66; + y1 = location[1] + 66; + + if ((x1 + windowW > screen.width - 33) || + (y1 + windowH > screen.height - 33)) { + // otherwise center on screen + x1 = (screen.width - windowW) / 2; + y1 = (screen.height - windowH) / 2; + } + frame.setBounds(x1, y1, windowW, windowH); //ww, wh); + } + + if (exact) { + // ignore the stuff above for window x y coords, but still use it + // for the bounds, and the placement of the applet within the window + //System.out.println("setting exact " + + //location[0] + " " + location[1]); + frame.setLocation(location[0], location[1]); + } + + frame.addComponentListener(new ComponentAdapter() { + public void componentMoved(ComponentEvent e) { + int newX = e.getComponent().getX(); + int newY = e.getComponent().getY(); + //System.out.println(newX + " " + newY); + } + }); + + frame.setLayout(null); + frame.add(applet); + frame.setBackground(SystemColor.control); + applet.setBounds((windowW - applet.width)/2, + insets.top + ((windowH - insets.top - insets.bottom) - + applet.height)/2, + windowW, windowH); + + applet.setupExternal(frame); + + } else { // !external + frame.setLayout(new BorderLayout()); + frame.add(applet, BorderLayout.CENTER); + frame.pack(); + + frame.setLocation((screen.width - applet.g.width) / 2, + (screen.height - applet.g.height) / 2); + + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + } + + frame.show(); + applet.requestFocus(); // get keydowns right away + + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + + + // ------------------------------------------------------------ + + // public functions from bagel + + + public void alpha(int alpha[]) { + g.alpha(alpha); + } + + + public void alpha(BImage alpha) { + g.alpha(alpha); + } + + + public int blendColor(int c1, int c2, int mode) { + return g.blendColor(c1, c2, mode); + } + + + public void toGrayscale() { + g.toGrayscale(); + } + + + public int get(int x, int y) { + return g.get(x, y); + } + + + public BImage get(int x, int y, int w, int h) { + return g.get(x, y, w, h); + } + + + public void set(int x, int y, int c) { + g.set(x, y, c); + } + + + public void set(int x, int y, BImage image) { + g.set(x, y, image); + } + + + public void copy(int sx, int sy, int dx, int dy) { + g.copy(sx, sy, dx, dy); + } + + + public void copy(BImage src, int sx, int sy, int dx, int dy) { + g.copy(src, sx, sy, dx, dy); + } + + + public void copy(int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2) { + g.copy(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + } + + + public void copy(BImage src, int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2) { + g.copy(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); + } + + + public void blend(BImage src, int sx, int sy, int dx, int dy, int mode) { + g.blend(src, sx, sy, dx, dy, mode); + } + + + public void blend(int sx, int sy, int dx, int dy, int mode) { + g.blend(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) { + g.blend(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); + } + + + public void blend(BImage src, int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2, int mode) { + g.blend(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); + } + + + public Object clone() throws CloneNotSupportedException { + return g.clone(); + } + + + public void save(String filename) { + g.save(filename); + } + + + public void defaults() { + g.defaults(); + } + + + public void beginFrame() { + g.beginFrame(); + } + + + public void endFrame() { + g.endFrame(); + } + + + public final float[] nextVertex() { + return g.nextVertex(); + } + + + public final void addTexture(BImage image) { + g.addTexture(image); + } + + + public final void addLine(int a, int b) { + g.addLine(a, b); + } + + + public final void addTriangle(int a, int b, int c) { + g.addTriangle(a, b, c); + } + + + public void beginShape() { + g.beginShape(); + } + + + public void beginShape(int kind) { + g.beginShape(kind); + } + + + public void texture(BImage image) { + g.texture(image); + } + + + public void textureMode(int texture_mode) { + g.textureMode(texture_mode); + } + + + public void normal(float nx, float ny, float nz) { + g.normal(nx, ny, nz); + } + + + public void vertex(float x, float y) { + g.vertex(x, y); + } + + + public void vertex(float x, float y, float u, float v) { + g.vertex(x, y, u, v); + } + + + public void vertex(float x, float y, float z) { + g.vertex(x, y, z); + } + + + public void vertex(float x, float y, float z, + float u, float v) { + g.vertex(x, y, z, u, v); + } + + + public void bezierVertex(float x, float y) { + g.bezierVertex(x, y); + } + + + public void bezierVertex(float x, float y, float z) { + g.bezierVertex(x, y, z); + } + + + public void curveVertex(float x, float y) { + g.curveVertex(x, y); + } + + + public void curveVertex(float x, float y, float z) { + g.curveVertex(x, y, z); + } + + + public void endShape_newgraphics() { + g.endShape_newgraphics(); + } + + + public void endShape() { + g.endShape(); + } + + + public void flat_image(BImage image, int sx1, int sy1) { + g.flat_image(image, sx1, sy1); + } + + + public void point(float x, float y) { + g.point(x, y); + } + + + public void point(float x, float y, float z) { + g.point(x, y, z); + } + + + public void line(float x1, float y1, float x2, float y2) { + g.line(x1, y1, x2, y2); + } + + + public void line(float x1, float y1, float z1, + float x2, float y2, float z2) { + g.line(x1, y1, z1, x2, y2, z2); + } + + + public void triangle(float x1, float y1, float x2, float y2, + float x3, float 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) { + g.quad(x1, y1, x2, y2, x3, y3, x4, y4); + } + + + public void rectMode(int mode) { + g.rectMode(mode); + } + + + public void rect(float x1, float y1, float x2, float y2) { + g.rect(x1, y1, x2, y2); + } + + + public void ellipseMode(int mode) { + g.ellipseMode(mode); + } + + + public void ellipse(float x, float y, float hradius, float vradius) { + g.ellipse(x, y, hradius, vradius); + } + + + public void box(float size) { + g.box(size); + } + + + public void box(float w, float h, float d) { + g.box(w, h, d); + } + + + public void sphereDetail(int res) { + g.sphereDetail(res); + } + + + public void sphere(float r) { + g.sphere(r); + } + + + public void sphere(float x, float y, float z, float r) { + g.sphere(x, y, z, 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 bezier(float x1, float y1, + float x2, float y2, + float x3, float y3, + float x4, float 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) { + g.bezier(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4); + } + + + public void bezierSegments(int segments) { + g.bezierSegments(segments); + } + + + public void curveSegments(int segments) { + g.curveSegments(segments); + } + + + public void curveTightness(float tightness) { + g.curveTightness(tightness); + } + + + 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 curve(float x1, float y1, + float x2, float y2, + float x3, float y3, + float x4, float 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) { + g.curve(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4); + } + + + public BImage loadImage(String filename) { + return g.loadImage(filename); + } + + + public BImage loadImage(String filename, boolean force) { + return g.loadImage(filename, force); + } + + + public void imageMode(int mode) { + g.imageMode(mode); + } + + + public void image(BImage image, float x1, float y1) { + g.image(image, x1, y1); + } + + + public void image(BImage image, + float x1, float y1, float x2, float y2) { + g.image(image, x1, y1, x2, y2); + } + + + public void image(BImage image, + float x1, float y1, float x2, float y2, + float u1, float v1, float u2, float v2) { + g.image(image, x1, y1, x2, y2, u1, v1, u2, v2); + } + + + public void cache(BImage image) { + g.cache(image); + } + + + public void cache(BImage images[]) { + g.cache(images); + } + + + public BFont loadFont(String name) { + return g.loadFont(name); + } + + + public void textFont(BFont which) { + g.textFont(which); + } + + + public void textFont(BFont which, float size) { + g.textFont(which, size); + } + + + public void textSize(float size) { + g.textSize(size); + } + + + public void textLeading(float leading) { + g.textLeading(leading); + } + + + public void textMode(int mode) { + g.textMode(mode); + } + + + public void textSpace(int space) { + g.textSpace(space); + } + + + public void text(char c, float x, float y) { + g.text(c, x, y); + } + + + public void text(char c, float x, float y, float z) { + g.text(c, x, y, z); + } + + + public void text(String s, float x, float y) { + g.text(s, x, y); + } + + + public void text(String s, float x, float y, float z) { + g.text(s, x, y, z); + } + + + public void text(int num, float x, float y) { + g.text(num, x, y); + } + + + public void text(int num, float x, float y, float z) { + g.text(num, x, y, z); + } + + + public void text(float num, float x, float y) { + g.text(num, x, y); + } + + + public void text(float num, float x, float y, float z) { + g.text(num, x, y, z); + } + + + public void push() { + g.push(); + } + + + public void pop() { + g.pop(); + } + + + public void resetMatrix() { + g.resetMatrix(); + } + + + 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) { + g.applyMatrix(n00, n01, n02, n03, n10, n11, n12, n13, n20, n21, n22, n23, n30, n31, n32, n33); + } + + + public void printMatrix() { + g.printMatrix(); + } + + + public void beginCamera() { + g.beginCamera(); + } + + + public void cameraMode(int icameraMode) { + g.cameraMode(icameraMode); + } + + + public void endCamera() { + g.endCamera(); + } + + + public void printCamera() { + g.printCamera(); + } + + + 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 objectX(float x, float y, float z) { + return g.objectX(x, y, z); + } + + + public float objectY(float x, float y, float z) { + return g.objectY(x, y, z); + } + + + public float objectZ(float x, float y, float z) { + return g.objectZ(x, y, z); + } + + + public void ortho(float left, float right, + float bottom, float top, + float near, float far) { + g.ortho(left, right, bottom, top, near, far); + } + + + public void perspective(float fovy, float aspect, float zNear, float zFar) { + g.perspective(fovy, aspect, zNear, zFar); + } + + + public void frustum(float left, float right, float bottom, + float top, float znear, float zfar) { + g.frustum(left, right, bottom, top, znear, zfar); + } + + + public void lookat(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) { + g.lookat(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); + } + + + public void translate(float tx, float ty) { + g.translate(tx, ty); + } + + + public void translate(float tx, float ty, float tz) { + g.translate(tx, ty, tz); + } + + + public void rotateX(float angle) { + g.rotateX(angle); + } + + + public void rotateY(float angle) { + g.rotateY(angle); + } + + + public void rotate(float angle) { + g.rotate(angle); + } + + + public void rotateZ(float angle) { + g.rotateZ(angle); + } + + + public void rotate(float angle, float v0, float v1, float v2) { + g.rotate(angle, v0, v1, v2); + } + + + public void scale(float s) { + g.scale(s); + } + + + public void scale(float sx, float sy) { + g.scale(sx, sy); + } + + + public void scale(float x, float y, float z) { + g.scale(x, y, z); + } + + + 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) { + g.transform(n00, n01, n02, n03, n10, n11, n12, n13, n20, n21, n22, n23, n30, n31, n32, n33); + } + + + public void colorMode(int icolorMode) { + g.colorMode(icolorMode); + } + + + public void colorMode(int icolorMode, float max) { + g.colorMode(icolorMode, max); + } + + + public void colorMode(int icolorMode, + float maxX, float maxY, float maxZ) { + g.colorMode(icolorMode, maxX, maxY, maxZ); + } + + + public void colorMode(int icolorMode, + float maxX, float maxY, float maxZ, float maxA) { + g.colorMode(icolorMode, maxX, maxY, maxZ, maxA); + } + + + public void noTint() { + g.noTint(); + } + + + public void tint(int rgb) { + g.tint(rgb); + } + + + public void tint(float gray) { + g.tint(gray); + } + + + public void tint(float gray, float alpha) { + g.tint(gray, alpha); + } + + + public void tint(float x, float y, float z) { + g.tint(x, y, z); + } + + + public void tint(float x, float y, float z, float a) { + g.tint(x, y, z, a); + } + + + public void noFill() { + g.noFill(); + } + + + public void fill(int rgb) { + g.fill(rgb); + } + + + public void fill(float gray) { + g.fill(gray); + } + + + public void fill(float gray, float alpha) { + g.fill(gray, alpha); + } + + + public void fill(float x, float y, float z) { + g.fill(x, y, z); + } + + + public void fill(float x, float y, float z, float a) { + g.fill(x, y, z, a); + } + + + public void strokeWeight(float weight) { + g.strokeWeight(weight); + } + + + public void strokeJoin(int join) { + g.strokeJoin(join); + } + + + public void strokeMiter(int miter) { + g.strokeMiter(miter); + } + + + public void noStroke() { + g.noStroke(); + } + + + public void stroke(int rgb) { + g.stroke(rgb); + } + + + public void stroke(float gray) { + g.stroke(gray); + } + + + public void stroke(float gray, float alpha) { + g.stroke(gray, alpha); + } + + + public void stroke(float x, float y, float z) { + g.stroke(x, y, z); + } + + + public void stroke(float x, float y, float z, float a) { + g.stroke(x, y, z, a); + } + + + public void background(int rgb) { + g.background(rgb); + } + + + public void background(float gray) { + g.background(gray); + } + + + public void background(float x, float y, float z) { + g.background(x, y, z); + } + + + public void background(BImage image) { + g.background(image); + } + + + public void clear() { + g.clear(); + } + + + public void lights() { + g.lights(); + } + + + public void noLights() { + g.noLights(); + } + + + public void smooth() { + g.smooth(); + } + + + public void noSmooth() { + g.noSmooth(); + } + + + public void hint(int which) { + g.hint(which); + } + + + public void unhint(int which) { + g.unhint(which); + } + + + public void message(int level, String message) { + g.message(level, message); + } + + + public void message(int level, String message, Exception e) { + g.message(level, message, e); + } + + + public InputStream openStream(String filename) throws IOException { + return g.openStream(filename); + } + + + public byte[] loadBytes(String filename) { + return g.loadBytes(filename); + } + + + static public byte[] loadBytes(InputStream input) { + return BGraphics.loadBytes(input); + } + + + public String[] loadStrings(String filename) { + return g.loadStrings(filename); + } + + + static public String[] loadStrings(InputStream input) { + return BGraphics.loadStrings(input); + } + + + public void saveBytes(String filename, byte buffer[]) { + g.saveBytes(filename, buffer); + } + + + public void saveBytes(OutputStream output, byte buffer[]) { + g.saveBytes(output, buffer); + } + + + public void saveStrings(String filename, String strings[]) { + g.saveStrings(filename, strings); + } + + + public void saveStrings(OutputStream output, String strings[]) { + g.saveStrings(output, strings); + } + + + public void sort(String what[]) { + g.sort(what); + } + + + public void sort(String what[], Object objects[]) { + g.sort(what, objects); + } + + + public void sort(int what[]) { + g.sort(what); + } + + + public void sort(int what[], Object objects[]) { + g.sort(what, objects); + } + + + public void sort(float what[]) { + g.sort(what); + } + + + public void sort(float what[], Object objects[]) { + g.sort(what, objects); + } + + + public void sort(double what[]) { + g.sort(what); + } + + + public void sort(double what[], Object objects[]) { + g.sort(what, objects); + } + + + public void sort(String what[], int count, Object objects[]) { + g.sort(what, count, objects); + } + + + public void sort(int what[], int count, Object objects[]) { + g.sort(what, count, objects); + } + + + public void sort(float what[], int count, Object objects[]) { + g.sort(what, count, objects); + } + + + public void sort(double what[], int count, Object objects[]) { + g.sort(what, count, objects); + } + + + public final int color(int x, int y, int z) { + return g.color(x, y, z); + } + + + public final int color(float x, float y, float z) { + return g.color(x, y, z); + } + + + public final int color(int x, int y, int z, int a) { + return g.color(x, y, z, a); + } + + + public final int color(float x, float y, float z, float a) { + return g.color(x, y, z, a); + } + + + 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); + } +} diff --git a/core/PConstants.java b/core/PConstants.java new file mode 100644 index 000000000..832702199 --- /dev/null +++ b/core/PConstants.java @@ -0,0 +1,300 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + BConstants - numbers shared throughout the bagel engine + Part of the Processing project - http://Proce55ing.net + + Copyright (c) 2001-03 + Ben Fry, Massachusetts Institute of Technology and + Casey Reas, Interaction Design Institute Ivrea + + 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 +*/ + +import java.awt.Cursor; +import java.awt.event.KeyEvent; + + +public interface BConstants { + + // 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; + + + // used by split, all the standard whitespace chars + // (uncludes 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 RGBA = 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; + + + // blend mode keyword definitions + + public final static int REPLACE = 0; + public final static int BLEND = 1; + public final static int ADD = 2; + public final static int SUBTRACT = 4; + public final static int LIGHTEST = 8; + public final static int DARKEST = 16; + + + // 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 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_DIAMETER = 3; + + + // uv texture orientation modes + + static final int NORMAL_SPACE = 0; // 0..1 + static final int IMAGE_SPACE = 1; + + + // text placement modes + + static final int SCREEN_SPACE = 2; + static final int OBJECT_SPACE = 3; + + + // text alignment modes + + static final int ALIGN_LEFT = 0; + static final int ALIGN_CENTER = 1; + static final int ALIGN_RIGHT = 2; + + + // stroke modes + + static final int SQUARE_ENDCAP = 1 << 0; + static final int ROUND_ENDCAP = 1 << 1; + static final int PROJECTED_ENDCAP = 1 << 2; + static final int STROKE_CAP_MASK = + SQUARE_ENDCAP | ROUND_ENDCAP | PROJECTED_ENDCAP; + + static final int MITERED_JOIN = 1 << 3; + static final int ROUND_JOIN = 1 << 4; + static final int BEVELED_JOIN = 1 << 5; + static final int STROKE_JOIN_MASK = + MITERED_JOIN | ROUND_JOIN | BEVELED_JOIN; + + + // lighting + + static final int DISABLED = 0; + static final int AMBIENT = 1; + static final int DIFFUSE = 2; + static final int SPECULAR = 3; + + + // net + + static final int CLIENT = 0; + static final int SERVER = 1; + + + // 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 + + 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; + + 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 NEW_GRAPHICS = 2; + static final int DISABLE_TEXT_SMOOTH = 3; + static final int DISABLE_SMOOTH_HACK = 4; + + static final int HINT_COUNT = 5; + + + ////////////////////////////////////////////////////////////// + + // 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 + static final int SG = 13; + static final int SB = 14; + static final int SA = 15; + + // not used in rendering + // only used for calculating colors + + static final int NX = 16; // normal + static final int NY = 17; + static final int NZ = 18; + + static final int VX = 19; // view space coords + static final int VY = 20; + static final int VZ = 21; + static final int VW = 22; + + static final int WT = 23; // stroke width + + //static final int SPY = 22; // for subpixel rendering + + static final int VERTEX_FIELD_COUNT = 24; + + // line fields + + static final int PA = 0; // point A + static final int PB = 1; // point B + static final int LI = 2; // shape index + static final int SM = 3; // stroke mode + + static final int LINE_FIELD_COUNT = 4; + + // triangle fields + + static final int VA = 0; // point A + static final int VB = 1; // point B + static final int VC = 2; // point B + static final int TI = 3; // shape index + static final int TEX = 4; // texture index + + static final int TRIANGLE_FIELD_COUNT = 5; +} diff --git a/core/PFont.java b/core/PFont.java new file mode 100644 index 000000000..fce2122e5 --- /dev/null +++ b/core/PFont.java @@ -0,0 +1,709 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + BFont - font object for text rendering + Part of the Processing project - http://processing.org + + Copyright (c) 2001-04 Massachusetts Institute of Technology + (Except where noted that the author is not Ben Fry) + + 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 +*/ + +import java.io.*; +import java.util.*; + +#ifdef JDK13 +// for font builder.. but prolly no need to ifdef +import java.awt.*; +import java.awt.image.*; +#endif + + +// value[] could be used to build a char to byte mapping table +// as the font is loaded.. +// when generating, use the native char mapping. + +public class BFont implements BConstants { + + /** + * 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 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 + }; + + static char[] charset; + static { + charset = new char[126-33+1 + EXTRA_CHARS.length]; + int index = 0; + for (int i = 33; i <= 126; i++) { + charset[index++] = (char)i; + } + for (int i = 0; i < EXTRA_CHARS.length; i++) { + charset[index++] = EXTRA_CHARS[i]; + } + }; + + + //int firstChar = 33; // always + int charCount; + BImage images[]; + + // image width, a power of 2 + // note! these will always be the same + int iwidth, iheight; + // float versions of the above + float iwidthf, iheightf; + + // mbox is just the font size (i.e. 48 for most vlw fonts) + int mbox; + + int value[]; // char code + int height[]; // height of the bitmap data + int width[]; // width of bitmap data + int setWidth[]; // width displaced by the char + int topExtent[]; // offset for the top + int leftExtent[]; // offset for the left + + // scaling, for convenience + float size; + float leading; + + boolean cached; + + + public BFont() { } // for BFontAI subclass and font builder + + + // can this throw an exception instead? + public BFont(String filename, BGraphics parent) throws IOException { + //this.parent = parent; + //this.valid = false; + + //try { + String lower = filename.toLowerCase(); + if (lower.endsWith(".vlw") || lower.endsWith(".vlw.gz")) { + //load_vlw_font(filename); + read(parent.openStream(filename)); + + //} else if (lower.endsWith(".fbf")) { + //load_fbf_font(filename); + + } else { + throw new IOException("don't know what type of file that is"); + } + cached = false; + size(); + //valid = true; + + //} catch (IOException e) { + //parent.message(COMPLAINT, "could not load font " + filename, e); + //} + } + + +#ifdef JDK13 + + public BFont(String name, int size) throws IOException { + this(new Font(name, Font.PLAIN, size), true); + } + + public BFont(String name, int size, boolean smooth) throws IOException { + this(new Font(name, Font.PLAIN, size), smooth); + } + + public BFont(Font font, boolean smooth) throws IOException { + //int firstChar = 33; + //int lastChar = 126; + + //this.charCount = lastChar - firstChar + 1; + this.charCount = charset.length; + this.mbox = font.getSize(); + + // size for image/texture is next power of 2 over font size + iwidth = iheight = (int) + Math.pow(2, Math.ceil(Math.log(mbox) / Math.log(2))); + + iwidthf = iheightf = (float) iwidth; + + /* + iwidth = (int) + Math.pow(2, Math.ceil(Math.log(mbox) / Math.log(2))); + iheight = (int) + Math.pow(2, Math.ceil(Math.log(mbox) / Math.log(2))); + + iwidthf = (float) iwidth; + iheightf = (float) iheight; + */ + + images = new BImage[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]; + + int mbox3 = mbox * 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); + + g.setFont(font); + FontMetrics metrics = g.getFontMetrics(); + + int index = 0; + for (int i = 0; i < charCount; i++) { + char c = charset[i]; + if (!font.canDisplay(c)) { // skip chars not in the font + continue; + } + + g.setColor(Color.white); + g.fillRect(0, 0, mbox3, mbox3); + g.setColor(Color.black); + g.drawString(String.valueOf(c), mbox, mbox * 2); + + // grabs copy of the current data.. so no updates (do each time) + Raster raster = playground.getData(); + + //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? + // 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 char " + ((char)i)); + // 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? + } + + value[index] = c; + height[index] = (maxY - minY) + 1; + width[index] = (maxX - minX) + 1; + setWidth[index] = metrics.charWidth(c); + + // offset from vertical location of baseline + // of where the char was drawn (mbox*2) + topExtent[index] = mbox*2 - minY; + + // offset from left of where coord was drawn + leftExtent[index] = minX - mbox; + + //System.out.println(height[index] + " " + width[index] + " " + + // setWidth[index] + " " + + // topExtent[index] + " " + leftExtent[index]); + + images[index] = new BImage(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++) { + //System.out.println("getting pixel " + x + " " + y); + int value = 255 - raster.getSample(x, y, 0); + int pindex = (y - minY) * width[index] + (x - minX); + images[index].pixels[pindex] = value; + //System.out.print(BApplet.nf(value, 3) + " "); + } + //System.out.println(); + } + //System.out.println(); + index++; + } + charCount = index; + } + +#endif + + + public void write(OutputStream output) throws IOException { + DataOutputStream os = new DataOutputStream(output); + + os.writeInt(charCount); + os.writeInt(8); // numBits + os.writeInt(mbox); // mboxX (font size) + os.writeInt(mbox); // mboxY (font size) + os.writeInt(0); // baseHt, ignored + os.writeInt(0); // 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++) { + //int bitmapSize = height[i] * width[i]; + //byte bitmap[] = new byte[bitmapSize]; + + for (int y = 0; y < height[i]; y++) { + for (int x = 0; x < width[i]; x++) { + os.write(images[i].pixels[y * width[i] + x] & 0xff); + } + } + } + os.flush(); + os.close(); // can/should i do this? + } + + + //private void load_vlw_font(String filename) throws IOException { + public void read(InputStream input) throws IOException { + DataInputStream is = new DataInputStream(input); + + charCount = is.readInt(); + int numBits = is.readInt(); + int mboxX = is.readInt(); // not used, just fontsize (48) + int mboxY = is.readInt(); // also just fontsize (48) + + // only store this one for leading calc + mbox = mboxY; + + // 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. + iwidth = (int) + Math.pow(2, Math.ceil(Math.log(mboxX) / Math.log(2))); + iheight = (int) + Math.pow(2, Math.ceil(Math.log(mboxY) / Math.log(2))); + + iwidthf = (float) iwidth; + iheightf = (float) iheight; + + // font size is 48, so default leading is 48 * 1.2 + // this is same as what illustrator uses for the default + //defaultLeading = ((float)mboxY / iheightf) * 1.2f; + + int baseHt = is.readInt(); // zero, ignored + is.readInt(); // ignore 4 for 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]; + + // 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(); + } + + images = new BImage[charCount]; + for (int i = 0; i < charCount; i++) { + //int pixels[] = new int[64 * 64]; + int pixels[] = new int[iwidth * iheight]; + //images[i] = new BImage(pixels, 64, 64, ALPHA); + images[i] = new BImage(pixels, iwidth, iheight, 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 x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + int valu = temp[y*w + x] & 0xff; + //images[i].pixels[y*64 + x] = valu; + images[i].pixels[y * iwidth + x] = valu; + // the following makes javagl more happy.. + // not sure what's going on + //(valu << 24) | (valu << 16) | (valu << 8) | valu; //0xffffff; + //System.out.print((images[i].pixels[y*64+x] > 128) ? "*" : "."); + } + //System.out.println(); + } + //System.out.println(); + } + //kind = VLW; + } + + + //boolean exists(char c) { + //return ((c >= firstChar) && (c - firstChar < charCount)); + //} + + + /** + * Get index for the char (convert from unicode to bagel charset). + * @return index into arrays or -1 if not found + */ + //static private int index(char c) { + //static public int index(char c) { + public int index(char c) { + // these chars required in all fonts + if ((c >= 33) && (c <= 126)) { + return c - 33; + } + // some other unicode char, hunt it out + //return index_hunt(c, 0, charset.length-1); + return index_hunt(c, 0, value.length-1); + } + + + // whups, this used the p5 charset rather than what was inside the font + // meaning that old fonts would crash.. fixed for 0069 + + //static private int index_hunt(int c, int start, int stop) { + private int index_hunt(int c, int start, int stop) { + //System.out.println("checking between " + start + " and " + stop); + int pivot = (start + stop) / 2; + + // if this is the char, then return it + //if (c == charset[pivot]) return pivot; + 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 < charset[pivot]) return index_hunt(c, start, pivot-1); + 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); + } + + + float kern(char a, char b) { + return 0; // * size, but since zero.. + } + + + public void size() { + size = 12; + } + + + public void size(float isize) { + size = isize; + //leading(); + } + + + public void leading() { + leading = size * ((float)mbox / iheightf) * 1.2f; + } + + + public void leading(float ileading) { + leading = ileading; + } + + + // supposedly this should be ok even in SCREEN_SPACE mode + // since the applet will set the 'size' of the font to iwidth + // (though this prolly breaks any sort of 'height' measurements) + public float width(char c) { + if (c == 32) return width('i'); + + int cc = index(c); + if (cc == -1) return 0; + + return ((float)setWidth[cc] / iwidthf) * size; + } + + + public float width(String string) { + //if (!valid) return 0; + float wide = 0; + float pwide = 0; + char previous = 0; + + char s[] = string.toCharArray(); + for (int i = 0; i < s.length; i++) { + if (s[i] == '\n') { + if (wide > pwide) pwide = wide; + wide = 0; + previous = 0; + + } else { + wide += width(s[i]); + if (previous != 0) { + wide += kern(previous, s[i]); + } + previous = s[i]; + } + } + return (pwide > wide) ? pwide : wide; + } + + + public void text(char c, float x, float y, BGraphics parent) { + text(c, x, y, 0, parent); + } + + + public void text(char c, float x, float y, float z, BGraphics parent) { + //if (!valid) return; + //if (!exists(c)) return; + + // eventually replace this with a table + // to convert the > 127 coded chars + //int glyph = c - 33; + int glyph = index(c); + if (glyph == -1) return; + + if (!cached) { + // cache on first run, to ensure a graphics context exists + parent.cache(images); + cached = true; + } + + if (parent.text_space == OBJECT_SPACE) { + float high = (float) height[glyph] / iheightf; + float bwidth = (float) width[glyph] / iwidthf; + float lextent = (float) leftExtent[glyph] / iwidthf; + float textent = (float) topExtent[glyph] / iheightf; + + int savedTextureMode = parent.texture_mode; + //boolean savedSmooth = parent.smooth; + boolean savedStroke = parent._stroke; + + parent.texture_mode = IMAGE_SPACE; + //parent.smooth = true; + parent.drawing_text = true; + parent._stroke = false; + + float x1 = x + lextent * size; + float y1 = y - textent * size; + float x2 = x1 + bwidth * size; + float y2 = y1 + high * size; + + // this code was moved here (instead of using parent.image) + // because now images use tint() for their coloring, which + // internally is kind of a hack because it temporarily sets + // the fill color to the tint values when drawing text. + // rather than doubling up the hack with this hack, the code + // is just included here instead. + + //System.out.println(x1 + " " + y1 + " " + x2 + " " + y2); + + parent.beginShape(QUADS); + parent.texture(images[glyph]); + parent.vertex(x1, y1, z, 0, 0); + parent.vertex(x1, y2, z, 0, height[glyph]); + parent.vertex(x2, y2, z, width[glyph], height[glyph]); + parent.vertex(x2, y1, z, width[glyph], 0); + parent.endShape(); + + parent.texture_mode = savedTextureMode; + //parent.smooth = savedSmooth; + parent.drawing_text = false; + parent._stroke = savedStroke; + + } else { // SCREEN_SPACE + int xx = (int) x + leftExtent[glyph];; + int yy = (int) y - topExtent[glyph]; + + //int x1 = xx + leftExtent[glyph]; + //int y1 = yy - topExtent[glyph]; + //int x2 = x1 + width[glyph]; + //int y2 = y1 + height[glyph]; + + int x0 = 0; + int y0 = 0; + int w0 = width[glyph]; + int h0 = height[glyph]; + + if ((xx >= parent.width) || (yy >= parent.height) || + (xx + w0 < 0) || (yy + h0 < 0)) return; + + if (xx < 0) { + x0 -= xx; + w0 += xx; + //System.out.println("x " + xx + " " + x0 + " " + w0); + xx = 0; + } + if (yy < 0) { + y0 -= yy; + h0 += yy; + //System.out.println("y " + yy + " " + y0 + " " + h0); + yy = 0; + } + if (xx + w0 > parent.width) { + //System.out.println("wide " + x0 + " " + w0); + w0 -= ((xx + w0) - parent.width); + } + if (yy + h0 > parent.height) { + h0 -= ((yy + h0) - parent.height); + } + + int fr = parent.fillRi; + int fg = parent.fillGi; + int fb = parent.fillBi; + int fa = parent.fillAi; + + int pixels1[] = images[glyph].pixels; + int pixels2[] = parent.pixels; + + //int index1 = y0 * iwidth; //0; + //int index2 = 0; + + //for (int row = 0; row < height[glyph]; row++) { + for (int row = y0; row < y0 + h0; row++) { + //for (int col = 0; col < width[glyph]; col++) { + for (int col = x0; col < x0 + w0; col++) { + int a1 = (fa * pixels1[row * iwidth + col]) >> 8; + //System.out.println(index1 + col); + //int a1 = (fa * pixels1[index1 + col]) >> 8; + int a2 = a1 ^ 0xff; + int p1 = pixels1[row * width[glyph] + col]; + int p2 = pixels2[(yy + row-y0)*parent.width + (xx+col-x0)]; + + pixels2[(yy + row-y0)*parent.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)); + } + //index1 += iwidth; + } + } + } + + /* + public final static int _blend(int p1, int p2, int a2) { + // scale alpha by alpha of incoming pixel + a2 = (a2 * (p2 >>> 24)) >> 8; + + int a1 = a2 ^ 0xff; + int r = (a1 * ((p1 >> 16) & 0xff) + a2 * ((p2 >> 16) & 0xff)) & 0xff00; + int g = (a1 * ((p1 >> 8) & 0xff) + a2 * ((p2 >> 8) & 0xff)) & 0xff00; + int b = (a1 * ( p1 & 0xff) + a2 * ( p2 & 0xff)) >> 8; + + return 0xff000000 | (r << 8) | g | b; + } + */ + + + private char c[] = new char[8192]; + + public void text(String str, float x, float y, BGraphics parent) { + text(str, x, y, 0, parent); + } + + public void text(String str, float x, float y, float z, BGraphics parent) { + float startX = x; + int index = 0; + char previous = 0; + + int length = str.length(); + if (length > c.length) { + c = new char[length + 10]; + } + str.getChars(0, length, c, 0); + + while (index < length) { + if (c[index] == '\n') { + x = startX; + y += leading; + previous = 0; + } else { + text(c[index], x, y, z, parent); + x += width(c[index]); + if (previous != 0) + x += kern(previous, c[index]); + previous = c[index]; + } + index++; + } + } +} diff --git a/core/PGraphics.java b/core/PGraphics.java new file mode 100644 index 000000000..146484cf2 --- /dev/null +++ b/core/PGraphics.java @@ -0,0 +1,6017 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + BGraphics - main graphics and rendering context for bagel + Part of the Processing project - http://processing.org + + Copyright (c) 2001-03 Massachusetts Institute of Technology + (Except where noted that the author is not Ben Fry) + + 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 +*/ + +import java.applet.*; +import java.awt.*; +import java.awt.event.*; +import java.awt.image.*; +import java.io.*; +import java.net.*; +import java.util.zip.*; + + +public class BGraphics extends BImage implements BConstants { + public Applet applet; // not comfortable with this being static + + // ........................................................ + + //int width, height; + public int width1, height1; // minus 1 + + int pixelCount; + //public int pixels[]; // wtf? appletviewer wants this pubic + int stencil[]; // stencil buffer used to antialias polygons + float zbuffer[]; + //boolean zbufferTainted; + + boolean depthTest; + + //int frameCount; + + // ........................................................ + + // specifics for java memoryimagesource + DirectColorModel cm; + MemoryImageSource mis; + Image image; + + // ........................................................ + + // needs to happen before background() is called + // and resize.. so it's gotta be outside + static boolean hints[] = new boolean[HINT_COUNT]; + static { + //hints[NEW_GRAPHICS] = true; + } + + + // ........................................................ + + // underscored_names are used for private functions or variables + + // internal values.. i.e. the var set by colorMode() + // becomes color_mode + // if it's a one-word feller like 'fill' or 'stroke' + // then an underscore is placed in front: _fill + + // color parameters + // internally, colors are 0..1 for the floats + // this makes the lighting computations clearer + int color_mode; + boolean color_scale; + float colorMaxX; + float colorMaxY; + float colorMaxZ; + float colorMaxA; + + // tint color + boolean _tint, tint_alpha; + float tintR, tintG, tintB, tintA; + int tintRi, tintGi, tintBi, tintAi; + int tint; + + // fill color + boolean _fill, fill_alpha; + float fillR, fillG, fillB, fillA; + int fillRi, fillGi, fillBi, fillAi; + int fill; + + // stroke color + boolean _stroke, stroke_alpha; + float strokeR, strokeG, strokeB, strokeA; + int strokeRi, strokeGi, strokeBi, strokeAi; + int stroke; + + float strokeWeight; + int strokeJoin; + int strokeMiter; + + // background color + boolean _background; + float backR, backG, backB; + int backRi, backGi, backBi; + int background; + + // internal color for setting/calculating + float calcR, calcG, calcB, calcA; + int calcRi, calcGi, calcBi, calcAi; + int calci; + boolean calc_alpha; + + // cache for hsb conversion values for get/set pixel + int cacheHsbKey; + float cacheHsbValue[] = new float[3]; // init to zero + + // lighting + static final int MAX_LIGHTS = 10; + boolean lighting; + float lightR[], lightG[], lightB[]; + float lightX[], lightY[], lightZ[]; + int lightKind[]; + + // inherited from BImage + //boolean smooth = false; // antialiasing + + // projection + //float prevProjX, prevProjY, prevProjZ; + //float projX, projY, projZ; + + // ........................................................ + + /** + * Model transformation of the form m[row][column], + * which is a "column vector" (as opposed to "row vector") matrix. + */ + float m00, m01, m02, m03; + float m10, m11, m12, m13; + float m20, m21, m22, m23; + float m30, m31, m32, m33; + + static final int MATRIX_STACK_DEPTH = 32; + float matrixStack[][] = new float[MATRIX_STACK_DEPTH][16]; + int matrixStackDepth; + + // ........................................................ + + // current 3D transformation matrix + int camera_mode; + //int projection; // none, perspective, or isometric + int dimensions; // 0, 2 (affine 2d), 3 (perspective/isometric) + + // perspective setup + float fov; + float eyeX, eyeY; + float eyeDist, nearDist, farDist; + float aspect; + + float p00, p01, p02, p03; // projection matrix + float p10, p11, p12, p13; + float p20, p21, p22, p23; + float p30, p31, p32, p33; + + // ........................................................ + + // shapes + + boolean shape; + int shapeKind; + + + // ........................................................ + + // OLD_GRAPHICS + + BPolygon polygon; // general polygon to use for shape + BPolygon fpolygon; // used to fill polys for tri or quad strips + BPolygon spolygon; // stroke/line polygon + float svertices[][]; // temp vertices used for stroking end of poly + + BPolygon tpolygon; // for calculating concave/convex + int TPOLYGON_MAX_VERTICES = 512; + int tpolygon_vertex_order[]; // = new int[MAX_VERTICES]; + + // ........................................................ + + // NEW_GRAPHICS + + int shape_index; + + // vertices + static final int DEFAULT_VERTICES = 512; + float vertices[][] = new float[DEFAULT_VERTICES][VERTEX_FIELD_COUNT]; + int vertex_count; // total number of vertices + int vertex_start; // pos of first vertex of current shape in vertices array + int vertex_end; // total number of vertex in current shape + // used for sorting points when triangulating a polygon + // warning - maximum number of vertices for a polygon is DEFAULT_VERTICES + int vertex_order[] = new int[DEFAULT_VERTICES]; + + // lines + static final int DEFAULT_LINES = 512; + BLine line; // used for drawing + int lines[][] = new int[DEFAULT_LINES][LINE_FIELD_COUNT]; + int lines_count; + + // triangles + static final int DEFAULT_TRIANGLES = 256; + BTriangle triangle; // used for rendering + int triangles[][] = new int[DEFAULT_TRIANGLES][TRIANGLE_FIELD_COUNT]; + int triangles_count; // total number of triangles + + // other options + public boolean clip = true; + public boolean z_order = true; + + // six planes + // (A,B,C in plane eq + D) + float frustum[][] = new float[6][4]; + float cp[] = new float[16]; // temporary + + + // ........................................................ + + // texture images + + int texture_mode = IMAGE_SPACE; + float textureU, textureV; + float normalX, normalY, normalZ; + + // OLD_GRAPHICS + boolean texture; + + // NEW_GRAPHICS + private BImage textureImage; + static final int DEFAULT_TEXTURES = 3; + BImage textures[] = new BImage[DEFAULT_TEXTURES]; + int texture_index; + + + // ........................................................ + + // changes + + boolean unchangedZ; + boolean strokeChanged; + boolean fillChanged; + boolean normalChanged; + + + // ........................................................ + + // curve vertices + + static final int CVERTEX_ALLOC = 128; + float cvertex[][] = new float[CVERTEX_ALLOC][VERTEX_FIELD_COUNT]; + int cvertexIndex; + boolean cverticesFlat; + + + // ........................................................ + + // 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[], 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 -1 && textures[tex] != null) { + triangle.setTexture(textures[tex]); + triangle.setUV(a[U], a[V], b[U], b[V], c[U], c[V]); + } + + triangle.setIntensities(a[R], a[G], a[B], a[A], + b[R], b[G], b[B], b[A], + c[R], c[G], c[B], 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(); + } + + // RENDER LINES + for (int i = 0; i < lines_count; i ++) { + float a[] = vertices[lines[i][PA]]; + float b[] = vertices[lines[i][PB]]; + int index = lines[i][LI]; + + 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]); + + line.setIndex(index); + + line.draw(); + } + } + } + + // note that the zbuffer was messed with for the next frame + //zbufferTainted = (dimensions != 0); + + // BLIT TO IMAGE (SCREEN) + //mis.newPixels(pixels, cm, 0, width); + //frameCount++; + + // moving this back here (post-68) because of macosx thread problem + mis.newPixels(pixels, cm, 0, width); + } + + + ////////////////////////////////////////////////////////////// + + // MEMORY HANDLING (NEW_GRAPHICS) + + + public final float[] nextVertex() { + if (!hints[NEW_GRAPHICS]) return polygon.nextVertex(); + + if (vertex_count == vertices.length) { + float temp[][] = new float[vertex_count<<1][VERTEX_FIELD_COUNT]; + System.arraycopy(vertices, 0, temp, 0, vertex_count); + vertices = temp; + System.out.println("allocating more vertices " + vertices.length); + } + + return vertices[vertex_count++]; + } + + + public final void addTexture(BImage image) { + + if (texture_index == textures.length - 1) { + BImage temp[] = new BImage[texture_index<<1]; + System.arraycopy(textures, 0, temp, 0, texture_index); + textures = temp; + System.out.println("allocating more textures " + textures.length); + } + + if(textures[0] != null) { + texture_index++; + } + + textures[texture_index] = image; + + return; + } + + + public final void addLine(int a, int b) { + + if (lines_count == lines.length) { + int temp[][] = new int[lines_count<<1][LINE_FIELD_COUNT]; + System.arraycopy(lines, 0, temp, 0, lines_count); + lines = temp; + System.out.println("allocating more lines " + lines.length); + } + + lines[lines_count][PA] = a; + lines[lines_count][PB] = b; + + // index -1 means line is a normal stroke + // other values indicate special blender mode + if(smooth && !_stroke) { + lines[lines_count][LI] = shape_index; + } else { + lines[lines_count][LI] = -1; + } + + lines[lines_count][SM] = strokeMiter | strokeJoin; //_strokeMode; + + lines_count ++; + + return; + } + + public final void addTriangle(int a, int b, int c) { + + if (triangles_count == triangles.length) { + int temp[][] = new int[triangles_count<<1][TRIANGLE_FIELD_COUNT]; + System.arraycopy(triangles, 0, temp, 0, triangles_count); + triangles = temp; + System.out.println("allocating more triangles " + triangles.length); + } + + triangles[triangles_count][VA] = a; + triangles[triangles_count][VB] = b; + triangles[triangles_count][VC] = c; + + if (textureImage == null) { + triangles[triangles_count][TEX] = -1; + } else { + triangles[triangles_count][TEX] = texture_index; + } + + triangles[triangles_count][TI] = shape_index; + + triangles_count ++; + + return; + } + + + ////////////////////////////////////////////////////////////// + + // LIGHTING AND COLOR + + /** + * lighting calculation of final colour. + * for now, ip is being done in screen space (transformed), + * because the normals are also being transformed + * + * @param r red component of object's colour + * @param g green of object's colour + * @param b blue of object's colour + * @param ix x coord of intersection + * @param iy y coord of intersection + * @param iz z coord of intersection + * @param nx x coord of normal vector + * @param ny y coord of normal + * @param nz z coord of normal + * @param target float array to store result + * @param toffset starting index in target array + */ + private void calc_lighting(float r, float g, float b, + float ix, float iy, float iz, + float nx, float ny, float nz, + float target[], int toffset) { + //System.out.println("calc_lighting normals " + nx + " " + ny + " " + nz); + + if (!lighting) { + target[toffset + 0] = r; + target[toffset + 1] = g; + target[toffset + 2] = b; + return; + } + + float nlen = mag(nx, ny, nz); + if (nlen != 0) { + nx /= nlen; ny /= nlen; nz /= nlen; + } + + // get direction based on inverse of perspective(?) matrix + //screenToWorld.getDirection(x + 0.5, y + 0.5, d); + + /* + // q in screen space + double qs[] = new double[4]; + qs[0] = x; + qs[1] = y; + qs[2] = 0; + qs[3] = 1; + + // q in world space + // transformed 4 vector (homogenous coords) + double qw[] = new double[4]; + multiply(mat, qs, qw); + dw.x = qw[0] * mat[3][2] - qw[3] * mat[0][2]; + dw.y = qw[1] * mat[3][2] - qw[3] * mat[1][2]; + dw.z = qw[2] * mat[3][2] - qw[3] * mat[2][2]; + */ + // multiply (inverse matrix) x (x y 0 1) = qw + + /* + // CALC OF DIRECTION OF EYE TO SCREEN/OBJECT + // !!! don't delete this code.. used for specular + float qwx = i00*sx + i01*sy + i03; + float qwy = i10*sx + i11*sy + i13; + float qwz = i20*sx + i21*sy + i23; + float qww = i30*sx + i31*sy + i33; + + float dwx = qwx*i32 - qww*i02; + float dwy = qwy*i32 - qww*i12; + float dwz = qwz*i32 - qww*i22; + */ + + //double kdr = material.kDiffuseReflection; == 1 + //double ksr = material.kSpecularReflection; == 0 + //double e = material.shadingExponent; == 0 + //RgbColor Cmat = material.color; == r, g, b + + // Direction of light i from ip, Li = L[i].position - ip + //Vector3 Li = new Vector3(); + + // Radiance of a light source, a color + //RgbColor Ii = new RgbColor(); + + // The halfway vector + //Vector3 Hi = new Vector3(); + + //float N_dot_Li, N_dot_Hi, N_dot_Hi_e; + + float diffuse_r = 0; // = lights[0].r; // sum in ambient term + float diffuse_g = 0; // = lights[0].g; + float diffuse_b = 0; // = lights[0].b; + + //float specular_r = 0; + //float specular_g = 0; + //float specular_b = 0; + + for (int i = 1; i < MAX_LIGHTS; i++) { + if (lightKind[i] == DISABLED) break; + + //Light light = (Light) list.value; + //Ii = light.color; + + //Vector3.subtract(light.position, ip, Li); + //Li.normalize(); + // li is the vector of the light as it points towards the point + // at which it intersects the object + float lix = lightX[i] - ix; + float liy = lightY[i] - iy; + float liz = lightZ[i] - iz; + float m = mag(lix, liy, liz); + if (m != 0) { + lix /= m; liy /= m; liz /= m; + } + float n_dot_li = (nx*lix + ny*liy + nz*liz); + //N_dot_Li = Vector3.dotProduct(N, Li); + + //if (N_dot_Li > 0.0) { + if (n_dot_li > 0) { + //System.out.println("n_dot_li = " + n_dot_li); + diffuse_r += lightR[i] * n_dot_li; + diffuse_g += lightG[i] * n_dot_li; + diffuse_b += lightB[i] * n_dot_li; + + /* + // not doing any specular for now + + //Vector3.subtract(light.position, direction, Hi); + float hix = lights[i].x - dwx; + float hiy = lights[i].y - dwy; + float hiz = lights[i].z - dwz; + float n_dot_hi = (nx*hix + ny*hiy + nz*hiz); + //N_dot_Hi = Vector3.dotProduct(N, Hi); + if (n_dot_hi > 0) { + //N_dot_Hi_e = pow(N_dot_Hi / Hi.getLength(), e); + // since e == 1 for now, this can be simplified + //float n_dot_hi_e = pow(n_dot_hi / sqrt(hix*hix + hiy*hiy + hiz*hiz), e); + float n_dot_hi_e = n_dot_hi / + sqrt(hix*hix + hiy*hiy + hiz*hiz); + specular_r += lights[i].r * n_dot_hi_e; + specular_g += lights[i].g * n_dot_hi_e; + specular_b += lights[i].b * n_dot_hi_e; + //specular_r += Ii.r * N_dot_Hi_e; + //specular_g += Ii.g * N_dot_Hi_e; + //specular_b += Ii.b * N_dot_Hi_e; + } + */ + } + } + // specular reflection (ksr) is set to zero, so simplify + //I.r = (kdr * Cmat.r * diffuse_r) + (ksr * specular_r); + //I.g = (kdr * Cmat.g * diffuse_g) + (ksr * specular_g); + //I.b = (kdr * Cmat.b * diffuse_b) + (ksr * specular_b); + + //System.out.println(r + " " + g + " " + b + " " + + // diffuse_r + " " + diffuse_g + " " + diffuse_b); + + // TODO ** this sucks! ** + //System.out.println(lights[0].r + " " + lights[0].g + " " + + // lights[0].b); + + target[toffset+0] = lightR[0] + (r * diffuse_r); + target[toffset+1] = lightG[0] + (g * diffuse_g); + target[toffset+2] = lightB[0] + (b * diffuse_b); + + if (target[toffset+0] > ONE) target[toffset+0] = ONE; + if (target[toffset+1] > ONE) target[toffset+1] = ONE; + if (target[toffset+2] > ONE) target[toffset+2] = ONE; + + //if (calc1) { + //calcR1 = lights[0].r + (r * diffuse_r); if (calcR1 > 1) calcR1 = 1; + //calcG1 = lights[0].g + (g * diffuse_g); if (calcG1 > 1) calcG1 = 1; + //calcB1 = lights[0].b + (b * diffuse_b); if (calcB1 > 1) calcB1 = 1; + + //System.out.println(255*calcR1 + " " + 255*calcG1 + " " + 255*calcB1); + //} else { + //calcR2 = lights[0].r + (r * diffuse_r); if (calcR2 > 1) calcR2 = 1; + //calcG2 = lights[0].g + (g * diffuse_g); if (calcG2 > 1) calcG2 = 1; + //calcB2 = lights[0].b + (b * diffuse_b); if (calcB2 > 1) calcB2 = 1; + //System.out.println(255*calcR2 + " " + 255*calcG2 + " " + 255*calcB2); + //} + } + + + ////////////////////////////////////////////////////////////// + + // SHAPES + + /** + * start a new shape of type POLYGON + */ + public void beginShape() { + beginShape(POLYGON); + } + + + /** + * start a new shape + * + * @param kind indicates shape type + */ + public void beginShape(int kind) { + shape = true; + shapeKind = kind; + + if (hints[NEW_GRAPHICS]) { + shape_index = shape_index + 1; + if (shape_index == -1) { + shape_index = 0; + } + + if (z_order == true) { + // continue with previous vertex, line and triangle count + // all shapes are rendered at endFrame(); + vertex_start = vertex_count; + vertex_end = 0; + + } else { + // reset vertex, line and triangle information + // every shape is rendered at endShape(); + vertex_count = 0; + line.reset(); + lines_count = 0; + triangle.reset(); + triangles_count = 0; + } + + textureImage = null; + } else { // OLD_GRAPHICS + polygon.reset(0); + fpolygon.reset(4); + spolygon.reset(4); + + texture = false; + polygon.interpUV = false; + } + cvertexIndex = 0; + cverticesFlat = true; + + unchangedZ = true; + strokeChanged = false; + fillChanged = false; + normalChanged = false; + } + + + /** + * set texture image for current shape + * needs to be called between @see beginShape and @see endShape + * + * @param image reference to a BImage object + */ + //public void textureImage(BImage image) { + public void texture(BImage image) { + if (hints[NEW_GRAPHICS]) { + if (z_order == true) { + addTexture(image); + } else { + triangle.setTexture(image); + } + textureImage = image; + + } else { // OLD_GRAPHICS + texture = true; polygon.texture(image); + } + } + + + /** + * set texture mode to either IMAGE_SPACE (more intuitive + * for new users) or NORMAL_SPACE (better for advanced chaps) + */ + public void textureMode(int texture_mode) { + this.texture_mode = texture_mode; + } + + + /** + * 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) + */ + //public void vertexTexture(float u, float v) { + protected void vertex_texture(float u, float v) { + if (hints[NEW_GRAPHICS]) { + if (textureImage == null) { + message(PROBLEM, "gotta use texture() " + + "after beginShape() and before vertexTexture()"); + return; + } + if (texture_mode == IMAGE_SPACE) { + textureU = (u < textureImage.width) ? u : textureImage.width; + if (textureU < 0) textureU = 0; + textureV = (v < textureImage.height) ? v : textureImage.height; + if (textureV < 0) textureV = 0; + textureU = u / (float) textureImage.width; + textureV = v / (float) textureImage.height; + + } else { // NORMAL_SPACE + textureU = u; + textureV = v; + if (textureU < 0) textureU = 0; + if (textureV < 0) textureV = 0; + if (textureU > ONE) textureU = ONE; + if (textureV > ONE) textureV = ONE; + } + + } else { // OLD_GRAPHICS + if (!texture) { + message(PROBLEM, "gotta use texture() " + + "after beginShape() and before vertex()"); + return; + } + if (texture_mode == IMAGE_SPACE) { + textureU = (u < polygon.twidth) ? u : polygon.twidth; + if (textureU < 0) textureU = 0; + + textureV = (v < polygon.theight) ? v : polygon.theight; + if (textureV < 0) textureV = 0; + + } else { + if (textureU < 0) textureU = 0; + if (textureV < 0) textureV = 0; + if (textureU > ONE) textureU = ONE; + if (textureV > ONE) textureV = ONE; + + textureU = u * polygon.twidth; + textureV = v * polygon.theight; + } + } + } + + + /** + * sets the current normal.. may apply to vertices if inside + * a beginShape, or to whatever else if outside + */ + //public void vertexNormal(float nx, float ny, float nz) { + public void normal(float nx, float ny, float nz) { + if (shape) { // if inside shape + if (!normalChanged) { + if (hints[NEW_GRAPHICS]) { + // set normals for vertices till now to the same thing + for (int i = vertex_start; i < vertex_end; i++) { + vertices[i][NX] = normalX; + vertices[i][NY] = normalY; + vertices[i][NZ] = normalZ; + } + + // [vertex change] + for (int i = vertex_start; i < vertex_end; i++) { + vertices[i][NX] = normalX; + vertices[i][NY] = normalY; + vertices[i][NZ] = normalZ; + } + + } else { // OLD_GRAPHICS + // set normals for vertices till now to the same thing + for (int i = 0; i < polygon.vertexCount; i++) { + polygon.vertices[i][NX] = normalX; + polygon.vertices[i][NY] = normalY; + polygon.vertices[i][NZ] = normalZ; + } + } + normalChanged = true; + } + } + + normalX = nx; + normalY = ny; + normalZ = nz; + } + + + public void vertex(float x, float y) { + setup_vertex(nextVertex(), x, y, 0); + } + + + public void vertex(float x, float y, float u, float v) { + vertex_texture(u, v); + setup_vertex(nextVertex(), x, y, 0); + } + + + public void vertex(float x, float y, float z) { + unchangedZ = false; + dimensions = 3; + setup_vertex(nextVertex(), x, y, z); + } + + + public void vertex(float x, float y, float z, + float u, float v) { + vertex_texture(u, v); + unchangedZ = false; + dimensions = 3; + setup_vertex(nextVertex(), x, y, z); + } + + + private void setup_vertex(float vertex[], float x, float y, float z) { + cvertexIndex = 0; // reset curves to start + + 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[WT] = strokeWeight; + } + + // this complicated if construct may defeat the purpose + if (((hints[NEW_GRAPHICS]) && (textureImage != null)) || + ((!hints[NEW_GRAPHICS]) && texture)) { + vertex[U] = textureU; + vertex[V] = textureV; + } + + if (normalChanged) { + vertex[NX] = normalX; + vertex[NY] = normalY; + vertex[NZ] = normalZ; + } + } + + + /* + CURVES + + 'curve' is a catmull-rom curve + + curve(x1, y1, x2, y2, x3, y3, x4, y4) + // is equivalent to: + beginShape(); + curveVertex(x1, y1); + curveVertex(x1, y1); + curveVertex(x2, y2); + curveVertex(x3, y3); + curveVertex(x4, y4); + curveVertex(x4, y4); + endShape(); + // the first and last points are doubled, because the catmull-rom + // algorithm needs the point 'before' and 'after' the segment to + // guide how the curve will begin. it's six calls because without + // doubling the first and last point, curve() produces mostly + // straight lines. not much fun. + + + 'bezier' is a bezier curve + + bezier(x1, y1, x2, y2, x3, y3, x4, y4) + // is equivalent to: + beginShape(); + bezierVertex(x1, y1); + bezierVertex(x2, y2); + bezierVertex(x3, y3); + bezierVertex(x4, y4); + endShape(); + // the first and last points are the on-curve points + // the middle two are the 'control' points, or 'handles' + // in an application like illustrator. + + // 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 bagel by adding these statements: + curveVertex(x4, y4); + curveVertex(x5, y5); + curveVertex(x6, y6); + curveVertex(x7, y7); + // note that x4/y4 are being pulled from the previous + // curveto and used again. + + this may be a bit more verbose, but in general, decisions opted + for maximum flexibility, since these beginShape() commands are + intended as a bit lower-level. rather than having many types of + curveto (curve to corner, and several others described in the + postscript and illustrator specs) let someone else implement a + nice moveto/lineto/curveto library on top. in fact, it's tempting + that we may put one in there ourselves. + + another method for bezier (though not implemented this way) + 1. first start with a call to vertex() + 2. every three calls to bezierVertex produce a new segment + this option seemed no good because of the confusion of mixing + vertex and bezierVertex calls. + */ + + private void c_vertex(float x, float y, float z, boolean bezier) { + // if more than 128 points, shift everything back to the beginning + if (cvertexIndex == CVERTEX_ALLOC) { + System.arraycopy(cvertex[CVERTEX_ALLOC-3], 0, + cvertex[0], 0, VERTEX_FIELD_COUNT); + System.arraycopy(cvertex[CVERTEX_ALLOC-2], 0, + cvertex[1], 0, VERTEX_FIELD_COUNT); + System.arraycopy(cvertex[CVERTEX_ALLOC-1], 0, + cvertex[2], 0, VERTEX_FIELD_COUNT); + cvertexIndex = 3; + } + // add the vertex here + // cvertexIndex and cvertexCount are reset to zero + // when regular vertex() is called, so store it + int savedIndex = cvertexIndex + 1; + if (cverticesFlat && (z != 0)) cverticesFlat = false; + setup_vertex(cvertex[cvertexIndex], x, y, z); + cvertexIndex = savedIndex; // restore cvertexIndex + + // draw a segment if there are enough points + if (cvertexIndex > 3) { + if (bezier) { + if ((cvertexIndex % 4) == 0) { + if (!bezier_inited) bezier_init(); + + if (cverticesFlat) { + spline_segment(cvertex[cvertexIndex-4][MX], + cvertex[cvertexIndex-4][MY], + cvertex[cvertexIndex-3][MX], + cvertex[cvertexIndex-3][MY], + cvertex[cvertexIndex-2][MX], + cvertex[cvertexIndex-2][MY], + cvertex[cvertexIndex-1][MX], + cvertex[cvertexIndex-1][MY], + cvertex[cvertexIndex-4][MX], + cvertex[cvertexIndex-4][MY], + bezier_draw, bezier_segments); + } else { + spline_segment(cvertex[cvertexIndex-4][MX], + cvertex[cvertexIndex-4][MY], + cvertex[cvertexIndex-4][MZ], + cvertex[cvertexIndex-3][MX], + cvertex[cvertexIndex-3][MY], + cvertex[cvertexIndex-3][MZ], + cvertex[cvertexIndex-2][MX], + cvertex[cvertexIndex-2][MY], + cvertex[cvertexIndex-2][MZ], + cvertex[cvertexIndex-1][MX], + cvertex[cvertexIndex-1][MY], + cvertex[cvertexIndex-1][MZ], + cvertex[cvertexIndex-4][MX], + cvertex[cvertexIndex-4][MY], + cvertex[cvertexIndex-4][MZ], + bezier_draw, bezier_segments); + } + } + } else { // !bezier + if (!curve_inited) curve_init(); + + if (cverticesFlat) { + spline_segment(cvertex[cvertexIndex-4][MX], + cvertex[cvertexIndex-4][MY], + cvertex[cvertexIndex-3][MX], + cvertex[cvertexIndex-3][MY], + cvertex[cvertexIndex-2][MX], + cvertex[cvertexIndex-2][MY], + cvertex[cvertexIndex-1][MX], + cvertex[cvertexIndex-1][MY], + cvertex[cvertexIndex-3][MX], + cvertex[cvertexIndex-3][MY], + curve_draw, curve_segments); + } else { + spline_segment(cvertex[cvertexIndex-4][MX], + cvertex[cvertexIndex-4][MY], + cvertex[cvertexIndex-4][MZ], + cvertex[cvertexIndex-3][MX], + cvertex[cvertexIndex-3][MY], + cvertex[cvertexIndex-3][MZ], + cvertex[cvertexIndex-2][MX], + cvertex[cvertexIndex-2][MY], + cvertex[cvertexIndex-2][MZ], + cvertex[cvertexIndex-1][MX], + cvertex[cvertexIndex-1][MY], + cvertex[cvertexIndex-1][MZ], + cvertex[cvertexIndex-3][MX], + cvertex[cvertexIndex-3][MY], + cvertex[cvertexIndex-3][MZ], + curve_draw, curve_segments); + } + } + } + // spline_segment() calls vertex(), which clears cvertexIndex + cvertexIndex = savedIndex; + } + + + public void bezierVertex(float x, float y) { + c_vertex(x, y, 0, true); + } + + public void bezierVertex(float x, float y, float z) { + c_vertex(x, y, z, true); + } + + public void curveVertex(float x, float y) { + c_vertex(x, y, 0, false); + } + + public void curveVertex(float x, float y, float z) { + c_vertex(x, y, z, false); + } + + + public void endShape_newgraphics() { + // clear the 'shape drawing' flag in case of early exit + shape = false; + + vertex_end = vertex_count; + + // ------------------------------------------------------------------ + // CREATE LINES + + int increment = 1; + int stop = 0; + int counter = 0; + + // make lines for both stroke triangles + // and antialiased triangles + boolean check = _stroke || smooth; + + // quick fix [rocha] + // antialiasing fonts with lines causes some artifacts + if (textureImage != null && textureImage.format == ALPHA) { + check = false; + } + + if (check) { + switch (shapeKind) { + + case POINTS: + { + stop = vertex_end; + for (int i = vertex_start; i < stop; i++) { + addLine(i,i); + } + } + break; + + case LINES: + case LINE_STRIP: + case LINE_LOOP: + { + // store index of first vertex + int first = lines_count; + stop = vertex_end-1; + increment = (shapeKind == LINES) ? 2 : 1; + + for (int i = vertex_start; i < stop; i+=increment) { + addLine(i,i+1); + } + + if (shapeKind == LINE_LOOP) { + addLine(stop,lines[first][PA]); + } + } + break; + + case TRIANGLES: + case TRIANGLE_STRIP: + { + // first draw all vertices as a line strip + stop = vertex_end-1; + + for (int i = vertex_start; i < stop; i++) { + counter = i - vertex_start; + addLine(i,i+1); + if ((shapeKind == TRIANGLES) && (counter%3 == 1)) { + i++; + } + } + + // then draw from vertex (n) to (n+2) + stop = vertex_end-2; + increment = (shapeKind == TRIANGLE_STRIP) ? 1 : 3; + + for (int i = vertex_start; i < stop; i+=increment) { + addLine(i,i+2); + } + } + break; + + case QUADS: + case QUAD_STRIP: + { + // first draw all vertices as a line strip + stop = vertex_end - 1; + + for (int i = vertex_start; i < stop; i++) { + counter = i - vertex_start; + addLine(i,i+1); + if ((shapeKind == QUADS) && (counter%4 == 2)) { + i++; + } + } + + // then draw from vertex (n) to (n+3) + stop = vertex_end-2; + increment = (shapeKind == QUAD_STRIP) ? 2 : 4; + + for (int i=vertex_start; i < stop; i+=increment) { + addLine(i,i+3); + } + } + break; + + case POLYGON: + case CONCAVE_POLYGON: + case CONVEX_POLYGON: + { + // store index of first vertex + int first = lines_count; + stop = vertex_end - 1; + + for (int i=vertex_start; i < stop; i++) { + addLine(i,i+1); + } + // draw the last line connecting back to the first point in poly + addLine(stop,lines[first][PA]); + } + break; + } + } + + // ------------------------------------------------------------------ + // CREATE TRIANGLES + + if (_fill) { + switch (shapeKind) { + case TRIANGLES: + case TRIANGLE_STRIP: + { + stop = vertex_end - 2; + increment = (shapeKind == TRIANGLES) ? 3 : 1; + for (int i = vertex_start; i < stop; i += increment) { + addTriangle(i, i+1, i+2); + } + } + break; + + case QUADS: + case QUAD_STRIP: + { + stop = vertex_count-3; + increment = (shapeKind == QUADS) ? 4 : 2; + + for (int i = vertex_start; i < stop; i += increment) { + // first triangle + addTriangle(i, i+1, i+2); + // second triangle + addTriangle(i, i+2, i+3); + } + } + break; + + case POLYGON: + case CONCAVE_POLYGON: + case CONVEX_POLYGON: + { + triangulate_polygon(); + } + break; + } + } + + // ------------------------------------------------------------------ + // POINTS FROM MODEL (MX, MY, MZ) TO VIEW SPACE (VX, VY, VZ) + + //if ((camera_mode == PERSPECTIVE) && (dimensions == 0)) { + if ((camera_mode != CUSTOM) && (dimensions == 0)) { + // flat 2D + for (int i = vertex_start; i < vertex_end; i++) { + vertices[i][X] = vertices[i][MX]; + vertices[i][Y] = vertices[i][MY]; + } + + } else if ((camera_mode != CUSTOM) && (dimensions == 2)) { + + // affine transform, ie rotated 2D + for (int i = vertex_start; i < vertex_end; i++) { + vertices[i][X] = m00*vertices[i][MX] + m01*vertices[i][MY] + m03; + vertices[i][Y] = m10*vertices[i][MX] + m11*vertices[i][MY] + m13; + } + + } else { + // dimension = 3 or camera mode is custom + + for (int i = vertex_start; i < vertex_end; i++) { + float vertex[] = vertices[i]; + + vertex[VX] = m00*vertex[MX] + m01*vertex[MY] + m02*vertex[MZ] + m03; + vertex[VY] = m10*vertex[MX] + m11*vertex[MY] + m12*vertex[MZ] + m13; + vertex[VZ] = m20*vertex[MX] + m21*vertex[MY] + m22*vertex[MZ] + m23; + vertex[VW] = m30*vertex[MX] + m31*vertex[MY] + m32*vertex[MZ] + m33; + } + } + + + // ------------------------------------------------------------------ + // CULLING + + // simple culling + // if they share the same clipping code, then cull + /* + boolean clipped = true; + float x = vertices[vertex_start][X]; + float y = vertices[vertex_start][Y]; + int clipCode = ((y < 0 ? 8 : 0) | (y > height1 ? 4 : 0) | + (x < 0 ? 2 : 0) | (x > width1 ? 1 : 0)); + for (int i = vertex_start + 1; i < vertex_end; i++) { + x = vertices[i][X]; + y = vertices[i][Y]; + int code = ((y < 0 ? 8 : 0) | (y > height1 ? 4 : 0) | + (x < 0 ? 2 : 0) | (x > width1 ? 1 : 0)); + if (code != clipCode) { + clipped = false; + break; + } + } + if ((clipCode != 0) && clipped) return; + */ + + // ------------------------------------------------------------------ + // NORMALS + + if (!normalChanged) { + // fill first vertext w/ the normal + vertices[vertex_start][NX] = normalX; + vertices[vertex_start][NY] = normalY; + vertices[vertex_start][NZ] = normalZ; + // homogenousNormals saves time from below, which is expensive + } + + for (int i = vertex_start; i < (normalChanged ? vertex_end : 1); i++) { + float v[] = vertices[i]; + float nx = m00*v[NX] + m01*v[NY] + m02*v[NZ] + m03; + float ny = m10*v[NX] + m11*v[NY] + m12*v[NZ] + m13; + float nz = m20*v[NX] + m21*v[NY] + m22*v[NZ] + m23; + float nw = m30*v[NX] + m31*v[NY] + m32*v[NZ] + m33; + + if (nw != 0) { + // divide by perspective coordinate + v[NX] = nx/nw; v[NY] = ny/nw; v[NZ] = nz/nw; + } else { + // can't do inline above + v[NX] = nx; v[NY] = ny; v[NZ] = nz; + } + + float nlen = mag(v[NX], v[NY], v[NZ]); // normalize + if (nlen != 0) { + v[NX] /= nlen; v[NY] /= nlen; v[NZ] /= nlen; + } + } + + + // ------------------------------------------------------------------ + // COLORS + + // calculate RGB for each vertex + //if (homogenousColors && !lighting) { // if no lighting, do only once + + if (!lighting) { + + // all the values for r, g, b have been set with calls to vertex() + // (no need to re-calculate anything here) + + } else { + + float f[] = vertices[vertex_start]; + + for (int i = vertex_start; i < vertex_end; i++) { + float v[] = vertices[i]; + if (normalChanged) { + if (_fill) { + calc_lighting(v[R], v[G], v[B], v[MX], v[MY], v[MZ], + v[NX], v[NY], v[NZ], v, R); + } + + if (_stroke) { + calc_lighting(v[SR], v[SG], v[SB], v[MX], v[MY], v[MZ], + v[NX], v[NY], v[NZ], v, SR); + } + } else { + if (_fill) { + calc_lighting(v[R], v[G], v[B], v[MX], v[MY], v[MZ], + f[NX], f[NY], f[NZ], v, R); + } + if (_stroke) { + calc_lighting(v[SR], v[SG], v[SB], v[MX], v[MY], v[MZ], + f[NX], f[NY], f[NZ], v, SR); + } + } + } + } + + // ------------------------------------------------------------------ + // NEAR PLANE CLIPPING AND CULLING + + //if ((camera_mode == PERSPECTIVE) && (dimensions == 3) && clip) { + //float z_plane = eyeDist + ONE; + + //for (int i = 0; i < lines_count; i ++) { + //line3dClip(); + //} + + //for (int i = 0; i < triangles_count; i ++) { + //} + //} + + // ------------------------------------------------------------------ + // POINTS FROM VIEW SPACE (MX, MY, MZ) TO SCREEN SPACE (X, Y, Z) + + if ((camera_mode == PERSPECTIVE) && (dimensions == 3)) { + + for (int i = vertex_start; i < vertex_end; i++) { + float vertex[] = vertices[i]; + + float ox = p00*vertex[VX] + p01*vertex[VY] + p02*vertex[VZ] + p03*vertex[VW]; + float oy = p10*vertex[VX] + p11*vertex[VY] + p12*vertex[VZ] + p13*vertex[VW]; + float oz = p20*vertex[VX] + p21*vertex[VY] + p22*vertex[VZ] + p23*vertex[VW]; + float ow = p30*vertex[VX] + p31*vertex[VY] + p32*vertex[VZ] + p33*vertex[VW]; + + if (ow != 0) { + ox /= ow; oy /= ow; oz /= ow; + } + + vertex[X] = width * (ONE + ox) / 2.0f; + vertex[Y] = height * (ONE + oy) / 2.0f; + vertex[Z] = (oz + ONE) / 2.0f; + } + } + + // ------------------------------------------------------------------ + // RENDER SHAPES FILLS HERE WHEN NOT Z_ORDERING + + if (z_order == true) { + return; + } + + // render all triangles in current shape + if (_fill) { + + for (int i = 0; i < triangles_count; i ++) { + float a[] = vertices[triangles[i][VA]]; + float b[] = vertices[triangles[i][VB]]; + float c[] = vertices[triangles[i][VC]]; + int index = triangles[i][TI]; + + if (textureImage != null) { + triangle.setUV(a[U], a[V], b[U], b[V], c[U], c[V]); + } + + triangle.setIntensities(a[R], a[G], a[B], a[A], + b[R], b[G], b[B], b[A], + c[R], c[G], c[B], 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(); + } + } + + // ------------------------------------------------------------------ + // DRAW POINTS, LINES AND SHAPE STROKES + + // draw all lines in current shape + if (_stroke || smooth) { + + for (int i = 0; i < lines_count; i ++) { + float a[] = vertices[lines[i][PA]]; + float b[] = vertices[lines[i][PB]]; + int index = lines[i][LI]; + + 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]); + + line.setIndex(index); + + line.draw(); + } + } + + shapeKind = 0; + } + + + + ////////////////////////////////////////////////////////////// + + // GEOMETRY STUFF + + + // triangulate the current polygon + private void triangulate_polygon() { + + // simple ear clipping polygon triangulation + // addapted from code by john w. ratcliff (jratcliff@verant.com) + + // 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][X] * vertices[p][Y] - vertices[p][X] * vertices[q][Y]; + } + + // then we sort the vertices so they are always in a counterclockwise order + int j = 0; + if ( 0.0f < area ){ + // def < + 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; + + Ax = -vertices[vertex_order[u]][X]; + Ay = vertices[vertex_order[u]][Y]; + Bx = -vertices[vertex_order[v]][X]; + By = vertices[vertex_order[v]][Y]; + Cx = -vertices[vertex_order[w]][X]; + Cy = vertices[vertex_order[w]][Y]; + + // 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; + } + + Px = -vertices[vertex_order[p]][X]; + Py = vertices[vertex_order[p]][Y]; + + ax = Cx - Bx; ay = Cy - By; + bx = Ax - Cx; by = Ay - Cy; + cx = Bx - Ax; cy = By - Ay; + apx= Px - Ax; apy= Py - Ay; + bpx= Px - Bx; bpy= Py - By; + cpx= Px - Cx; cpy= Py - Cy; + + aCROSSbp = ax * bpy - ay * bpx; + cCROSSap = cx * apy - cy * apx; + bCROSScp = bx * cpy - by * cpx; + + if ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)) { + snip = false; + } + } + + if (snip) { + int a,b,c,s,t; + + // true names of the vertices + a = vertex_order[u]; b = vertex_order[v]; c = vertex_order[w]; + + // create triangle + addTriangle(a, b, c); + + m++; + + // remove v from remaining polygon + for( s = v, t = v + 1; t < vc; s++, t++) { + vertex_order[s] = vertex_order[t]; + } + + vc--; + + // resest error detection counter + count = 2 * vc; + } + } + } + + + //private void sortTriangles() { + //} + + + ////////////////////////////////////////////////////////////// + + + public void endShape() { + if (hints[NEW_GRAPHICS]) { + endShape_newgraphics(); + return; + } + // could initialize unchangedZ if false, + // model matrix is not identity + // same with homoegenousColors and !lighting + + // clear the 'shape drawing' flag in case of early exit + shape = false; + + int vertexCount = polygon.vertexCount; + float vertices[][] = polygon.vertices; + + // ------------------------------------------------------------------ + // POINTS FROM MODEL (MX, MY, MZ) TO SCREEN SPACE (X, Y, Z) + + if ((camera_mode == PERSPECTIVE) && (dimensions == 0)) { + polygon.interpZ = false; + spolygon.interpZ = false; + for (int i = 0; i < vertexCount; i++) { + vertices[i][X] = vertices[i][MX]; + vertices[i][Y] = vertices[i][MY]; + } + + } else if ((camera_mode == PERSPECTIVE) && (dimensions == 2)) { + polygon.interpZ = false; + spolygon.interpZ = false; + for (int i = 0; i < vertexCount; i++) { + vertices[i][X] = m00*vertices[i][MX] + m01*vertices[i][MY] + m03; + vertices[i][Y] = m10*vertices[i][MX] + m11*vertices[i][MY] + m13; + } + + /* + } else if (camera_mode == ISOMETRIC) { + for (int i = 0; i < vertexCount; i++) { + float v[] = vertices[i]; + v[X] = v[MX] - v[MZ]; + v[Y] = -v[MX]/2f + v[MY] - v[MZ]/2f; + v[Z] = v[MZ]; + } + */ + + } else { // dimension = 3 or camera mode is custom + polygon.interpZ = true; + spolygon.interpZ = true; + + for (int i = 0; i < vertexCount; i++) { + float vertex[] = vertices[i]; + + float ax = m00*vertex[MX] + m01*vertex[MY] + m02*vertex[MZ] + m03; + float ay = m10*vertex[MX] + m11*vertex[MY] + m12*vertex[MZ] + m13; + float az = m20*vertex[MX] + m21*vertex[MY] + m22*vertex[MZ] + m23; + float aw = m30*vertex[MX] + m31*vertex[MY] + m32*vertex[MZ] + m33; + + float ox = p00*ax + p01*ay + p02*az + p03*aw; + float oy = p10*ax + p11*ay + p12*az + p13*aw; + float oz = p20*ax + p21*ay + p22*az + p23*aw; + float ow = p30*ax + p31*ay + p32*az + p33*aw; + + if (ow != 0) { + ox /= ow; oy /= ow; oz /= ow; + } + + vertex[X] = width * (ONE + ox) / 2.0f; + vertex[Y] = height * (ONE + oy) / 2.0f; + vertex[Z] = (oz + ONE) / 2.0f; + } + } + + + // simple clipping.. if they share the same clipping code, then cull + boolean clipped = true; + int clipCode = thin_flat_lineClipCode(vertices[0][X], vertices[0][Y]); + for (int i = 1; i < vertexCount; i++) { + int code = thin_flat_lineClipCode(vertices[i][X], vertices[i][Y]); + if (code != clipCode) { + clipped = false; + break; + } + } + if ((clipCode != 0) && clipped) return; + + + // ------------------------------------------------------------------ + // NORMALS + + if (!normalChanged) { // fill first vertext w/ the normal + vertices[0][NX] = normalX; + vertices[0][NY] = normalY; + vertices[0][NZ] = normalZ; + // homogenousNormals saves time from below, which is expensive + } + + for (int i = 0; i < (normalChanged ? vertexCount : 1); i++) { + float v[] = vertices[i]; + float nx = m00*v[NX] + m01*v[NY] + m02*v[NZ] + m03; + float ny = m10*v[NX] + m11*v[NY] + m12*v[NZ] + m13; + float nz = m20*v[NX] + m21*v[NY] + m22*v[NZ] + m23; + float nw = m30*v[NX] + m31*v[NY] + m32*v[NZ] + m33; + + if (nw != 0) { // divide by perspective coordinate + v[NX] = nx/nw; v[NY] = ny/nw; v[NZ] = nz/nw; + } else { // can't do inline above + v[NX] = nx; v[NY] = ny; v[NZ] = nz; + } + float nlen = mag(v[NX], v[NY], v[NZ]); // normalize + if (nlen != 0) { + v[NX] /= nlen; v[NY] /= nlen; v[NZ] /= nlen; + } + } + + // ------------------------------------------------------------------ + // TEXTURES + + // inherit UV characteristics from polygon + // this is an uglyish sort of hack + if (polygon.interpUV) { + fpolygon.texture(polygon.timage); + } + + // ------------------------------------------------------------------ + // COLORS + + // calculate RGB for each vertex + //if (homogenousColors && !lighting) { // if no lighting, do only once + + if (!lighting) { + //polygon.interpRGB = //false; + spolygon.interpRGBA = strokeChanged; //false; + fpolygon.interpRGBA = fillChanged; //false; + + // all the values for r, g, b have been set with calls to vertex() + // (no need to re-calculate anything here) + + } else { + //polygon.interpRGB = true; + spolygon.interpRGBA = true; + fpolygon.interpRGBA = true; + + float f[] = polygon.vertices[0]; + + for (int i = 0; i < vertexCount; i++) { + float v[] = polygon.vertices[i]; + if (normalChanged) { + if (_fill) { + calc_lighting(v[R], v[G], v[B], + v[MX], v[MY], v[MZ], + v[NX], v[NY], v[NZ], v, R); + } + if (_stroke) { + calc_lighting(v[SR], v[SG], v[SB], + v[MX], v[MY], v[MZ], + v[NX], v[NY], v[NZ], v, SR); + } + } else { + if (_fill) { + calc_lighting(v[R], v[G], v[B], + v[MX], v[MY], v[MZ], + f[NX], f[NY], f[NZ], v, R); + } + if (_stroke) { + calc_lighting(v[SR], v[SG], v[SB], + v[MX], v[MY], v[MZ], + f[NX], f[NY], f[NZ], v, SR); + } + } + } + } + + // ------------------------------------------------------------------ + // RENDER SHAPES + + int increment; + + // test for concave-convex + if (shapeKind == POLYGON) { + shapeKind = isConvex() ? CONVEX_POLYGON : CONCAVE_POLYGON; + } + + switch (shapeKind) { + case POINTS: + if ((dimensions == 0) && unchangedZ && + (strokeWeight == ONE) && !lighting) { + if (!strokeChanged) { + for (int i = 0; i < vertexCount; i++) { + thin_point((int) vertices[i][X], (int) vertices[i][Y], + 0, stroke); + } + } else { + for (int i = 0; i < vertexCount; i++) { + thin_point((int) vertices[i][X], (int) vertices[i][Y], + 0, float_color(vertices[i][SR], + vertices[i][SG], + vertices[i][SB])); + } + //strokei = strokeiSaved; + } + } else { + float f[] = vertices[0]; + + for (int i = 0; i < vertexCount; i++) { + float v[] = vertices[i]; + + // if this is the first time (i == 0) + // or if lighting is enabled + // or the stroke color has changed inside beginShape/endShape + // then re-calculate the color at this vertex + if ((i == 0) || lighting || strokeChanged) { + // push calculated color into 'f' (this way, f is always valid) + calc_lighting(v[SR], v[SG], v[SB], + v[X], v[Y], v[Z], + v[NX], v[NY], v[NZ], f, R); + } + // uses [SA], since stroke alpha isn't moved into [A] the + // way that [SR] goes to [R] etc on the calc_lighting call + // (there's no sense in copying it to [A], except consistency + // in the code.. but why the extra slowness?) + thick_point(v[X], v[Y], v[Z], f[R], f[G], f[B], f[SA]); + } + } + break; + + case LINES: + case LINE_STRIP: + case LINE_LOOP: + if (!_stroke) return; + + // if it's a line loop, copy the vertex data to the last element + if (shapeKind == LINE_LOOP) { + float v0[] = polygon.vertices[0]; + float v1[] = polygon.nextVertex(); + vertexCount++; // since it had already been read above + + v1[X] = v0[X]; v1[Y] = v0[Y]; v1[Z] = v0[Z]; + v1[SR] = v0[SR]; v1[SG] = v0[SG]; v1[SB] = v0[SB]; + } + + // increment by two for individual lines + increment = (shapeKind == LINES) ? 2 : 1; + draw_lines(vertices, vertexCount-1, 1, increment, 0); + break; + + case TRIANGLES: + case TRIANGLE_STRIP: + increment = (shapeKind == TRIANGLES) ? 3 : 1; + // do fill and stroke separately because otherwise + // the lines will be stroked more than necessary + if (_fill) { + fpolygon.vertexCount = 3; + for (int i = 0; i < vertexCount-2; i += increment) { + for (int j = 0; j < 3; j++) { + fpolygon.vertices[j][R] = vertices[i+j][R]; + fpolygon.vertices[j][G] = vertices[i+j][G]; + fpolygon.vertices[j][B] = vertices[i+j][B]; + fpolygon.vertices[j][A] = vertices[i+j][A]; + + fpolygon.vertices[j][X] = vertices[i+j][X]; + fpolygon.vertices[j][Y] = vertices[i+j][Y]; + fpolygon.vertices[j][Z] = vertices[i+j][Z]; + + if (polygon.interpUV) { + fpolygon.vertices[j][U] = vertices[i+j][U]; + fpolygon.vertices[j][V] = vertices[i+j][V]; + } + } + fpolygon.render(); + } + } + if (_stroke) { + // first draw all vertices as a line strip + if (shapeKind == TRIANGLE_STRIP) { + draw_lines(vertices, vertexCount-1, 1, 1, 0); + } else { + draw_lines(vertices, vertexCount-1, 1, 1, 3); + } + // then draw from vertex (n) to (n+2) + // incrementing n using the same as above + draw_lines(vertices, vertexCount-2, 2, increment, 0); + // changed this to vertexCount-2, because it seemed + // to be adding an extra (nonexistant) line + } + break; + + case QUADS: + case QUAD_STRIP: + //System.out.println("pooping out a quad"); + increment = (shapeKind == QUADS) ? 4 : 2; + if (_fill) { + fpolygon.vertexCount = 4; + for (int i = 0; i < vertexCount-3; i += increment) { + for (int j = 0; j < 4; j++) { + fpolygon.vertices[j][R] = vertices[i+j][R]; + fpolygon.vertices[j][G] = vertices[i+j][G]; + fpolygon.vertices[j][B] = vertices[i+j][B]; + fpolygon.vertices[j][A] = vertices[i+j][A]; + + fpolygon.vertices[j][X] = vertices[i+j][X]; + fpolygon.vertices[j][Y] = vertices[i+j][Y]; + fpolygon.vertices[j][Z] = vertices[i+j][Z]; + + if (polygon.interpUV) { + fpolygon.vertices[j][U] = vertices[i+j][U]; + fpolygon.vertices[j][V] = vertices[i+j][V]; + } + } + fpolygon.render(); + } + } + if (_stroke) { + // first draw all vertices as a line strip + if (shapeKind == QUAD_STRIP) { + draw_lines(vertices, vertexCount-1, 1, 1, 0); + } else { // skip every few for quads + draw_lines(vertices, vertexCount, 1, 1, 4); + } + // then draw from vertex (n) to (n+3) + // incrementing n by the same increment as above + draw_lines(vertices, vertexCount-2, 3, increment, 0); + } + break; + + case POLYGON: + case CONCAVE_POLYGON: + if (_fill) { + // the triangulator produces polygons that don't align + // when smoothing is enabled. but if there is a stroke around + // the polygon, then smoothing can be temporarily disabled. + boolean smoov = smooth; + if (_stroke && !hints[DISABLE_SMOOTH_HACK]) smooth = false; + concaveRender(); + if (_stroke && !hints[DISABLE_SMOOTH_HACK]) smooth = smoov; + } + + if (_stroke) { + draw_lines(vertices, vertexCount-1, 1, 1, 0); + // draw the last line connecting back + // to the first point in poly + svertices[0] = vertices[vertexCount-1]; + svertices[1] = vertices[0]; + draw_lines(svertices, 1, 1, 1, 0); + } + break; + + case CONVEX_POLYGON: + if (_fill) { + polygon.render(); + if (_stroke) polygon.unexpand(); + } + + if (_stroke) { + draw_lines(vertices, vertexCount-1, 1, 1, 0); + // draw the last line connecting back to the first point in poly + svertices[0] = vertices[vertexCount-1]; + svertices[1] = vertices[0]; + draw_lines(svertices, 1, 1, 1, 0); + } + break; + } + // to signify no shape being drawn + //shapeKind = 0; + } + + + ////////////////////////////////////////////////////////////// + + // CONCAVE/CONVEX POLYGONS + + // pile of shit hack from rocha that cost us piles of $$ + + + private boolean isConvex() { + float v[][] = polygon.vertices; + int n = polygon.vertexCount; + int j,k; + int flag = 0; + float z; + //float tol = 0.001f; + + if (n < 3) + // ERROR: this is a line or a point, render with CONVEX + return true; + + // iterate along border doing dot product. + // if the sign of the result changes, then is concave + for (int i=0;i 0) + flag |= 2; + if (flag == 3) + return false; // CONCAVE + } + if (flag != 0) + return true; // CONVEX + else + // ERROR: colinear points, self intersection + // treat as CONVEX + return true; + } + + + // triangulate the current polygon + private void concaveRender() { + // WARNING: code is not in optimum form + // local initiations of some variables are made to + // keep the code modular and easy to integrate + // restet triangle + float vertices[][] = polygon.vertices; + + if (tpolygon == null) { + // allocate on first use, rather than slowing + // the startup of the class. + tpolygon = new BPolygon(this); + tpolygon_vertex_order = new int[TPOLYGON_MAX_VERTICES]; + } + tpolygon.reset(3); + + // copy render parameters + + if (texture) { + tpolygon.texture(polygon.timage); + } + + tpolygon.interpX = polygon.interpX; + tpolygon.interpZ = polygon.interpZ; + tpolygon.interpUV = polygon.interpUV; + tpolygon.interpRGBA = polygon.interpRGBA; + + // simple ear clipping polygon triangulation + // addapted from code by john w. ratcliff (jratcliff@verant.com) + + // 1 - first we check if the polygon goes CW or CCW + // CW-CCW ordering adapted from code by + // Joseph O'Rourke orourke@cs.smith.edu + // 1A - we start by finding the lowest-right most vertex + + boolean ccw = false; // clockwise + + int n = polygon.vertexCount; + int mm; // postion for LR vertex + float min[] = new float[2]; + + min[X] = vertices[0][X]; + min[Y] = vertices[0][Y]; + mm = 0; + + for(int i = 0; i < n; i++ ) { + if( (vertices[i][Y] < min[Y]) || + ( (vertices[i][Y] == min[Y]) && (vertices[i][X] > min[X]) ) + ) { + mm = i; + min[X] = vertices[mm][X]; + min[Y] = vertices[mm][Y]; + } + } + + // 1B - now we compute the cross product of the edges of this vertex + float cp; + int mm1; + + // just for renaming + float a[] = new float[2]; + float b[] = new float[2]; + float c[] = new float[2]; + + mm1 = (mm + (n-1)) % n; + + // assign a[0] to point to poly[m1][0] etc. + for(int i = 0; i < 2; i++ ) { + a[i] = vertices[mm1][i]; + b[i] = vertices[mm][i]; + c[i] = vertices[(mm+1)%n][i]; + } + + cp = a[0] * b[1] - a[1] * b[0] + + a[1] * c[0] - a[0] * c[1] + + b[0] * c[1] - c[0] * b[1]; + + if ( cp > 0 ) + ccw = true; // CCW + else + ccw = false; // CW + + // 1C - then we sort the vertices so they + // are always in a counterclockwise order + int j = 0; + if (!ccw) { + // keep the same order + for (int i = 0; i < n; i++) { + tpolygon_vertex_order[i] = i; + } + + } else { + // invert the order + for (int i = 0; i < n; i++) { + tpolygon_vertex_order[i] = (n - 1) - i; + } + } + + // 2 - begin triangulation + // resulting triangles are stored in the triangle array + // remove vc-2 Vertices, creating 1 triangle every time + int vc = n; + 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; + + Ax = -vertices[tpolygon_vertex_order[u]][X]; + Ay = vertices[tpolygon_vertex_order[u]][Y]; + Bx = -vertices[tpolygon_vertex_order[v]][X]; + By = vertices[tpolygon_vertex_order[v]][Y]; + Cx = -vertices[tpolygon_vertex_order[w]][X]; + Cy = vertices[tpolygon_vertex_order[w]][Y]; + + if ( EPSILON > (((Bx-Ax) * (Cy-Ay)) - ((By-Ay) * (Cx-Ax)))) { + continue; + } + + for (int p = 0; p < vc; p++) { + + // this part is a bit osbscure, basically what it does + // is test if this tree vertices are and ear or not, looking for + // intersections with the remaining vertices using a cross product + 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; + } + + Px = -vertices[tpolygon_vertex_order[p]][X]; + Py = vertices[tpolygon_vertex_order[p]][Y]; + + ax = Cx - Bx; ay = Cy - By; + bx = Ax - Cx; by = Ay - Cy; + cx = Bx - Ax; cy = By - Ay; + apx= Px - Ax; apy= Py - Ay; + bpx= Px - Bx; bpy= Py - By; + cpx= Px - Cx; cpy= Py - Cy; + + aCROSSbp = ax * bpy - ay * bpx; + cCROSSap = cx * apy - cy * apx; + bCROSScp = bx * cpy - by * cpx; + + if ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)) { + snip = false; + } + } + + if (snip) { + // yes, the trio is an ear, render it and cut it + + int triangle_vertices[] = new int[3]; + int s,t; + + // true names of the vertices + triangle_vertices[0] = tpolygon_vertex_order[u]; + triangle_vertices[1] = tpolygon_vertex_order[v]; + triangle_vertices[2] = tpolygon_vertex_order[w]; + + // create triangle + render_triangle(triangle_vertices); + + m++; + + // remove v from remaining polygon + for( s = v, t = v + 1; t < vc; s++, t++) { + tpolygon_vertex_order[s] = tpolygon_vertex_order[t]; + } + + vc--; + + // resest error detection counter + count = 2 * vc; + } + } + } + + + private final void render_triangle(int[] triangle_vertices) { + // copy all fields of the triangle vertices + for (int i = 0; i < 3; i++) { + float[] src = polygon.vertices[triangle_vertices[i]]; + float[] dest = tpolygon.vertices[i]; + for (int j = 0; j < VERTEX_FIELD_COUNT; j++) { + dest[j] = src[j]; + } + } + + // render triangle + tpolygon.render(); + } + + + ////////////////////////////////////////////////////////////// + + // RENDERING + + + // expects properly clipped coords, hence does + // NOT check if x/y are in bounds [toxi] + private void thin_pointAt(int x, int y, float z, int color) { + int index = y*width+x; // offset values are pre-calced in constructor + pixels[index] = color; + zbuffer[index] = z; + } + + // expects offset/index in pixelbuffer array instead of x/y coords + // used by optimized parts of thin_flat_line() [toxi] + private void thin_pointAtIndex(int offset, float z, int color) { + pixels[offset] = color; + zbuffer[offset] = z; + } + + // points are inherently flat, but always tangent + // to the screen surface. the z is only so that things + // get scaled properly if the pt is way in back + private void thick_point(float x, float y, float z, // note floats + float r, float g, float b, float a) { + spolygon.reset(4); + spolygon.interpRGBA = false; // no changes for vertices of a point + + float strokeWidth2 = strokeWeight/2.0f; + + float svertex[] = spolygon.vertices[0]; + svertex[X] = x - strokeWidth2; + svertex[Y] = y - strokeWidth2; + svertex[Z] = z; + + svertex[R] = r; + svertex[G] = g; + svertex[B] = b; + svertex[A] = a; + + svertex = spolygon.vertices[1]; + svertex[X] = x + strokeWidth2; + svertex[Y] = y - strokeWidth2; + svertex[Z] = z; + + svertex = spolygon.vertices[2]; + svertex[X] = x + strokeWidth2; + svertex[Y] = y + strokeWidth2; + svertex[Z] = z; + + svertex = spolygon.vertices[3]; + svertex[X] = x - strokeWidth2; + svertex[Y] = y + strokeWidth2; + svertex[Z] = z; + + spolygon.render(); + } + + + // new bresenham clipping code, as old one was buggy [toxi] + private void thin_flat_line(int x1, int y1, int x2, int y2) { + int nx1,ny1,nx2,ny2; + + // get the "dips" for the points to clip + int code1 = thin_flat_lineClipCode(x1, y1); + int code2 = thin_flat_lineClipCode(x2, y2); + + if ((code1 & code2)!=0) { + return; + } else { + int dip = code1 | code2; + if (dip != 0) { + // now calculate the clipped points + float a1 = 0, a2 = 1, a = 0; + for (int i=0;i<4;i++) { + if (((dip>>i)%2)==1) { + a = thin_flat_lineSlope((float)x1, (float)y1, + (float)x2, (float)y2, i+1); + if (((code1>>i)%2)==1) { + a1 = (float)Math.max(a, a1); + } else { + a2 = (float)Math.min(a, a2); + } + } + } + if (a1>a2) return; + else { + nx1=(int) (x1+a1*(x2-x1)); + ny1=(int) (y1+a1*(y2-y1)); + nx2=(int) (x1+a2*(x2-x1)); + ny2=(int) (y1+a2*(y2-y1)); + } + // line is fully visible/unclipped + } else { + nx1=x1; nx2=x2; + ny1=y1; ny2=y2; + } + } + + // new "extremely fast" line code + // adapted from http://www.edepot.com/linee.html + + boolean yLonger=false; + int shortLen=ny2-ny1; + int longLen=nx2-nx1; + if (Math.abs(shortLen)>Math.abs(longLen)) { + int swap=shortLen; + shortLen=longLen; + longLen=swap; + yLonger=true; + } + int decInc; + if (longLen==0) decInc=0; + else decInc = (shortLen << 16) / longLen; + + if (nx1==nx2) { + // special case: vertical line + if (ny1>ny2) { int ty=ny1; ny1=ny2; ny2=ty; } + int offset=ny1*width+nx1; + for(int j=ny1; j<=ny2; j++) { + thin_pointAtIndex(offset,0,stroke); + offset+=width; + } + return; + } else if (ny1==ny2) { + // special case: horizontal line + if (nx1>nx2) { int tx=nx1; nx1=nx2; nx2=tx; } + int offset=ny1*width+nx1; + for(int j=nx1; j<=nx2; j++) thin_pointAtIndex(offset++,0,stroke); + return; + } else if (yLonger) { + if (longLen>0) { + longLen+=ny1; + for (int j=0x8000+(nx1<<16);ny1<=longLen;++ny1) { + thin_pointAt(j>>16, ny1, 0, stroke); + j+=decInc; + } + return; + } + longLen+=ny1; + for (int j=0x8000+(nx1<<16);ny1>=longLen;--ny1) { + thin_pointAt(j>>16, ny1, 0, stroke); + j-=decInc; + } + return; + } else if (longLen>0) { + longLen+=nx1; + for (int j=0x8000+(ny1<<16);nx1<=longLen;++nx1) { + thin_pointAt(nx1, j>>16, 0, stroke); + j+=decInc; + } + return; + } + longLen+=nx1; + for (int j=0x8000+(ny1<<16);nx1>=longLen;--nx1) { + thin_pointAt(nx1, j>>16, 0, stroke); + j-=decInc; + } + } + + private int thin_flat_lineClipCode(float x, float y) { + return ((y < 0 ? 8 : 0) | (y > height1 ? 4 : 0) | + (x < 0 ? 2 : 0) | (x > width1 ? 1 : 0)); + } + + private float thin_flat_lineSlope(float x1, float y1, + float x2, float y2, int border) { + switch (border) { + case 4: { + return (-y1)/(y2-y1); + } + case 3: { + return (height1-y1)/(y2-y1); + } + case 2: { + return (-x1)/(x2-x1); + } + case 1: { + return (width1-x1)/(x2-x1); + } + } + return -1f; + } + + + private boolean flat_line_retribution(float x1, float y1, + float x2, float y2, + float r1, float g1, float b1) { + // assume that if it is/isn't big in one dir, then the + // other doesn't matter, cuz that's a weird case + float lwidth = m00*strokeWeight + m01*strokeWeight; + //float lheight = m10*strokeWeight + m11*strokeWeight; + // lines of stroke thickness 1 can be anywhere from -1.41 to 1.41 + if ((strokeWeight < TWO) && (!hints[SCALE_STROKE_WIDTH])) { + //if (abs(lwidth) < 1.5f) { + //System.out.println("flat line retribution " + r1 + " " + g1 + " " + b1); + int strokeSaved = stroke; + stroke = float_color(r1, g1, b1); + thin_flat_line((int)x1, (int)y1, (int)x2, (int)y2); + stroke = strokeSaved; + return true; + } + return false; + } + + + private void thick_flat_line(float ox1, float oy1, + float r1, float g1, float b1, float a1, + float ox2, float oy2, + float r2, float g2, float b2, float a2) { + spolygon.interpRGBA = (r1 != r2) || (g1 != g2) || (b1 != b2) || (a1 != a2); + spolygon.interpZ = false; + + if (!spolygon.interpRGBA && + flat_line_retribution(ox1, oy1, ox2, oy2, r1, g1, b1)) { + return; + } + + float dX = ox2-ox1 + EPSILON; + float dY = oy2-oy1 + EPSILON; + float len = sqrt(dX*dX + dY*dY); + + // TODO strokeWidth should be transformed! + float rh = strokeWeight / len; + + float dx0 = rh * dY; + float dy0 = rh * dX; + float dx1 = rh * dY; + float dy1 = rh * dX; + + spolygon.reset(4); + + float svertex[] = spolygon.vertices[0]; + svertex[X] = ox1+dx0; + svertex[Y] = oy1-dy0; + svertex[R] = r1; + svertex[G] = g1; + svertex[B] = b1; + svertex[A] = a1; + + svertex = spolygon.vertices[1]; + svertex[X] = ox1-dx0; + svertex[Y] = oy1+dy0; + svertex[R] = r1; + svertex[G] = g1; + svertex[B] = b1; + svertex[A] = a1; + + svertex = spolygon.vertices[2]; + svertex[X] = ox2-dx1; + svertex[Y] = oy2+dy1; + svertex[R] = r2; + svertex[G] = g2; + svertex[B] = b2; + svertex[A] = a2; + + svertex = spolygon.vertices[3]; + svertex[X] = ox2+dx1; + svertex[Y] = oy2-dy1; + svertex[R] = r2; + svertex[G] = g2; + svertex[B] = b2; + svertex[A] = a2; + + spolygon.render(); + } + + + // OPT version without z coords can save 8 multiplies and some other + private void spatial_line(float x1, float y1, + float r1, float g1, float b1, + float x2, float y2, + float r2, float g2, float b2) { + spatial_line(x1, y1, 0, r1, g1, b1, + x2, y2, 0, r2, g2, b2); + } + + + // the incoming values are transformed, + // and the colors have been calculated + + private void spatial_line(float x1, float y1, float z1, + float r1, float g1, float b1, + float x2, float y2, float z2, + float r2, float g2, float b2) { + spolygon.interpRGBA = (r1 != r2) || (g1 != g2) || (b1 != b2); + if (!spolygon.interpRGBA && + flat_line_retribution(x1, y1, x2, y2, r1, g1, b1)) { + return; + } + + spolygon.interpZ = true; + + float ox1 = x1; float oy1 = y1; float oz1 = z1; + float ox2 = x2; float oy2 = y2; float oz2 = z2; + + float dX = ox2-ox1 + 0.0001f; + float dY = oy2-oy1 + 0.0001f; + float len = sqrt(dX*dX + dY*dY); + + //float x0 = m00*0 + m01*0 + m03; + + float rh = strokeWeight / len; + + float dx0 = rh * dY; + float dy0 = rh * dX; + float dx1 = rh * dY; + float dy1 = rh * dX; + + spolygon.reset(4); + + float svertex[] = spolygon.vertices[0]; + svertex[X] = ox1+dx0; + svertex[Y] = oy1-dy0; + svertex[Z] = oz1; + svertex[R] = r1; //calcR1; + svertex[G] = g1; //calcG1; + svertex[B] = b1; //calcB1; + + svertex = spolygon.vertices[1]; + svertex[X] = ox1-dx0; + svertex[Y] = oy1+dy0; + svertex[Z] = oz1; + svertex[R] = r1; //calcR1; + svertex[G] = g1; //calcG1; + svertex[B] = b1; //calcB1; + + svertex = spolygon.vertices[2]; + svertex[X] = ox2-dx1; + svertex[Y] = oy2+dy1; + svertex[Z] = oz2; + svertex[R] = r2; //calcR2; + svertex[G] = g2; //calcG2; + svertex[B] = b2; //calcB2; + + svertex = spolygon.vertices[3]; + svertex[X] = ox2+dx1; + svertex[Y] = oy2-dy1; + svertex[Z] = oz2; + svertex[R] = r2; //calcR2; + svertex[G] = g2; //calcG2; + svertex[B] = b2; //calcB2; + + spolygon.render(); + } + + + // max is what to count to + // offset is offset to the 'next' vertex + // increment is how much to increment in the loop + private void draw_lines(float vertices[][], int max, + int offset, int increment, int skip) { + + if (strokeWeight < 2) { + for (int i = 0; i < max; i += increment) { + if ((skip != 0) && (((i+offset) % skip) == 0)) continue; + + float a[] = vertices[i]; + float b[] = vertices[i+offset]; + + 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]); + + line.draw(); + } + + } else { // use old line code for thickness > 1 + + if ((dimensions != 3) && unchangedZ) { + if ((strokeWeight < TWO) && !lighting && !strokeChanged) { + // need to set color at least once? + + // THIS PARTICULAR CASE SHOULD NO LONGER BE REACHABLE + + for (int i = 0; i < max; i += increment) { + if ((skip != 0) && (((i+offset) % skip) == 0)) continue; + thin_flat_line((int) vertices[i][X], + (int) vertices[i][Y], + (int) vertices[i+offset][X], + (int) vertices[i+offset][Y]); + } + } else { + for (int i = 0; i < max; i += increment) { + if ((skip != 0) && (((i+offset) % skip) == 0)) continue; + float v1[] = vertices[i]; + float v2[] = vertices[i+offset]; + thick_flat_line(v1[X], v1[Y], v1[SR], v1[SG], v1[SB], v1[SA], + v2[X], v2[Y], v2[SR], v2[SG], v2[SB], v2[SA]); + } + } + } else { + for (int i = 0; i < max; i += increment) { + if ((skip != 0) && (((i+offset) % skip) == 0)) continue; + float v1[] = vertices[i]; + float v2[] = vertices[i+offset]; + spatial_line(v1[X], v1[Y], v1[Z], v1[SR], v1[SG], v1[SB], + v2[X], v2[Y], v2[Z], v2[SR], v2[SG], v2[SB]); + } + } + } + } + + + + ////////////////////////////////////////////////////////////// + + // UGLY RENDERING SHIT + + + private void thin_point(int x, int y, float z, int color) { + // necessary? [fry] yes! [toxi] + if (x<0 || x>width1 || y<0 || y>height1) return; + + int index = y*width + x; + if ((color & 0xff000000) == 0xff000000) { // opaque + pixels[index] = color; + + } else { // transparent + // couldn't seem to get this working correctly + + //pixels[index] = _blend(pixels[index], + // color & 0xffffff, (color >> 24) & 0xff); + + // a1 is how much of the orig pixel + int a2 = (color >> 24) & 0xff; + int a1 = a2 ^ 0xff; + + int p2 = stroke; + int p1 = pixels[index]; + + int r = (a1 * ((p1 >> 16) & 0xff) + a2 * ((p2 >> 16) & 0xff)) & 0xff00; + int g = (a1 * ((p1 >> 8) & 0xff) + a2 * ((p2 >> 8) & 0xff)) & 0xff00; + int b = (a1 * ( p1 & 0xff) + a2 * ( p2 & 0xff)) >> 8; + + pixels[index] = 0xff000000 | (r << 8) | g | b; + + //pixels[index] = _blend(pixels[index], + // color & 0xffffff, (color >> 24) & 0xff); + /* + pixels[index] = 0xff000000 | + ((((a1 * ((pixels[index] >> 16) & 0xff) + + a2 * ((color >> 16) & 0xff)) & 0xff00) << 24) << 8) | + (((a1 * ((pixels[index] >> 8) & 0xff) + + a2 * ((color >> 8) & 0xff)) & 0xff00) << 16) | + (((a1 * ( pixels[index] & 0xff) + + a2 * ( color & 0xff)) >> 8)); + */ + } + zbuffer[index] = z; + } + + + // optimized because it's used so much + private void flat_rect(int x1, int y1, int x2, int y2) { + //System.out.println("flat quad"); + if (y2 < y1) { + int temp = y1; y1 = y2; y2 = temp; + } + if (x2 < x1) { + int temp = x1; x1 = x2; x2 = temp; + } + // checking to watch out for boogers + if ((x1 > width1) || (x2 < 0) || + (y1 > height1) || (y2 < 0)) return; + + if (_fill) { + int fx1 = x1; + int fy1 = y1; + int fx2 = x2; + int fy2 = y2; + + // these only affect the fill, not the stroke + // (otherwise strange boogers at edges b/c frame changes shape) + if (fx1 < 0) fx1 = 0; + if (fx2 > width) fx2 = width; + if (fy1 < 0) fy1 = 0; + if (fy2 > height) fy2 = height; + + // [toxi 031223] + // on avg. 20-25% faster fill routine using System.arraycopy() + int ww = fx2 - fx1; + int hh = fy2 - fy1; + int[] row = new int[ww]; + for (int i = 0; i < ww; i++) row[i] = fill; + int idx = fy1 * width + fx1; + for (int y = 0; y < hh; y++) { + System.arraycopy(row, 0, pixels, idx, ww); + idx += width; + } + row = null; + } + + // broken in the new graphics engine + if (!hints[NEW_GRAPHICS]) { + if (_stroke) { + if (strokeWeight == 1) { + thin_flat_line(x1, y1, x2, y1); + thin_flat_line(x2, y1, x2, y2); + thin_flat_line(x2, y2, x1, y2); + thin_flat_line(x1, y2, x1, y1); + + } else { + thick_flat_line(x1, y1, fillR, fillG, fillB, fillA, + x2, y1, fillR, fillG, fillB, fillA); + thick_flat_line(x2, y1, fillR, fillG, fillB, fillA, + x2, y2, fillR, fillG, fillB, fillA); + thick_flat_line(x2, y2, fillR, fillG, fillB, fillA, + x1, y2, fillR, fillG, fillB, fillA); + thick_flat_line(x1, y2, fillR, fillG, fillB, fillA, + x1, y1, fillR, fillG, fillB, fillA); + } + } + } + } + + + private void flat_circle(int centerX, int centerY, int radius) { + if (dimensions == 2) { // translate but no scale + centerX = (int) screenX(centerX, centerY, 0); + centerY = (int) screenY(centerX, centerY, 0); + } + if (_fill) flat_circle_fill(centerX, centerY, radius); + if (_stroke) flat_circle_stroke(centerX, centerY, radius); + } + + + /** + * Draw the outline around a flat circle using a bresenham-style + * algorithm. Adapted from drawCircle function in "Computer Graphics + * for Java Programmers" by Leen Ammeraal, p. 110 + * + * This function is included because the quality is so much better, + * and the drawing significantly faster than with adaptive ellipses + * drawn using the sine/cosine tables. + * + * Circle quadrants break down like so: + * | + * \ NNW | NNE / + * \ | / + * WNW \ | / ENE + * ------------------- + * WSW / | \ ESE + * / | \ + * / SSW | SSE \ + * | + * + * @param xc x center + * @param yc y center + * @param r radius + */ + private void flat_circle_stroke(int xC, int yC, int r) { + int x = 0, y = r, u = 1, v = 2 * r - 1, E = 0; + while (x < y) { + thin_point(xC + x, yC + y, 0, stroke); // NNE + thin_point(xC + y, yC - x, 0, stroke); // ESE + thin_point(xC - x, yC - y, 0, stroke); // SSW + thin_point(xC - y, yC + x, 0, stroke); // WNW + + x++; E += u; u += 2; + if (v < 2 * E) { + y--; E -= v; v -= 2; + } + if (x > y) break; + + thin_point(xC + y, yC + x, 0, stroke); // ENE + thin_point(xC + x, yC - y, 0, stroke); // SSE + thin_point(xC - y, yC - x, 0, stroke); // WSW + thin_point(xC - x, yC + y, 0, stroke); // NNW + } + } + + /** + * Heavily adapted version of the above algorithm that handles + * filling the ellipse. Works by drawing from the center and + * outwards to the points themselves. Has to be done this way + * because the values for the points are changed halfway through + * the function, making it impossible to just store a series of + * left and right edges to be drawn more quickly. + * + * @param xc x center + * @param yc y center + * @param r radius + */ + private void flat_circle_fill(int xc, int yc, int r) { + int x = 0, y = r, u = 1, v = 2 * r - 1, E = 0; + while (x < y) { + for (int xx = xc; xx < xc + x; xx++) { // NNE + thin_point(xx, yc + y, 0, fill); + } + for (int xx = xc; xx < xc + y; xx++) { // ESE + thin_point(xx, yc - x, 0, fill); + } + for (int xx = xc - x; xx < xc; xx++) { // SSW + thin_point(xx, yc - y, 0, fill); + } + for (int xx = xc - y; xx < xc; xx++) { // WNW + thin_point(xx, yc + x, 0, fill); + } + + x++; E += u; u += 2; + if (v < 2 * E) { + y--; E -= v; v -= 2; + } + if (x > y) break; + + for (int xx = xc; xx < xc + y; xx++) { // ENE + thin_point(xx, yc + x, 0, fill); + } + for (int xx = xc; xx < xc + x; xx++) { // SSE + thin_point(xx, yc - y, 0, fill); + } + for (int xx = xc - y; xx < xc; xx++) { // WSW + thin_point(xx, yc - x, 0, fill); + } + for (int xx = xc - x; xx < xc; xx++) { // NNW + thin_point(xx, yc + y, 0, fill); + } + } + } + + // unfortunately this can't handle fill and stroke simultaneously, + // because the fill will later replace some of the stroke points + + private final void flat_ellipse_symmetry(int centerX, int centerY, + int ellipseX, int ellipseY, + boolean filling) { + if (filling) { + for (int i = centerX - ellipseX + 1; i < centerX + ellipseX; i++) { + thin_point(i, centerY - ellipseY, 0, fill); + thin_point(i, centerY + ellipseY, 0, fill); + } + } else { + thin_point(centerX - ellipseX, centerY + ellipseY, 0, stroke); + thin_point(centerX + ellipseX, centerY + ellipseY, 0, stroke); + thin_point(centerX - ellipseX, centerY - ellipseY, 0, stroke); + thin_point(centerX + ellipseX, centerY - ellipseY, 0, stroke); + } + } + + + /** + * Bresenham-style ellipse drawing function, adapted from a posting to + * comp.graphics.algortihms. + * + * This function is included because the quality is so much better, + * and the drawing significantly faster than with adaptive ellipses + * drawn using the sine/cosine tables. + * + * @param centerX x coordinate of the center + * @param centerY y coordinate of the center + * @param a horizontal radius + * @param b vertical radius + */ + private void flat_ellipse_internal(int centerX, int centerY, + int a, int b, boolean filling) { + int x, y, a2, b2, s, t; + + a2 = a*a; + b2 = b*b; + x = 0; + y = b; + s = a2*(1-2*b) + 2*b2; + t = b2 - 2*a2*(2*b-1); + flat_ellipse_symmetry(centerX, centerY, x, y, filling); + + do { + if (s < 0) { + s += 2*b2*(2*x+3); + t += 4*b2*(x+1); + x++; + } else if (t < 0) { + s += 2*b2*(2*x+3) - 4*a2*(y-1); + t += 4*b2*(x+1) - 2*a2*(2*y-3); + x++; + y--; + } else { + s -= 4*a2*(y-1); + t -= 2*a2*(2*y-3); + y--; + } + flat_ellipse_symmetry(centerX, centerY, x, y, filling); + + } while (y > 0); + } + + + private void flat_ellipse(int centerX, int centerY, int a, int b) { + if (dimensions == 2) { // probably a translate but no scale + centerX = (int) screenX(centerX, centerY, 0); + centerY = (int) screenY(centerX, centerY, 0); + } + if (_fill) flat_ellipse_internal(centerX, centerY, a, b, true); + if (_stroke) flat_ellipse_internal(centerX, centerY, a, b, false); + } + + + /** + * Image drawn in flat "screen space", with no scaling or warping. + * this is so common that a special routine is included for it, + * because the alternative is much slower. + * + * @param image image to be drawn + * @param sx1 x coordinate of upper-lefthand corner in screen space + * @param sy1 y coordinate of upper-lefthand corner in screen space + */ + public void flat_image(BImage image, int sx1, int sy1) { + int ix1 = 0; + int iy1 = 0; + int ix2 = image.width; + int iy2 = image.height; + + if (image_mode == CENTER_DIAMETER) { + sx1 -= image.width / 2; + sy1 -= image.height / 2; + } + + int sx2 = sx1 + image.width; + int sy2 = sy1 + image.height; + + // don't draw if completely offscreen + // (without this check, ArrayIndexOutOfBoundsException) + if ((sx1 > width1) || (sx2 < 0) || + (sy1 > height1) || (sy2 < 0)) return; + + if (sx1 < 0) { // off left edge + ix1 -= sx1; + sx1 = 0; + } + if (sy1 < 0) { // off top edge + iy1 -= sy1; + sy1 = 0; + } + if (sx2 > width) { // off right edge + ix2 -= sx2 - width; + sx2 = width; + } + if (sy2 > height) { // off bottom edge + iy2 -= sy2 - height; + sy2 = height; + } + + int source = iy1 * image.width + ix1; + int target = sy1 * width; + + if (image.format == RGBA) { + for (int y = sy1; y < sy2; y++) { + int tx = 0; + + for (int x = sx1; x < sx2; x++) { + pixels[target + x] = + _blend(pixels[target + x], + image.pixels[source + tx], + image.pixels[source + tx++] >>> 24); + } + source += image.width; + target += width; + } + } else if (image.format == ALPHA) { + for (int y = sy1; y < sy2; y++) { + int tx = 0; + + for (int x = sx1; x < sx2; x++) { + pixels[target + x] = + _blend(pixels[target + x], + fill, + image.pixels[source + tx++]); + } + source += image.width; + target += width; + } + + } else if (image.format == RGB) { + target += sx1; + int tw = sx2 - sx1; + for (int y = sy1; y < sy2; y++) { + System.arraycopy(image.pixels, source, pixels, target, tw); + // should set z coordinate in here + // or maybe not, since dims=0, meaning no relevant z + source += image.width; + target += width; + } + } + } + + + + ////////////////////////////////////////////////////////////// + + // SIMPLE SHAPES WITH ANALOGUES IN beginShape() + + + public void point(float x, float y) { + //line(x, y, x, y); + beginShape(POINTS); + vertex(x, y); + endShape(); + } + + + public void point(float x, float y, float z) { + //line(x, y, z, x, y, z); + beginShape(POINTS); + vertex(x, y, z); + endShape(); + } + + + public void line(float x1, float y1, float x2, float y2) { + beginShape(LINES); + vertex(x1, y1); + vertex(x2, y2); + endShape(); + } + + + 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); + 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); + vertex(x1, y1); + vertex(x2, y2); + vertex(x3, y3); + vertex(x4, y4); + endShape(); + } + + + ////////////////////////////////////////////////////////////// + + // 2D SHAPES, DRAWN WITH x/y/w/h + + + public void rectMode(int mode) { + rect_mode = mode; + } + + + public void rect(float x1, float y1, float x2, float y2) { + float hradius, vradius; + switch (rect_mode) { + 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_DIAMETER: + hradius = x2 / 2.0f; + vradius = y2 / 2.0f; + x2 = x1 + hradius; + y2 = y1 + vradius; + x1 -= hradius; + y1 -= vradius; + } + + if ((dimensions == 0) && !lighting && !fill_alpha) { + // draw in 2D + flat_rect((int) x1, (int) y1, (int) x2, (int) y2); + + } else { + // draw in 3D + beginShape(QUADS); + vertex(x1, y1); + vertex(x2, y1); + vertex(x2, y2); + vertex(x1, y2); + endShape(); + } + } + + + public void ellipseMode(int mode) { + ellipse_mode = mode; + } + + + // adaptive ellipse accuracy contributed by toxi + public void ellipse(float x, float y, float hradius, float vradius) { + switch (ellipse_mode) { + case CENTER_RADIUS: + break; + case CENTER_DIAMETER: + hradius /= 2f; vradius /= 2f; + break; + case CORNER: + hradius /= 2f; vradius /= 2f; + x += hradius; y += vradius; + break; + case CORNERS: + hradius -= x; + vradius -= y; + break; + } + + // adapt accuracy to radii used w/ a minimum of 4 segments [toxi] + // now uses current scale factors to determine "real" transformed radius + + //System.out.println(m00 + " " + m11); + //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); + + boolean plain = + !lighting && !smooth && (strokeWeight == 1) && + !fill_alpha && !stroke_alpha; + + boolean flat = (dimensions == 0) || + ((dimensions == 2) && (m00 == m11) && (m00 == 1)); + + if (plain && flat) { + if (hradius == vradius) { + //if ((dimensions == 0) && + //!lighting && !smooth && (hradius == vradius)) { + flat_circle((int)x, (int)y, (int)hradius); + //if (_fill) flat_circle_fill((int)x, (int)y, (int)hradius); + //if (_stroke) flat_circle_stroke((int)x, (int)y, (int)hradius); + + //} else if (((dimensions == 0) || ((dimensions == 2) && + // (m00 == m11) && (m00 == 1))) && + // !lighting && !smooth) { + } else { + flat_ellipse((int)x, (int)y, (int)hradius, (int)vradius); + } + + } else { + // [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(x+cosLUT[(int)val]*hradius, y+sinLUT[(int)val]*vradius); + val += inc; + } + // unnecessary extra point that spoiled triangulation [rocha] + if (!hints[NEW_GRAPHICS]) { + vertex(x + cosLUT[0]*hradius, y + sinLUT[0]*vradius); + } + endShape(); + } + } + + + ////////////////////////////////////////////////////////////// + + // 3D SHAPES, DRAWN FROM CENTER + + + // solid or wire depends on settings for stroke and fill + // slices/stacks can be set by an advanced option + + //public void cube(float size) { + public void box(float size) { + //box(-size/2, -size/2, -size/2, size/2, size/2, size/2); + 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 x1, float y1, float z1, + // float x2, float y2, float z2) { + 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 (hints[NEW_GRAPHICS]) triangle.setCulling(true); + + beginShape(QUADS); + + // front + vertex(x1, y1, z1); + vertex(x2, y1, z1); + vertex(x2, y2, z1); + vertex(x1, y2, z1); + + // right + vertex(x2, y1, z1); + vertex(x2, y1, z2); + vertex(x2, y2, z2); + vertex(x2, y2, z1); + + // back + vertex(x2, y1, z2); + vertex(x1, y1, z2); + vertex(x1, y2, z2); + vertex(x2, y2, z2); + + // left + vertex(x1, y1, z2); + vertex(x1, y1, z1); + vertex(x1, y2, z1); + vertex(x1, y2, z2); + + // top + vertex(x1, y1, z2); + vertex(x2, y1, z2); + vertex(x2, y1, z1); + vertex(x1, y1, z1); + + // bottom + vertex(x1, y2, z1); + vertex(x2, y2, z1); + vertex(x2, y2, z2); + vertex(x1, y2, z2); + + endShape(); + + if (hints[NEW_GRAPHICS]) triangle.setCulling(false); + } + + + // [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 != sphere_detail) { + 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; + } + sphere_detail = res; + } + } + + + // 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) { + sphere(0, 0, 0, r); + } + + public void sphere(float x, float y, float z, float r) { + if (sphere_detail == 0) { + sphereDetail(30); + } + + int v1,v11,v2; + push(); + if (x!=0f && y!=0f && z!=0f) translate(x,y,z); + scale(r); + + if (hints[NEW_GRAPHICS]) triangle.setCulling(true); + + // 1st ring from south pole + beginShape(TRIANGLE_STRIP); + for (int i = 0; i < sphere_detail; i++) { + vertex(0, -1, 0); + vertex(sphereX[i], sphereY[i], sphereZ[i]); + } + vertex(0, -1, 0); + vertex(sphereX[0], sphereY[0], sphereZ[0]); + endShape(); + + // middle rings + int voff = 0; + for(int i = 2; i < sphere_detail; i++) { + v1=v11=voff; + voff += sphere_detail; + v2=voff; + beginShape(TRIANGLE_STRIP); + for (int j = 0; j < sphere_detail; j++) { + vertex(sphereX[v1], sphereY[v1], sphereZ[v1++]); + vertex(sphereX[v2], sphereY[v2], sphereZ[v2++]); + } + // close each ring + v1=v11; + v2=voff; + vertex(sphereX[v1], sphereY[v1], sphereZ[v1]); + vertex(sphereX[v2], sphereY[v2], sphereZ[v2]); + endShape(); + } + + // add the northern cap + beginShape(TRIANGLE_STRIP); + for (int i = 0; i < sphere_detail; i++) { + v2 = voff + i; + vertex(0, 1, 0); + vertex(sphereX[v2], sphereY[v2], sphereZ[v2]); + } + vertex(0, 1, 0); + vertex(sphereX[voff], sphereY[voff], sphereZ[voff]); + endShape(); + pop(); + + if (hints[NEW_GRAPHICS]) triangle.setCulling(false); + //triangle.setCulling(false); + } + + + + ////////////////////////////////////////////////////////////// + + // 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 = bezier(85, 10, 90, 15, t); + * float y = bezier(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; + + // quadratic bezier + //return a*t1*t1 + 2*b*t*t1 + c*t*t; + + // cubic bezier + //return a*t*t*t + 3*b*t*t*t1 + 3*c*t*t1*t1 + d*t1*t1*t1; + 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); + } + + + public void bezier(float x1, float y1, + float x2, float y2, + float x3, float y3, + float x4, float y4) { + beginShape(LINE_STRIP); + bezierVertex(x1, y1); + bezierVertex(x2, y2); + bezierVertex(x3, y3); + bezierVertex(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) { + beginShape(LINE_STRIP); + bezierVertex(x1, y1, z1); + bezierVertex(x2, y2, z2); + bezierVertex(x3, y3, z3); + bezierVertex(x4, y4, z4); + endShape(); + } + + + //static final int BEZIER_SEGMENTS = 20; + private boolean bezier_inited = false; + private int bezier_segments = 20; //BEZIER_SEGMENTS; + // msjvm complained when bezier_basis was final + private float bezier_basis[][] = { + { -1, 3, -3, 1}, + { 3, -6, 3, 0}, + { -3, 3, 0, 0}, + { 1, 0, 0, 0} + }; + private float bezier_forward[][]; // = new float[4][4]; + private float bezier_draw[][]; // = new float[4][4]; + + + private void bezier_init() { + bezierSegments(bezier_segments); //BEZIER_SEGMENTS); + //bezier_inited = true; + } + + + public void bezierSegments(int segments) { + if (bezier_forward == null) { + bezier_forward = new float[4][4]; + bezier_draw = new float[4][4]; + } + bezier_segments = segments; + bezier_inited = true; + + // setup matrix for forward differencing to speed up drawing + setup_spline_forward(segments, 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); + } + + + // catmull-rom basis matrix, perhaps with optional s parameter + private boolean curve_inited = false; + //static final int CURVE_SEGMENTS = 20; + private int curve_segments = 20; //CURVE_SEGMENTS; + private float curve_tightness = 0; + private float curve_basis[][]; // = new float[4][4]; + private float curve_forward[][]; // = new float[4][4]; + private float curve_draw[][]; + + + private void curve_init() { + curve_mode(curve_segments, curve_tightness); + //curve_inited = true; + } + + + public void curveSegments(int segments) { + curve_mode(segments, curve_tightness); + //curve_inited = true; + } + + + public void curveTightness(float tightness) { + curve_mode(curve_segments, tightness); + //curve_inited = true; + } + + + /** + * 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) + */ + private void curve_mode(int segments, float s) { + curve_segments = segments; + //curve_mode = ((curve_tightness != 0) || + // (curve_segments != CURVE_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); + + // 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); + } + + + /** + * 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; + } + + + /** + * Draws a segment of Catmull-Rom curve. + * + * This function doubles the first and last points to use them + * as control points, because four vertices for a Catmull-Rom + * is basically a line. So in the end, this will produce three + * curve segments (where bezier would only produce one with its + * complementary function). + */ + 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(x1, y1); + curveVertex(x2, y2); + curveVertex(x3, y3); + curveVertex(x4, y4); + 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) { + beginShape(LINE_STRIP); + curveVertex(x1, y1, z1); + curveVertex(x1, y1, z1); + curveVertex(x2, y2, z2); + curveVertex(x3, y3, z3); + curveVertex(x4, y4, z4); + curveVertex(x4, y4, z4); + endShape(); + } + + + /** + * 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 + */ + private 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]); + private 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. + */ + private void spline_segment(float x1, float y1, float x2, float y2, + float x3, float y3, float x4, float y4, + float x0, float y0, float m[][], int segments) { + + 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(x0, y0); + for (int j = 0; j < segments; j++) { + x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3; + y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3; + vertex(x0, y0); + } + } + + + private void spline_segment(float x1, float y1, float z1, + float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4, + float x0, float y0, float z0, + float m[][], int segments) { + + 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(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(x1, y1, z1); + vertex(x0, y0, z0); + } + } + + + + ////////////////////////////////////////////////////////////// + + // IMAGES + + + private Image gimmeImage(URL url, boolean force) { + Toolkit tk = Toolkit.getDefaultToolkit(); + + URLConnection conn = null; + try { + //conn = new URLConnection(url); + conn = url.openConnection(); + + // i don't think this does anything, + // but just set the fella for good measure + conn.setUseCaches(false); + // also had a note from zach about parent.obj.close() on url + // but that doesn't seem to be needed anymore... + + // throws an exception if it doesn't exist + conn.connect(); + + if (!force) { + // how do you close the bastard? + conn = null; + // close connection and just use regular method + return tk.getImage(url); + } + + // slurp contents of that stream + InputStream stream = conn.getInputStream(); + + BufferedInputStream bis = new BufferedInputStream(stream); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + int c = bis.read(); + while (c != -1) { + out.write(c); + c = bis.read(); + } + } catch (IOException e) { + return null; + } + bis.close(); // will this help? + //byte bytes[] = out.toByteArray(); + + // build an image out of it + //return tk.createImage(bytes); + return tk.createImage(out.toByteArray()); + + } catch (Exception e) { // null pointer or i/o ex + //System.err.println("error loading image: " + url); + return null; + } + } + + public BImage loadImage(String filename) { + if (filename.toLowerCase().endsWith(".tga")) { + return loadTargaImage(filename); + } + return loadImage(filename, true); + } + + // returns null if no image of that name is found + public BImage loadImage(String filename, boolean force) { + Image awtimage = null; + //String randomizer = "?" + nf((int) (random()*10000), 4); + + if (filename.startsWith("http://")) { + try { + URL url = new URL(filename); + awtimage = gimmeImage(url, force); + + } catch (MalformedURLException e) { + System.err.println("error loading image from " + filename); + e.printStackTrace(); + return null; + } + + } else { + awtimage = gimmeImage(getClass().getResource(filename), force); + if (awtimage == null) { + awtimage = + gimmeImage(getClass().getResource("data/" + filename), force); + } + /* + boolean insideBrowser = true; + try { + applet.getAppletContext(); + } catch (NullPointerException e) { + insideBrowser = false; + force = false; // the ?2394 trick won't work + } + + System.out.println("get dat way"); + URL url = force ? + getClass().getResource(filename); + try { + url.openConnection(); + } catch (Exception e) { + System.out.println("unhappy"); + e.printStackTrace(); + } + //awtimage = tk.getImage(); + System.out.println("got url"); + awtimage = tk.getImage(url); + + if (awtimage == null) { + System.out.println("get dis way"); + awtimage = tk.getImage(getClass().getResource("data/" + filename)); + } + */ + } + if (awtimage == null) { + System.err.println("could not load image " + filename); + return null; + } + + Component component = applet; + if (component == null) { + component = new Frame(); + ((Frame)component).pack(); + // now we have a peer! yay! + } + + MediaTracker tracker = new MediaTracker(component); + tracker.addImage(awtimage, 0); + try { + tracker.waitForAll(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + int jwidth = awtimage.getWidth(null); + int jheight = awtimage.getHeight(null); + + int jpixels[] = new int[jwidth*jheight]; + PixelGrabber pg = + new PixelGrabber(awtimage, 0, 0, jwidth, jheight, jpixels, 0, jwidth); + try { + pg.grabPixels(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + //int format = RGB; + if (filename.toLowerCase().endsWith(".gif")) { + // if it's a .gif image, test to see if it has transparency + for (int i = 0; i < jpixels.length; i++) { + // since transparency is often at corners, hopefully this + // will find a non-transparent pixel quickly and exit + if ((jpixels[i] & 0xff000000) != 0xff000000) { + return new BImage(jpixels, jwidth, jheight, RGBA); + //format = RGBA; + //break; + } + } + } + return new BImage(jpixels, jwidth, jheight, RGB); + } + + + /* + public BImage loadImage(String file) { + try { + byte[] imgarray=loadBytes(file); + java.awt.Image awtimage = + Toolkit.getDefaultToolkit().createImage(imgarray); + MediaTracker tracker = new MediaTracker(this); + tracker.addImage(awtimage, 0); + try { + tracker.waitForAll(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + int w = awtimage.getWidth(null); + int h = awtimage.getHeight(null); + int[] pix = new int[w*h]; + + PixelGrabber pg = new PixelGrabber(awtimage, 0, 0, w, h, pix, 0, w); + + try { + pg.grabPixels(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + BImage img=new BImage(pix,w,h,RGB); + + if (file.toLowerCase().endsWith(".gif")) { + for (int i = 0; i < pix.length; i++) { + if ((pix[i] & 0xff000000) != 0xff000000) { + img.format=RGBA; + break; + } + } + } + return img; + } + catch(Exception e) { + return null; + } + } + */ + + + // [toxi 040304] Targa bitmap loader for 24/32bit RGB(A) + + // [fry] this could be optimized to not use loadBytes + // which would help out memory situations with large images + + protected BImage loadTargaImage(String filename) { + // load image file as byte array + byte[] buffer = loadBytes(filename); + + // check if it's a TGA and has 8bits/colour channel + if (buffer[2] == 2 && buffer[17] == 8) { + // get image dimensions + //int w=(b2i(buffer[13])<<8) + b2i(buffer[12]); + int w = ((buffer[13] & 0xff) << 8) + (buffer[12] & 0xff); + //int h=(b2i(buffer[15])<<8) + b2i(buffer[14]); + int h = ((buffer[15] & 0xff) << 8) + (buffer[14] & 0xff); + // check if image has alpha + boolean hasAlpha=(buffer[16] == 32); + + // setup new image object + BImage img = new BImage(w,h); + img.format = (hasAlpha ? RGBA : RGB); + + // 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); + + //b2i(buffer[offset++]) | b2i(buffer[offset++])<<8 | b2i(buffer[offset++])<<16; + // set alpha based on alpha data or revert to 100% (if there's only a 24bit image) + //if (hasAlpha) img.pixels[index + x]|=b2i(buffer[offset++])<<24; + //else img.pixels[index + x] |= 0xff000000; + } + index -= w; + } + return img; + } + System.err.println("loadImage(): bad targa image format"); + return null; + } + + + public void imageMode(int mode) { + image_mode = mode; + } + + public void image(BImage image, float x1, float y1) { + if ((dimensions == 0) && !lighting && !_tint && + (image_mode != CENTER_RADIUS)) { + // if drawing a flat image with no warping, + // use faster routine to draw direct to the screen + flat_image(image, (int)x1, (int)y1); + + } else { + image(image, x1, y1, image.width, image.height, + 0, 0, image.width, image.height); + } + } + + + public void image(BImage image, + float x1, float y1, float x2, float y2) { + image(image, x1, y1, x2, y2, 0, 0, image.width, image.height); + } + + + public void image(BImage image, + float x1, float y1, float x2, float y2, + float u1, float v1, float u2, float v2) { + switch (image_mode) { + case CORNERS: + break; + case CORNER: + x2 += x1; y2 += y1; + break; + case CENTER_DIAMETER: + x2 /= 2f; + y2 /= 2f; + case CENTER_RADIUS: + float hr = x2; + float vr = y2; + x2 = x1 + hr; + y2 = y1 + vr; + x1 -= hr; + y1 -= vr; + break; + } + + // fill must be set to 'true' for image to show up + // (although need to do some sort of color blending) + // stroke should be set to false or it gets confusing + // (and annoying) because one has to keep disabling stroke + + boolean savedStroke = _stroke; + boolean savedFill = _fill; + + float savedFillR = fillR; + float savedFillG = fillG; + float savedFillB = fillB; + float savedFillA = fillA; + + _stroke = false; + _fill = true; + + if (_tint) { + fillR = tintR; + fillG = tintG; + fillB = tintB; + fillA = tintA; + + } else { + fillR = fillG = fillB = fillA = 1; + } + + beginShape(QUADS); + texture(image); // moved outside.. make javagl happier? + 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; + + fillR = savedFillR; + fillG = savedFillG; + fillB = savedFillB; + fillA = savedFillA; + } + + + public void cache(BImage image) { + } + + public void cache(BImage images[]) { + } + + protected void cache(BImage image, int index) { + } + + + ////////////////////////////////////////////////////////////// + + // TEXT/FONTS + + + public BFont loadFont(String name) { + try { + BFont font = new BFont(name, this); + return font; + + } catch (IOException e) { + System.err.println("Could not load font " + name); + System.err.println("Make sure that the font has been copied"); + System.err.println("to the data folder of your sketch."); + e.printStackTrace(); + } + return null; + } + + + public void textFont(BFont which) { + if (which == null) { + System.err.println("Ignoring improperly loaded font in textFont()"); + return; + } + text_font = which; + if (text_space != SCREEN_SPACE) { + text_font.size(); + } else { + text_font.size(text_font.iwidth); + } + text_font.leading(); + } + + public void textFont(BFont which, float size) { + if (which == null) { + System.err.println("Ignoring improperly loaded font in textFont()"); + return; + } + text_font = which; + if (text_space != SCREEN_SPACE) { + text_font.size(size); + } else { + System.err.println("Cannot set size of SCREEN_SPACE fonts"); + text_font.size(text_font.iwidth); + } + text_font.leading(); + } + + public void textSize(float size) { + if (text_font == null) { + System.err.println("First set a font before setting its size."); + return; + } + if (text_space == SCREEN_SPACE) { + System.err.println("Cannot set size of SCREEN_SPACE fonts."); + return; + } + text_font.size(size); + } + + public void textLeading(float leading) { + if (text_font == null) { + System.err.println("First set a font before setting its leading."); + return; + } + text_font.leading(leading); + } + + public void textMode(int mode) { + text_mode = mode; + } + + public void textSpace(int space) { + text_space = space; + + if ((space == SCREEN_SPACE) && (text_font != null)) { + text_font.size(text_font.iwidth); + text_font.leading(); + } + } + + + public void text(char c, float x, float y) { + text(c, x, y, 0); + } + + public void text(char c, float x, float y, float z) { + if (text_font == null) { + System.err.println("text(): first set a font before drawing text"); + return; + } + if (text_mode == ALIGN_CENTER) { + x -= text_font.width(c) / 2f; + + } else if (text_mode == ALIGN_RIGHT) { + x -= text_font.width(c); + } + text_font.text(c, x, y, z, this); + } + + + public void text(String s, float x, float y) { + text(s, x, y, 0); + } + + public void text(String s, float x, float y, float z) { + if (text_font == null) { + System.err.println("text(): first set a font before drawing text"); + return; + } + if (text_mode == ALIGN_CENTER) { + x -= text_font.width(s) / 2f; + + } else if (text_mode == ALIGN_RIGHT) { + x -= text_font.width(s); + } + text_font.text(s, x, y, z, this); + } + + + public void text(int num, float x, float y) { + text(String.valueOf(num), x, y, 0); + } + + public void text(int num, float x, float y, float z) { + text(String.valueOf(num), x, y, z); + } + + + public void text(float num, float x, float y) { + text(BApplet.nfs(num, 0, 3), x, y, 0); + } + + /** + * This does a basic number formatting, to avoid the + * generally ugly appearance of printing floats. + * Users who want more control should use their own nfs() cmmand. + */ + public void text(float num, float x, float y, float z) { + text(BApplet.nfs(num, 0, 3), x, y, z); + } + + + ////////////////////////////////////////////////////////////// + + // MATRIX MATH + + + public void push() { + if (matrixStackDepth+1 == MATRIX_STACK_DEPTH) { + message(COMPLAINT, "matrix stack overflow, to much pushmatrix"); + return; + } + float cm[] = matrixStack[matrixStackDepth]; + cm[ 0] = m00; cm[ 1] = m01; cm[ 2] = m02; cm[ 3] = m03; + cm[ 4] = m10; cm[ 5] = m11; cm[ 6] = m12; cm[ 7] = m13; + cm[ 8] = m20; cm[ 9] = m21; cm[10] = m22; cm[11] = m23; + cm[12] = m30; cm[13] = m31; cm[14] = m32; cm[15] = m33; + matrixStackDepth++; + } + + + public void pop() { + if (matrixStackDepth == 0) { + message(COMPLAINT, "matrix stack underflow, to many popmatrix"); + return; + } + matrixStackDepth--; + float cm[] = matrixStack[matrixStackDepth]; + m00 = cm[ 0]; m01 = cm[ 1]; m02 = cm[ 2]; m03 = cm[ 3]; + m10 = cm[ 4]; m11 = cm[ 5]; m12 = cm[ 6]; m13 = cm[ 7]; + m20 = cm[ 8]; m21 = cm[ 9]; m22 = cm[10]; m23 = cm[11]; + m30 = cm[12]; m31 = cm[13]; m32 = cm[14]; m33 = cm[15]; + + if ((matrixStackDepth == 0) && + (m00 == 1) && (m01 == 0) && (m02 == 0) && (m03 == 0) && + (m10 == 0) && (m11 == 1) && (m12 == 0) && (m13 == 0) && + (m20 == 0) && (m21 == 0) && (m22 == 1) && (m23 == 0) && + (m30 == 0) && (m31 == 0) && (m32 == 0) && (m33 == 1)) { + dimensions = 0; + } + } + + + public void resetMatrix() { + dimensions = 0; + m00 = 1; m01 = 0; m02 = 0; m03 = 0; + m10 = 0; m11 = 1; m12 = 0; m13 = 0; + m20 = 0; m21 = 0; m22 = 1; m23 = 0; + m30 = 0; m31 = 0; m32 = 0; m33 = 1; + } + + + 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) { + //modelMatrixIsIdentity = false; + + 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 printMatrix() { + int big = (int) 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))))); + int d = 1; + while ((big /= 10) != 0) d++; + + BApplet.println(BApplet.nfs(m00, d, 4) + " " + BApplet.nfs(m01, d, 4) + " " + + BApplet.nfs(m02, d, 4) + " " + BApplet.nfs(m03, d, 4)); + + BApplet.println(BApplet.nfs(m10, d, 4) + " " + BApplet.nfs(m11, d, 4) + " " + + BApplet.nfs(m12, d, 4) + " " + BApplet.nfs(m13, d, 4)); + + BApplet.println(BApplet.nfs(m20, d, 4) + " " + BApplet.nfs(m21, d, 4) + " " + + BApplet.nfs(m22, d, 4) + " " + BApplet.nfs(m23, d, 4)); + + BApplet.println(BApplet.nfs(m30, d, 4) + " " + BApplet.nfs(m31, d, 4) + " " + + BApplet.nfs(m32, d, 4) + " " + BApplet.nfs(m33, d, 4)); + + BApplet.println(); + } + + + ////////////////////////////////////////////////////////////// + + + public void beginCamera() { + resetMatrix(); + } + + public void cameraMode(int icameraMode) { + camera_mode = icameraMode; // this doesn't do much + + if (camera_mode == PERSPECTIVE) { + beginCamera(); + perspective(fov, aspect, nearDist, farDist); + lookat(eyeX, eyeY, eyeDist, eyeX, eyeY, 0, 0, 1, 0); + endCamera(); + + } else if (camera_mode == ORTHOGRAPHIC) { + beginCamera(); + ortho(0, width, 0, height, -10, 10); + endCamera(); + } + } + + public void endCamera() { + p00 = m00; p01 = m01; p02 = m02; p03 = m03; + p10 = m10; p11 = m11; p12 = m12; p13 = m13; + p20 = m20; p21 = m21; p22 = m22; p23 = m23; + p30 = m30; p31 = m31; p32 = m32; p33 = m33; + resetMatrix(); + } + + + public void printCamera() { + int big = (int) max(max(max(max(abs(p00), abs(p01)), max(abs(p02), abs(p03))), + max(max(abs(p10), abs(p11)), max(abs(p12), abs(p13)))), + max(max(max(abs(p20), abs(p21)), max(abs(p22), abs(p23))), + max(max(abs(p30), abs(p31)), max(abs(p32), abs(p33))))); + int d = 1; + while ((big /= 10) != 0) d++; + + BApplet.println(BApplet.nfs(p00, d, 4) + " " + BApplet.nfs(p01, d, 4) + " " + + BApplet.nfs(p02, d, 4) + " " + BApplet.nfs(p03, d, 4)); + + BApplet.println(BApplet.nfs(p10, d, 4) + " " + BApplet.nfs(p11, d, 4) + " " + + BApplet.nfs(p12, d, 4) + " " + BApplet.nfs(p13, d, 4)); + + BApplet.println(BApplet.nfs(p20, d, 4) + " " + BApplet.nfs(p21, d, 4) + " " + + BApplet.nfs(p22, d, 4) + " " + BApplet.nfs(p23, d, 4)); + + BApplet.println(BApplet.nfs(p30, d, 4) + " " + BApplet.nfs(p31, d, 4) + " " + + BApplet.nfs(p32, d, 4) + " " + BApplet.nfs(p33, d, 4)); + + BApplet.println(); + } + + + // all the screenX/Y/Z and objectX/Y/Z functions return + // values based on there being a 3D scene. the assumption is + // that even if dimensions isn't necessarily 3, the only + // time you'll want to use these functions is when there + // has been a transformation, and they're not intended to be + // fast anyway, so it should be just fine that way. + + public float screenX(float x, float y, float z) { + float ax = m00*x + m01*y + m02*z + m03; + float ay = m10*x + m11*y + m12*z + m13; + float az = m20*x + m21*y + m22*z + m23; + float aw = m30*x + m31*y + m32*z + m33; + + float ox = p00*ax + p01*ay + p02*az + p03*aw; + float ow = p30*ax + p31*ay + p32*az + p33*aw; + + if (ow != 0) ox /= ow; + return width * (1 + ox) / 2.0f; + } + + + public float screenY(float x, float y, float z) { + float ax = m00*x + m01*y + m02*z + m03; + float ay = m10*x + m11*y + m12*z + m13; + float az = m20*x + m21*y + m22*z + m23; + float aw = m30*x + m31*y + m32*z + m33; + + float oy = p10*ax + p11*ay + p12*az + p13*aw; + float ow = p30*ax + p31*ay + p32*az + p33*aw; + + if (ow != 0) oy /= ow; + return height * (1 + oy) / 2.0f; + } + + + public float screenZ(float x, float y, float z) { + float ax = m00*x + m01*y + m02*z + m03; + float ay = m10*x + m11*y + m12*z + m13; + float az = m20*x + m21*y + m22*z + m23; + float aw = m30*x + m31*y + m32*z + m33; + + float oz = p20*ax + p21*ay + p22*az + p23*aw; + float ow = p30*ax + p31*ay + p32*az + p33*aw; + + if (ow != 0) oz /= ow; + return (oz + 1) / 2.0f; + } + + + public float objectX(float x, float y, float z) { + float ax = m00*x + m01*y + m02*z + m03; + float aw = m30*x + m31*y + m32*z + m33; + return (aw != 0) ? ax / aw : ax; + } + + + public float objectY(float x, float y, float z) { + float ay = m10*x + m11*y + m12*z + m13; + float aw = m30*x + m31*y + m32*z + m33; + return (aw != 0) ? ay / aw : ay; + } + + + public float objectZ(float x, float y, float z) { + float az = m20*x + m21*y + m22*z + m23; + float aw = m30*x + m31*y + m32*z + m33; + return (aw != 0) ? az / aw : az; + } + + + ////////////////////////////////////////////////////////////// + + + // based on mesa, 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); + + applyMatrix(x, 0, 0, tx, + 0, y, 0, ty, + 0, 0, z, tz, + 0, 0, 0, 1); + } + + + // based on mesa, glu.c + public void perspective(float fovy, float aspect, float zNear, float zFar) { + //System.out.println("perspective: " + fovy + " " + aspect + " " + + // zNear + " " + zFar); + float ymax = zNear * tan(fovy * PI / 360.0f); + float ymin = -ymax; + + float xmin = ymin * aspect; + float xmax = ymax * aspect; + + frustum(xmin, xmax, ymin, ymax, zNear, zFar); + } + + + // implemented based on gl ref book + public void frustum(float left, float right, float bottom, + float top, float znear, float zfar) { + //System.out.println("frustum: " + left + " " + right + " " + + // bottom + " " + top + " " + znear + " " + zfar); + applyMatrix((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); + } + + + // based on mesa, glu.c + public void lookat(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; + } + + applyMatrix(x0, x1, x2, 0, + y0, y1, y2, 0, + z0, z1, z2, 0, + 0, 0, 0, 1); + translate(-eyeX, -eyeY, -eyeZ); + } + + + ////////////////////////////////////////////////////////////// + + + public void translate(float tx, float ty) { + if (dimensions == 3) { + translate(tx, ty, 0); + + } else { + if (dimensions == 0) dimensions = 2; // otherwise already 2 or higher + + m03 += tx*m00 + ty*m01 + m02; + m13 += tx*m10 + ty*m11 + m12; + m23 += tx*m20 + ty*m21 + m22; + m33 += tx*m30 + ty*m31 + m32; + } + } + + + public void translate(float tx, float ty, float tz) { + dimensions = 3; + + 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; + } + + + // 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) { + //rotate(angle, 1, 0, 0); + dimensions = 3; + float c = cos(angle); + float s = sin(angle); + applyMatrix(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1); + } + + + public void rotateY(float angle) { + //rotate(angle, 0, 1, 0); + dimensions = 3; + float c = cos(angle); + float s = sin(angle); + applyMatrix(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1); + } + + + // two dimensional rotation is the same as rotating along the z-axis + public void rotate(float angle) { + rotateZ(angle); + } + + // note that this doesn't make things 3D + public void rotateZ(float angle) { + //rotate(angle, 0, 0, 1); + if (dimensions == 0) dimensions = 2; // otherwise already 2 or higher + float c = cos(angle); + float s = sin(angle); + applyMatrix(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + + + // should be in radians (i think), instead of degrees (gl uses degrees) + // based on 15-463 code, but similar to opengl ref p.443 + + public void rotate(float angle, float v0, float v1, float v2) { + //modelMatrixIsIdentity = false; + dimensions = 3; + + // TODO should make sure this vector is normalized + + float c = cos(angle); + float s = sin(angle); + float t = 1.0f - c; + + applyMatrix((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) { + if (dimensions == 3) { + applyMatrix(s, 0, 0, 0, 0, s, 0, 0, 0, 0, s, 0, 0, 0, 0, 1); + + } else { + dimensions = 2; + applyMatrix(s, 0, 0, 0, 0, s, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + + // figure out whether 2D or 3D matrix + //scale(xyz, xyz, xyz); + } + + + public void scale(float sx, float sy) { + if (dimensions == 0) dimensions = 2; + applyMatrix(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + + + // OPTIMIZE: same as above + public void scale(float x, float y, float z) { + //modelMatrixIsIdentity = false; + dimensions = 3; + applyMatrix(x, 0, 0, 0, 0, y, 0, 0, 0, 0, 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) { + dimensions = 3; + applyMatrix(n00, n01, n02, n03, n10, n11, n12, n13, + n20, n21, n22, n23, n30, n31, n32, n33); + } + + + ////////////////////////////////////////////////////////////// + + // COLOR + + + public void colorMode(int icolorMode) { + color_mode = icolorMode; + } + + + public void colorMode(int icolorMode, float max) { + colorMode(icolorMode, max, max, max, max); + } + + + // note that this doesn't set the alpha color max.. + // so colorMode(RGB, 255, 255, 255) would retain the previous max alpha + // could be a problem when colorMode(HSB, 360, 100, 100); + + public void colorMode(int icolorMode, + float maxX, float maxY, float maxZ) { + colorMode(icolorMode, maxX, maxY, maxZ, colorMaxA); //maxX); //ONE); + } + + + public void colorMode(int icolorMode, + float maxX, float maxY, float maxZ, float maxA) { + color_mode = icolorMode; + + colorMaxX = maxX; // still needs to be set for hsb + colorMaxY = maxY; + colorMaxZ = maxZ; + colorMaxA = maxA; + + // if color max values are all 1, then no need to scale + color_scale = ((maxA != ONE) || (maxX != maxY) || + (maxY != maxZ) || (maxZ != maxA)); + } + + + ////////////////////////////////////////////////////////////// + + + protected void calc_color(float gray) { + calc_color(gray, colorMaxA); + } + + + protected void calc_color(float gray, float alpha) { + if (gray > colorMaxX) gray = colorMaxX; + if (alpha > colorMaxA) alpha = colorMaxA; + + if (gray < 0) gray = 0; + if (alpha < 0) alpha = 0; + + calcR = color_scale ? (gray / colorMaxX) : gray; + calcG = calcR; + calcB = calcR; + calcA = color_scale ? (alpha / colorMaxA) : alpha; + + calcRi = (int)(calcR*255); calcGi = (int)(calcG*255); + calcBi = (int)(calcB*255); calcAi = (int)(calcA*255); + calci = (calcAi << 24) | (calcRi << 16) | (calcGi << 8) | calcBi; + calc_alpha = (calcAi != 255); + } + + + protected void calc_color(float x, float y, float z) { + calc_color(x, y, z, colorMaxA); + } + + + protected void calc_color(float x, float y, float z, float a) { + if (x > colorMaxX) x = colorMaxX; + if (y > colorMaxY) y = colorMaxY; + if (z > colorMaxZ) z = colorMaxZ; + if (a > colorMaxA) a = colorMaxA; + + if (x < 0) x = 0; + if (y < 0) y = 0; + if (z < 0) z = 0; + if (a < 0) a = 0; + + switch (color_mode) { + case RGB: + if (color_scale) { + calcR = x / colorMaxX; + calcG = y / colorMaxY; + calcB = z / colorMaxZ; + calcA = a / colorMaxA; + } else { + calcR = x; calcG = y; calcB = z; calcA = a; + } + break; + + case HSB: + x /= colorMaxX; // h + y /= colorMaxY; // s + z /= colorMaxZ; // b + + calcA = color_scale ? (a/colorMaxA) : 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); + calci = (calcAi << 24) | (calcRi << 16) | (calcGi << 8) | calcBi; + calc_alpha = (calcAi != 255); + } + + + /** + * unpacks AARRGGBB color for direct use with calc_color. + * handled here with its own function since this is independent + * 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 bounds check since it's a 32 bit number) + */ + protected void unpack_for_calc(int rgb) { + calci = rgb; + calcAi = (rgb >> 24) & 0xff; + calcRi = (rgb >> 16) & 0xff; + calcGi = (rgb >> 8) & 0xff; + calcBi = rgb & 0xff; + calcA = (float)calcAi / 255.0f; + calcR = (float)calcRi / 255.0f; + calcG = (float)calcGi / 255.0f; + calcB = (float)calcBi / 255.0f; + calc_alpha = (calcAi != 255); + + /* + calci = rgb; + calcRi = (rgb >> 16) & 0xff; + calcGi = (rgb >> 8) & 0xff; + calcBi = rgb & 0xff; + calcAi = 255; + calcR = (float)calcRi / 255.0f; + calcG = (float)calcGi / 255.0f; + calcB = (float)calcBi / 255.0f; + calcA = 1; + calc_alpha = false; + */ + } + + + protected void calc_tint() { + _tint = true; + tintR = calcR; + tintG = calcG; + tintB = calcB; + tintA = calcA; + tintRi = calcRi; + tintGi = calcGi; + tintBi = calcBi; + tintAi = calcAi; + tint = calci; + tint_alpha = calc_alpha; + } + + + protected void calc_fill() { + _fill = true; + fillChanged = true; + fillR = calcR; + fillG = calcG; + fillB = calcB; + fillA = calcA; + fillRi = calcRi; + fillGi = calcGi; + fillBi = calcBi; + fillAi = calcAi; + fill = calci; + fill_alpha = calc_alpha; + } + + + protected void calc_stroke() { + _stroke = true; + strokeChanged = true; + strokeR = calcR; + strokeG = calcG; + strokeB = calcB; + strokeA = calcA; + strokeRi = calcRi; + strokeGi = calcGi; + strokeBi = calcBi; + strokeAi = calcAi; + stroke = calci; + stroke_alpha = calc_alpha; + } + + + protected void calc_background() { + _background = true; + backR = calcR; + backG = calcG; + backB = calcB; + backRi = calcRi; + backGi = calcGi; + backBi = calcBi; + background = calci; + } + + + ////////////////////////////////////////////////////////////// + + + public void noTint() { + _tint = false; + } + + + // if high bit isn't set, then it's not a #ffcc00 style web color + // so redirect to the float version, b/c they want a gray. + // only danger is that someone would try to set the color to a + // zero alpha.. which would be kooky but not unlikely + // (i.e. if it were in a loop) so in addition to checking the high + // bit, check to see if the value is at least just below the + // colorMaxX (i.e. 0..255). can't just check the latter since + // if the high bit is > 0x80 then the int value for rgb will be + // negative. yay for no unsigned types in java! + + public void tint(int rgb) { + if (((rgb & 0xff000000) == 0) && (rgb <= colorMaxX)) { + tint((float) rgb); + + } else { + unpack_for_calc(rgb); + calc_tint(); + } + } + + public void tint(float gray) { + calc_color(gray); + calc_tint(); + } + + + public void tint(float gray, float alpha) { + calc_color(gray, alpha); + calc_tint(); + } + + + public void tint(float x, float y, float z) { + calc_color(x, y, z); + calc_tint(); + } + + + public void tint(float x, float y, float z, float a) { + calc_color(x, y, z, a); + calc_tint(); + } + + + ////////////////////////////////////////////////////////////// + + + public void noFill() { + _fill = false; + } + + + public void fill(int rgb) { + if (((rgb & 0xff000000) == 0) && (rgb <= colorMaxX)) { // see above + fill((float) rgb); + + } else { + unpack_for_calc(rgb); + calc_fill(); + } + } + + public void fill(float gray) { + calc_color(gray); + calc_fill(); + } + + + public void fill(float gray, float alpha) { + calc_color(gray, alpha); + calc_fill(); + } + + + public void fill(float x, float y, float z) { + calc_color(x, y, z); + calc_fill(); + } + + + public void fill(float x, float y, float z, float a) { + calc_color(x, y, z, a); + calc_fill(); + } + + + ////////////////////////////////////////////////////////////// + + + public void strokeWeight(float weight) { + strokeWeight = weight; + } + + + public void strokeJoin(int join) { + strokeJoin = join; + } + + + public void strokeMiter(int miter) { + strokeMiter = miter; + } + + + public void noStroke() { + _stroke = false; + } + + + public void stroke(int rgb) { + if (((rgb & 0xff000000) == 0) && (rgb <= colorMaxX)) { // see above + stroke((float) rgb); + + } else { + unpack_for_calc(rgb); + calc_stroke(); + } + } + + + public void stroke(float gray) { + //System.out.println("stroke " + gray); + calc_color(gray); + calc_stroke(); + } + + + public void stroke(float gray, float alpha) { + calc_color(gray, alpha); + calc_stroke(); + } + + + public void stroke(float x, float y, float z) { + calc_color(x, y, z); + calc_stroke(); + } + + + public void stroke(float x, float y, float z, float a) { + calc_color(x, y, z, a); + calc_stroke(); + } + + + ////////////////////////////////////////////////////////////// + + + public void background(int rgb) { + if (((rgb & 0xff000000) == 0) && (rgb <= colorMaxX)) { // see above + background((float) rgb); + + } else { + unpack_for_calc(rgb); + calc_background(); + } + clear(); + } + + + public void background(float gray) { + calc_color(gray); + calc_background(); + clear(); + } + + + public void background(float x, float y, float z) { + calc_color(x, y, z); + calc_background(); + clear(); + } + + + public void background(BImage image) { + if ((image.width != width) || (image.height != height)) { + System.err.println("background image must be the same size " + + "as your application"); + return; + } + if ((image.format != RGB) && (image.format != RGBA)) { + System.err.println("background images should be RGB or RGBA"); + return; + } + + // blit image to the screen + System.arraycopy(image.pixels, 0, pixels, 0, pixels.length); + + // clear the zbuffer + //if (dimensions == 3) { + for (int i = 0; i < pixelCount; i++) { + zbuffer[i] = MAX_FLOAT; + } + //} + } + + + /** + * Clears pixel and z-buffer. If dimensions not set to 3, + * then the zbuffer doesn't get cleared. Some values might be + * zero, but since it's <= when comparing zbuffer (because of + * 2D drawing) that's no problem. Saves a lotta time to not + * reset the zbuffer if it's not used. + * + * Actually had to punt on that optimization because it was + * producing weird results and it was close to a release deadline. + * A little sleep would probably show why it wasn't working. + */ + public void clear() { + //zbufferTainted = true; + + //if (dimensions == 3) { + //if (zbufferTainted) { + //System.out.println("clearing zbuffer"); + for (int i = 0; i < pixelCount; i++) { + pixels[i] = background; + zbuffer[i] = MAX_FLOAT; + } + //zbufferTainted = false; + //} else { + //System.out.println("not clearing zbuffer"); + //for (int i = 0; i < pixelCount; i++) { + // pixels[i] = background; + //} + //} + } + + + ////////////////////////////////////////////////////////////// + + // LIGHTS + + + public void lights() { + lighting = true; + } + + public void noLights() { + lighting = false; + } + + + ////////////////////////////////////////////////////////////// + + // SMOOTHING (ANTIALIASING) + + + public void smooth() { + smooth = true; + } + + public void noSmooth() { + smooth = false; + } + + + ////////////////////////////////////////////////////////////// + + // HINTS + + // 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. + + public void hint(int which) { + hints[which] = true; + } + + public void unhint(int which) { + hints[which] = false; + } + + + ////////////////////////////////////////////////////////////// + + // MESSAGES / ERRORS / LOGGING + + + public void message(int level, String message) { + switch (level) { + case CHATTER: + //System.err.println("bagel chatter: " + message); + break; + case COMPLAINT: + System.err.println("bagel complaint: " + message); + break; + case PROBLEM: + System.err.println("bagel problem: " + message); + break; + } + } + + public void message(int level, String message, Exception e) { + message(level, message); + e.printStackTrace(); + } + + + ////////////////////////////////////////////////////////////// + + // FILE I/O + + + public InputStream openStream(String filename) throws IOException { + InputStream stream = null; + boolean gz = filename.toLowerCase().endsWith(".gz"); + + if (filename.startsWith("http://")) { + try { + URL url = new URL(filename); + stream = url.openStream(); + return gz ? new GZIPInputStream(stream) : stream; + + } catch (MalformedURLException e) { + e.printStackTrace(); + return null; + } + } + + try { + stream = getClass().getResourceAsStream(filename); + if (stream != null) return gz ? new GZIPInputStream(stream) : stream; + } catch (IOException e) { } + + try { + stream = getClass().getResourceAsStream("data/" + filename); + if (stream != null) return gz ? new GZIPInputStream(stream) : stream; + } catch (IOException e) { } + + try { + try { + stream = new FileInputStream(new File("data", filename)); + if (stream != null) return gz ? new GZIPInputStream(stream) : stream; + } catch (IOException e2) { } + + try { + stream = new FileInputStream(filename); + if (stream != null) return gz ? new GZIPInputStream(stream) : stream; + } catch (IOException e1) { } + + } catch (SecurityException se) { } // online, whups + + if (stream == null) { + throw new IOException("openStream() could not open " + filename); + } + return null; // #$(*@ compiler + } + + + public byte[] loadBytes(String filename) { + try { + return loadBytes(openStream(filename)); + + } catch (IOException e) { + System.err.println("problem loading bytes from " + filename); + e.printStackTrace(); + } + 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(); + } + return null; + } + + + public String[] loadStrings(String filename) { + try { + return loadStrings(openStream(filename)); + + } catch (IOException e) { + System.err.println("problem loading strings from " + filename); + e.printStackTrace(); + } + 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 appropraite amount for these lines + String output[] = new String[lineCount]; + System.arraycopy(lines, 0, output, 0, lineCount); + return output; + + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + + public void saveBytes(String filename, byte buffer[]) { + try { + FileOutputStream fos = new FileOutputStream(filename); + saveBytes(fos, buffer); + fos.close(); + + } catch (IOException e) { + System.err.println("error saving bytes to " + filename); + e.printStackTrace(); + } + } + + public void saveBytes(OutputStream output, byte buffer[]) { + try { + //BufferedOutputStream bos = new BufferedOutputStream(output); + output.write(buffer); + output.flush(); + + } catch (IOException e) { + System.err.println("error while saving bytes"); + e.printStackTrace(); + } + } + + + public void saveStrings(String filename, String strings[]) { + try { + FileOutputStream fos = new FileOutputStream(filename); + saveStrings(fos, strings); + fos.close(); + + } catch (IOException e) { + System.err.println("error while saving strings"); + e.printStackTrace(); + } + } + + public void saveStrings(OutputStream output, String strings[]) { + //try { + PrintWriter writer = + new PrintWriter(new OutputStreamWriter(output)); + for (int i = 0; i < strings.length; i++) { + writer.println(strings[i]); + } + writer.flush(); + //} catch (IOException e) { + //System.err.println("error while saving strings"); + //e.printStackTrace(); + //} + } + + + ////////////////////////////////////////////////////////////// + + // SORT + + int sort_mode; + + static final int STRINGS = 0; + static final int INTS = 1; + static final int FLOATS = 2; + static final int DOUBLES = 3; + + String sort_strings[]; + int sort_ints[]; + float sort_floats[]; + double sort_doubles[]; + Object sort_objects[]; + + + /** + * Sort an array of String objects. + */ + public void sort(String what[]) { + sort(what, what.length, null); + } + + /** + * Sort an array of String objects, along with a generic + * array of type Object. + * + * String names[] = { "orange", "black", "red" }; + * Object colors[] = { Color.orange, Color.black, Color.red }; + * sort(names, colors); + * + * result is 'names' alphabetically sorted + * and the colors[] array sorted along with it. + */ + public void sort(String what[], Object objects[]) { + sort(what, what.length, objects); + } + + public void sort(int what[]) { + sort(what, what.length, null); + } + + public void sort(int what[], Object objects[]) { + sort(what, what.length, objects); + } + + public void sort(float what[]) { + sort(what, what.length, null); + } + + public void sort(float what[], Object objects[]) { + sort(what, what.length, objects); + } + + public void sort(double what[]) { + sort(what, what.length, null); + } + + public void sort(double what[], Object objects[]) { + sort(what, what.length, objects); + } + + + public void sort(String what[], int count, Object objects[]) { + if (count == 0) return; + sort_mode = STRINGS; + sort_strings = what; + sort_objects = objects; + sort_internal(0, count-1); + } + + public void sort(int what[], int count, Object objects[]) { + if (count == 0) return; + sort_mode = INTS; + sort_ints = what; + sort_objects = objects; + sort_internal(0, count-1); + } + + public void sort(float what[], int count, Object objects[]) { + if (count == 0) return; + sort_mode = FLOATS; + sort_floats = what; + sort_objects = objects; + sort_internal(0, count-1); + } + + public void sort(double what[], int count, Object objects[]) { + if (count == 0) return; + sort_mode = DOUBLES; + sort_doubles = what; + sort_objects = objects; + sort_internal(0, count-1); + } + + + 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 STRINGS: + String stemp = sort_strings[a]; + sort_strings[a] = sort_strings[b]; + sort_strings[b] = stemp; + 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 DOUBLES: + double dtemp = sort_doubles[a]; + sort_doubles[a] = sort_doubles[b]; + sort_doubles[b] = dtemp; + break; + } + if (sort_objects != null) { + Object otemp = sort_objects[a]; + sort_objects[a] = sort_objects[b]; + sort_objects[b] = otemp; + } + } + + protected int sort_compare(int a, int b) { + switch (sort_mode) { + case STRINGS: + return sort_strings[a].compareTo(sort_strings[b]); + case INTS: + if (sort_ints[a] < sort_ints[b]) return -1; + return (sort_ints[a] == sort_ints[b]) ? 0 : 1; + case FLOATS: + if (sort_floats[a] < sort_floats[b]) return -1; + return (sort_floats[a] == sort_floats[b]) ? 0 : 1; + case DOUBLES: + if (sort_doubles[a] < sort_doubles[b]) return -1; + return (sort_doubles[a] == sort_doubles[b]) ? 0 : 1; + } + return 0; + } + + + ////////////////////////////////////////////////////////////// + + // PIXELS + + // 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 x, int y, int z) { + if ((color_mode == RGB) && (!color_scale)) { + return 0xff000000 | (x << 16) | (y << 8) | z; + } + calc_color(x, y, z); + return calci; + } + + public final int color(float x, float y, float z) { + calc_color(x, y, z); + return calci; + } + + + public final int color(int x, int y, int z, int a) { + if ((color_mode == RGB) && (!color_scale)) { + return (a << 24) | (x << 16) | (y << 8) | z; + } + calc_color(x, y, z, a); + return calci; + } + + public final int color(float x, float y, float z, float a) { + calc_color(x, y, z, a); + return calci; + } + + + public final float alpha(int what) { + float c = (what >> 24) & 0xff; + if (colorMaxA == 255) return c; + return (c / 255.0f) * colorMaxA; + } + + public final float red(int what) { + float c = (what >> 16) & 0xff; + if ((color_mode == RGB) && (!color_scale)) return c; + return (c / 255.0f) * colorMaxX; + } + + public final float green(int what) { + float c = (what >> 8) & 0xff; + if ((color_mode == RGB) && (!color_scale)) return c; + return (c / 255.0f) * colorMaxY; + } + + public final float blue(int what) { + float c = (what) & 0xff; + if ((color_mode == RGB) && (!color_scale)) return c; + return (c / 255.0f) * colorMaxZ; + } + + + 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] * colorMaxX; + } + + 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] * colorMaxY; + } + + 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] * colorMaxZ; + } + + + // should only be used by other parts of the bagel library + + + static private final int float_color(float r, float g, float b) { + return (0xff000000 | + ((int) (255.0f * r)) << 16 | + ((int) (255.0f * g)) << 8 | + ((int) (255.0f * b))); + } + + + public final static int _blend(int p1, int p2, int a2) { + // scale alpha by alpha of incoming pixel + a2 = (a2 * (p2 >>> 24)) >> 8; + + int a1 = a2 ^ 0xff; + int r = (a1 * ((p1 >> 16) & 0xff) + a2 * ((p2 >> 16) & 0xff)) & 0xff00; + int g = (a1 * ((p1 >> 8) & 0xff) + a2 * ((p2 >> 8) & 0xff)) & 0xff00; + int b = (a1 * ( p1 & 0xff) + a2 * ( p2 & 0xff)) >> 8; + + return 0xff000000 | (r << 8) | g | b; + } + + + ////////////////////////////////////////////////////////////// + + // MATH + + // these are *only* the functions used internally + // the real math functions are inside BApplet + + // these have been made private so as not to conflict + // with the versions found in BApplet when fxn importing happens + // also might be faster that way. hmm. + + + 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 max(float a, float b) { + return (a > b) ? a : b; + } + + private final float sq(float a) { + return a*a; + } + + private final float sqrt(float a) { + return (float)Math.sqrt(a); + } + + + 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); + } +} + diff --git a/core/PImage.java b/core/PImage.java new file mode 100644 index 000000000..2b0748034 --- /dev/null +++ b/core/PImage.java @@ -0,0 +1,1000 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + BImage - storage class for pixel data + Part of the Processing project - http://Proce55ing.net + + Copyright (c) 2001-03 + Ben Fry, Massachusetts Institute of Technology and + Casey Reas, Interaction Design Institute Ivrea + + 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 +*/ + +import java.awt.*; +import java.awt.image.*; +import java.io.*; + + +/** + * [toxi 030722] + * advanced copying/blitting code + * + * [fry 030918] + * integrated and modified to fit p5 spec + * + * [toxi 030930] + * - target pixel buffer doesn't loose alpha channel anymore + * with every blitting operation alpha values are increased now + * - resizing by large factors (>250%) doesn't yield any rounding errors + * anymore, changed to 16bit precision (=65536% max or 0.000015% min) + * - replicate() is now only using REPLACE mode to avoid semantic problems + * - added blend() methods to use replicate()'s functionality, + * but with blend modes + * + * [toxi 031006] + * blit_resize() is now clipping input coordinates to avoid array + * exceptions target dimension can be larger than destination image + * object, outside pixels will be skipped + * + * [toxi 031017] + * versions of replicate() and blend() methods which use cross-image + * blitting are now called in the destination image and expect a source + * image object as parameter. this is to provide an easy syntax for cases + * where the main pixel buffer is the destination. as those methods are + * overloaded in BApplet, users can call those functions directly without + * explicitly giving a reference to BGraphics. + */ +public class BImage implements BConstants, Cloneable { + + // note that RGB images still require 0xff in the high byte + // because of how they'll be manipulated by other functions + int format; + + int pixels[]; + int width, height; + // maybe also scan line, etc? + + // note! inherited by BGraphics + boolean smooth = false; //true; // for now.. how to fix? + + // for gl subclass / hardware accel + int cacheIndex; + + // blend mode used for copy et al. + //int blend_mode = REPLACE; + + + // 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; + + + /** + * Constructor required by java compiler for subclasses. + */ + public BImage() { } + + + /** + * Create a new (transparent) image of a specific size. + * All pixels are set to zero, meaning black, but since the + * alpha is zero, it will be transparent. + */ + public BImage(int width, int height) { + this(new int[width * height], width, height, RGBA); + // toxi: is it maybe better to init the image with max alpha enabled? + //for(int i=0; i>16&0xff) + 151*(col>>8&0xff) + 28*(col&0xff) )>>8; + pixels[i] = (col & ALPHA_MASK) | lum<<16 | lum<<8 | lum; + } + } + + ////////////////////////////////////////////////////////////// + + // GETTING PIXELS + + + public int get(int x, int y) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return 0; + return pixels[y*width + x]; + } + + + public BImage get(int x, int y, int w, int h) { + if (x < 0) x = 0; + if (y < 0) y = 0; + + if (x + w > width) w = width - x; + if (y + h > height) h = height - y; + + BImage newbie = new BImage(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; + } + + + + ////////////////////////////////////////////////////////////// + + // SETTING PIXELS + + + public void set(int x, int y, int c) { + if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) return; + pixels[y*width + x] = c; + } + + + // not all variables here are needed.. fix it up later + + public void set(int x, int y, BImage image) { + // source + int sx = 0; + int sy = 0; + int sw = image.width; + int sh = image.height; + + // target + int tx = x; // < 0 ? 0 : x; + int ty = y; // < 0 ? 0 : y; + int tw = image.width; + int th = image.height; + + if (tx < 0) { // say if target x were -3 + sx -= tx; // source x -(-3) (or add 3) + sw += tx; // source width -3 + tw += tx; // target width -3 + tx = 0; // target x is zero (upper corner) + } + if (ty < 0) { + sy -= ty; + sh += ty; + th += ty; + ty = 0; + } + if (tx + tw > width) { + int extra = (tx + tw) - width; + sw -= extra; + tw -= extra; + } + if (ty + th > height) { + int extra = (ty + th) - height; + sh -= extra; + sw -= extra; + } + + for (int row = sy; row < sy + sh; row++) { + System.arraycopy(image.pixels, row*image.width + sx, + pixels, (y+row)*width + tx, sw); + } + } + + + + ////////////////////////////////////////////////////////////// + + // REPLICATING & BLENDING (AREAS) OF PIXELS + + + // copies a pixel from place to another (in the same image) + // this function is excluded from using any blend modes + // it always replaces/overwrites the target pixel value + + // should this copy the zbuffer and stencil for this pixel? + + public void copy(int sx, int sy, int dx, int dy) { + if ((sx >= 0) && (sx < width) && (dx >= 0) && (dx < width) && + (sy >= 0) && (sy < height) && (dy >= 0) && (dy < height)) { + pixels[dy * width + dx] = pixels[sy * width + sx]; + } + } + + + // copies a pixel from place to another (in different images) + // this function is excluded from using any blend modes + // it always replaces/overwrites the target pixel value + + public void copy(BImage src, int sx, int sy, int dx, int dy) { + if ((dx >= 0) && (dx < width) && (sx >= 0) && (sx < src.width) && + (dy >= 0) && (dy < height) && (sy >= 0) && (sy < src.height)) { + pixels[dy * width + dx] = src.pixels[sy * src.width + sx]; + } + } + + + /** + * Copy things from one area of this image to another area in the same image + */ + public void copy(int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2) { + if (intersect(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)) { + blit_resize(this.get(sx1, sy1, sx2 - sx1, sy2 - sy1), + 0, 0, sx2 - sx1 - 1, sy2 - sy1 - 1, + pixels, width, height, dx1, dy1, dx2, dy2, REPLACE); + } else { + blit_resize(this, sx1, sy1, sx2, sy2, + pixels, width, height, dx1, dy1, dx2, dy2, REPLACE); + } + } + + + /** + * Copies area of one image into another BImage object + */ + public void copy(BImage src, int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2) { + blit_resize(src, sx1, sy1, sx2, sy2, + pixels, width, height, dx1, dy1, dx2, dy2, REPLACE); + } + + + /** + * Copies and blends 1 pixel with MODE to pixel in another image + */ + public void blend(BImage 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] = + blendColor(pixels[dy * width + dx], src.pixels[sy * src.width + sx], mode); + } + } + + 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] = + blendColor(pixels[dy * width + dx], pixels[sy * width + sx], mode); + } + } + + /** + * Copy things from 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) { + if (intersect(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2)) { + blit_resize(this.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(this, sx1, sy1, sx2, sy2, + pixels, width, height, dx1, dy1, dx2, dy2, mode); + } + } + + + /** + * Copies area of one image into another BImage object + */ + public void blend(BImage src, int sx1, int sy1, int sx2, int sy2, + int dx1, int dy1, int dx2, int dy2, int mode) { + blit_resize(src, sx1, sy1, sx2, sy2, + pixels, width, height, dx1, dy1, dx2, dy2, mode); + } + + + + ////////////////////////////////////////////////////////////// + + // COPYING IMAGE DATA + + public Object clone() throws CloneNotSupportedException { + BImage c = (BImage) 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; + } + + + /** + * Duplicate an image, returns new object + */ + /* + public BImage duplicate() { + BImage c = new BImage(new int[pixels.length], width, height, format); + System.arraycopy(pixels, 0, c.pixels, 0, pixels.length); + return c; + } + */ + + /** + * Duplicate and resize image + */ + /* + public BImage duplicate(int newWidth, int newHeight) { + BImage dupe = new BImage(new int[newWidth * newHeight], + newWidth, newHeight, format); + + dupe.copy(this, 0, 0, width - 1, height - 1, + 0, 0, newWidth - 1, newHeight - 1); + + return dupe; + } + */ + + /** + * Check to see if two rectangles intersect one another + */ + 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); + } + + + + ////////////////////////////////////////////////////////////// + + // 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(BImage 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.width) 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; + } + + + private 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 + }; + + static void write_tiff(OutputStream output, int pixels[], + int width, int height) throws IOException { + + 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); + + 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(); + } + + + /** + * [toxi 030902] + * Creates a Targa32 formatted byte sequence of specified pixel buffer + * + * [fry 030917] + * Modified to write directly to OutputStream, because of + * memory issues with first making an array of the data. + * tga spec: http://organicbit.com/closecombat/formats/tga.html + */ + static void write_targa(OutputStream output, int pixels[], + int width, int height) throws IOException { + + byte header[] = new byte[18]; + + // set header info + header[2] = 0x02; + header[12] = (byte) (width & 0xff); + header[13] = (byte) (width >> 8); + header[14] = (byte) (height & 0xff); + header[15] = (byte) (height >> 8); + header[16] = 32; // bits per pixel + header[17] = 8; // bits per colour component + + output.write(header); + + int index = (height-1) * width; + + for (int y = height-1; y >= 0; y--) { + for (int x = 0; x < width; x++) { + int col = pixels[index + x]; + output.write(col & 0xff); + output.write(col >> 8 & 0xff); + output.write(col >> 16 & 0xff); + output.write(col >>> 24 & 0xff); + } + index -= width; + } + output.flush(); + } + + + public void save(String filename) { + try { + OutputStream os = null; + + if (filename.toLowerCase().endsWith(".tga")) { + os = new BufferedOutputStream(new FileOutputStream(filename), 32768); + write_targa(os, pixels, width, height); + + } 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); + write_tiff(os, pixels, width, height); + } + os.flush(); + os.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + + /* + // why is this code here? me confused [fry] + public void save(OutputStream output, int type) { + try { + if (type == TARGA) { + write_targa(output, pixels, width, height); + + } else if (type == TIFF) { + write_tiff(output, pixels, width, height); + + } else { + System.err.println("can't save image as that type"); + } + } catch (IOException e) { + System.err.println("error while trying to save image"); + e.printStackTrace(); + } + } + */ +} + diff --git a/core/PLibrary.java b/core/PLibrary.java new file mode 100644 index 000000000..6025bb4f1 --- /dev/null +++ b/core/PLibrary.java @@ -0,0 +1,44 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + BLibrary - interface for classes that plug into bagel + Part of the Processing project - http://Proce55ing.net + + Copyright (c) 2001-03 + Ben Fry, Massachusetts Institute of Technology and + Casey Reas, Interaction Design Institute Ivrea + + 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 +*/ + + +public interface BLibrary { + + // called when the applet is stopped + public void stop(); +} + + + +/* +public void libraryEvent(BLibrary who, Object data) { + //if (who instanceof BVideo) { + if (who.signature() == Sonia.SIGNATURE) { + BImage frame = (BImage)data; + // do something with the data + } +} +*/ diff --git a/core/PLine.java b/core/PLine.java new file mode 100644 index 000000000..27097503a --- /dev/null +++ b/core/PLine.java @@ -0,0 +1,1426 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + BFont - font object for text rendering + Part of the Processing project - http://Proce55ing.net + + Copyright (c) 2001-03 + Ben Fry, Massachusetts Institute of Technology and + Casey Reas, Interaction Design Institute Ivrea + + 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 + */ + + +public class BLine implements BConstants +{ + + 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 r0; + private float g0; + private float b0; + private float a0; + private float z0; + + // deltas + private float dz; + + // rgba deltas + private float dr; + private float dg; + private float db; + private float da; + + //points + //int x[] = new int[2]; + //int y[] = new int[2]; + + private BGraphics parent; + + + public BLine(BGraphics 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; + + 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 BGraphics 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 lenght; + boolean visible = true; + + if (parent.smooth) { + SMOOTH = true; + m_drawFlags |= R_SMOOTH; + + } else { + + SMOOTH = false; + m_drawFlags &= ~R_SMOOTH; + } + + /////////////////////////////////////// + // 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 [fry] + // some kind of bug exists with the line-stepping algorithm + // that causes strange patterns in the anti-aliasing + if (x_array[1] < x_array[0]) { + float 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; + } + + // important - dont 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]; + + lenght = -longLen; + + } else { + + o0 = 0; + o1 = 1; + + xi = (int) x_array[0]; + yi = (int) y_array[0]; + + lenght = longLen; + } + + // calculate dt + if (lenght== 0) { + dt = 0; + } else { + dt = (shortLen << 16) / longLen; + } + + r0 = r_array[o0]; + g0 = g_array[o0]; + b0 = b_array[o0]; + + if (INTERPOLATE_RGB) { + dr = (r_array[o1] - r_array[o0]) / lenght; + dg = (g_array[o1] - g_array[o0]) / lenght; + db = (b_array[o1] - b_array[o0]) / lenght; + } else { + dr = 0; + dg = 0; + db = 0; + } + + a0 = a_array[o0]; + + if (INTERPOLATE_ALPHA) { + da = (a_array[o1] - a_array[o0]) / lenght; + } else { + da = 0; + } + + z0 = z_array[o0]; + z0 += -0.001f; // [rocha] ugly fix for z buffer precision + + if(INTERPOLATE_Z) { + dz = (z_array[o1] - z_array[o0]) / lenght; + } else { + dz = 0; + } + + // draw thin points + if (lenght == 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) { + + //drawPoint_alpha(x1, y1); + drawLine_smooth(xi, yi, dt, lenght, yLonger); + + } else { + + if (m_drawFlags == 0) { + + drawLine_plain(xi, yi, dt, lenght, yLonger); + + } else if (m_drawFlags == R_ALPHA) { + + drawLine_plain_alpha(xi, yi, dt, lenght, yLonger); + + } else if (m_drawFlags == R_COLOR) { + + drawLine_color(xi, yi, dt, lenght, yLonger); + + } else if (m_drawFlags == (R_COLOR + R_ALPHA)) { + + drawLine_color_alpha(xi, yi, dt, lenght, yLonger); + + } else if (m_drawFlags == R_SPATIAL) { + + drawLine_plain_spatial(xi, yi, dt, lenght, yLonger); + + } else if (m_drawFlags == (R_SPATIAL + R_ALPHA)) { + + drawLine_plain_alpha_spatial(xi, yi, dt, lenght, yLonger); + + } else if (m_drawFlags == (R_SPATIAL + R_COLOR)) { + + drawLine_color_spatial(xi, yi, dt, lenght, yLonger); + + } else if (m_drawFlags == (R_SPATIAL + R_COLOR + R_ALPHA)) { + + drawLine_color_alpha_spatial(xi, yi, dt, lenght, 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 = 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 = 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] = z0; + j+=dt; + } + return; + + } 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] = z0; + j+=dt; + } + return; + } + } + + 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) (a0); + + if (vertical) { + + // 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] = z0; + + ia+= da; + j+=dt; + } + return; + + } 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] = z0; + + ia+= da; + j+=dt; + } + return; + } + } + + private void drawLine_color(int x0, int y0, int dt, + int length, boolean vertical) { + + int offset = 0; + + int ir = (int) r0; + int ig = (int) g0; + int ib = (int) b0; + + if (vertical) { + + // 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] = z0; + ir+= dr; + ig+= dg; + ib+= db; + j+=dt; + } + return; + + } 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] = z0; + ir+= dr; + ig+= dg; + ib+= db; + j+=dt; + } + return; + } + } + + + private void drawLine_color_alpha(int x0, int y0, int dt, + int length, boolean vertical) { + + int offset = 0; + + int ir = (int) r0; + int ig = (int) g0; + int ib = (int) b0; + int ia = (int) (a0); + + if (vertical) { + + // 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] = z0; + + ir+= dr; + ig+= dg; + ib+= db; + ia+= da; + j+=dt; + } + return; + + } 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] = z0; + + ir+= dr; + ig+= dg; + ib+= db; + ia+= da; + j+=dt; + } + return; + } + } + + + private void drawLine_plain_spatial(int x0, int y0, int dt, + int length, boolean vertical) { + + int offset = 0; + float iz = z0; + + if (vertical) { + + // 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; + } + return; + + } 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; + } + return; + } + } + + private void drawLine_plain_alpha_spatial(int x0, int y0, int dt, + int length, boolean vertical) { + + int offset = 0; + + float iz = z0; + + int pr = m_stroke & 0xFF0000; + int pg = m_stroke & 0xFF00; + int pb = m_stroke & 0xFF; + + int ia = (int) (a0); + + if (vertical) { + + // 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; + } + return; + + } 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; + } + return; + } + } + + private void drawLine_color_spatial(int x0, int y0, int dt, + int length, boolean vertical) { + + int offset = 0; + + float iz = z0; + + int ir = (int) r0; + int ig = (int) g0; + int ib = (int) b0; + + if (vertical) { + + // 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; + } + return; + + } 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 = z0; + + int ir = (int) r0; + int ig = (int) g0; + int ib = (int) b0; + int ia = (int) (a0); + + if (vertical) { + + // 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; + } + return; + + } 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; + } + return; + } + } + + + 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 = z0; + + int ir = (int) r0; + int ig = (int) g0; + int ib = (int) b0; + int ia = (int) (a0); + + if (vertical) { + + // 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, used for anti-aliasing polygon edges + * by [sami] + */ + 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/PPolygon.java b/core/PPolygon.java new file mode 100644 index 000000000..0583906b5 --- /dev/null +++ b/core/PPolygon.java @@ -0,0 +1,712 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + BPolygon - zbuffer polygon rendering object for BGraphics + Part of the Processing project - http://Proce55ing.net + + Copyright (c) 2001-03 + Ben Fry, Massachusetts Institute of Technology and + Casey Reas, Interaction Design Institute Ivrea + + 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 +*/ + + +public class BPolygon implements BConstants { + 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 interpRGBA; + + int rgba; + int r2, g2, b2, a2, a2orig; + + float zbuffer[]; + boolean noDepthTest; + + BGraphics parent; + int pixels[]; + + // the parent's width/height, + // or if smoothing is enabled, parent's w/h scaled + // up by the smoothing dimension + int width, height; + int width1, height1; + + BImage 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 smoothing; + 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 BPolygon(BGraphics iparent) { + parent = iparent; + reset(0); + } + + + public void reset(int count) { + vertexCount = count; + interpX = true; + interpZ = true; + interpUV = false; + interpRGBA = 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++]; + } + + + public void texture(BImage 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.depthTest; + smoothing = parent.smooth; + + // by default, text turns on smoothing 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 = smoothing ? parent.width*SUBXRES : parent.width; + height = smoothing ? parent.height*SUBYRES : parent.height; + + width1 = width - 1; + height1 = height - 1; + + if (!interpRGBA) { + 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; + } + + if (smoothing) { + 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 (smoothing) { + //System.out.println("y/lasty/lastmody = " + y + " " + lastY + " " + lastModY); + //} + } + + + public void unexpand() { + if (smoothing) { + 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 smoothing + 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 (smoothing) { + 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 smoothing factor + int offset = smoothing ? parent.width * (y / SUBYRES) : parent.width*y; + + int truelx = 0, truerx = 0; + if (smoothing) { + 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])) { + + // 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 (smoothing || 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) * + (interpRGBA ? ((int) (sp[A]*255)) : a2orig)) >> 8; + + } else if (tformat == RGBA) { + 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) * + (interpRGBA ? ((int) (sp[A]*255)) : a2orig)) >> 8; + + } else { // RGB image, no alpha + ta = interpRGBA ? ((int) (sp[A]*255)) : a2orig; + } + + if ((tformat == RGB) || (tformat == RGBA)) { + 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) * + (interpRGBA ? ((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) * + (interpRGBA ? ((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) * + (interpRGBA ? ((int) sp[B]*255) : b2)) >> 8; + + } else { // alpha image, only use current fill color + if (interpRGBA) { + 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 smoothing + // checks smoothing again here because of + // hints[SMOOTH_IMAGES] used up above + int weight = smoothing ? coverage(x) : 255; + if (weight != 255) ta = ta*weight >> 8; + + } else { // no smoothing, 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 (interpRGBA) { + 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 RGBA + ta = (tformat == RGB) ? 255 : (tpixel >> 24) & 0xff; + + if (interpRGBA) { + 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 = smoothing ? coverage(x) : 255; + + if (interpRGBA) { + 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 smoothing enabled, don't increment values + // for the pixel in the stretch out version + // of the scanline used to get smooth edges. + if (!smoothing || ((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 (interpRGBA) { + 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 (smoothing) { + //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 (smoothing) { + 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 (interpRGBA) { + 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 (smoothing) { + //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 (interpRGBA) { + 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/PTriangle.java b/core/PTriangle.java new file mode 100644 index 000000000..99af5b6e4 --- /dev/null +++ b/core/PTriangle.java @@ -0,0 +1,2537 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + BFont - font object for text rendering + Part of the Processing project - http://Proce55ing.net + + Copyright (c) 2001-03 + Ben Fry, Massachusetts Institute of Technology and + Casey Reas, Interaction Design Institute Ivrea + + 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 + */ + +// written by sami www.sumea.com + +public class BTriangle implements BConstants +{ + 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_stencil; + private int[] m_texture; + private float[] m_zbuffer; + + // texture image + private BImage m_tImage; + + // + 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 BGraphics parent; + + /** */ + private boolean m_culling; + + /** */ + private boolean m_singleRight; + + /** */ + private boolean m_bilinear; + + public BTriangle(BGraphics 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 BGraphics 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_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(BImage 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 == RGBA) { + 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 BGraphics [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 (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 (iz <= m_zbuffer[xstart]) { + //m_zbuffer[xstart] = iz; + + int alpha = ia >> 16; + int r0 = m_pixels[xstart]; + 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[xstart] = (r0 & 0xFF0000) | (g0 & 0xFF00) | (b0 & 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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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]) { + //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 (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]) { + //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/core/done.txt b/core/done.txt new file mode 100644 index 000000000..cd7c653e4 --- /dev/null +++ b/core/done.txt @@ -0,0 +1,66 @@ +0069 bagel +X text(x, y, z) +X fixed camera modes / replaced how isometric is handled +X whoa.. BGraphics.screenX() et al had the camera stuff shut off +X and that's what shipped in 67. shite. +X need to get things fixed up properly so camera is properly set +X ISOMETRIC was completely broken.. need to implement properly +X also, the default camera is perspective +X cameraMode(PERSPECTIVE) and cameraMode(ORTHOGRAPHIC) setup basic cameras +X if the user wants a custom camera, call cameraMode(CUSTOM); before begin() +X printMatrix() and printCamera() to output the matrices for each +X more functions made static (loadStrings, loadBytes) that could be +X print() commands in BApplet were among these +X die() command to exit an application or just stall out an applet +X die(String error) and die(String error, Exception e) +X more documentation in comments for functions +X chop() function that properly also handles nbsp +X join() was handled weird +X run nf() and nfs() on arrays +X nfp() to show plus sign +X toInt, toFloat, toDouble (nf is for toString.. inconsistent) +o split() function splits strings instead of splitStrings() +o ints() converts an array of strings/doubles/floats to an int array +o split() should also use StringTokenizer +o to do countTokens() and then stuff into an array +o shave() or chomp() or trim() method to remove whitespace on either side +o including unicode nbsp +X min() and max() with three values were broken (now fixed) +X http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1076804172 +X CONTROL wasn't properly set in BConstants +X http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1077058788 +X macosx.. flickering several times on startup +X fixed this, along with other sluggishness related threading issues +X http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1073111031 +X bug with charset table on older fonts +X index_hunt was look through the table, not what was in the font +X http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1077475307;start=0 +X seems to be some difficult threading problems still present +X fixed many threading issues across macosx and linux +X side-porting changes +X new(er) line code is not in the main 'processing' +X making perlin non-static not in the main bagel +X polygon stroking hack code from the end of 68 +X erikb found a bug inside split(), it would die on empty strings +X http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1077664736 +X fixed it, also modified behavior of split() with a delimeter +X previously, it would remove the final delimeter and an empty entry +X but that's now disabled, since the split cmd is very specific +X code from toxi to support .tga files in loadImage +X http://processing.org/discourse/yabb/YaBB.cgi?board=Programs;action=display;num=1078459847;start=0 +X fix bug where single pixel points were ignoring their alpha values +X static loadStrings and loadBytes aren't being added to BApplet +X (the versions that use an inputstream) +X added support for handling static functions in make.pl +X threading is broken again on windows +X windows needs a sleep(1) instead of the yield() +X random(3) should be non-inclusive of the 3 +X http://processing.org/discourse/yabb/YaBB.cgi?board=Syntax;action=display;num=1079935258;start=5 + +api changes +X loadStream -> openStream for better consistency + +jdf +X dynamic loading of code for setting cursor (removed JDK13 ifdef) +X why aren't cursors working on the mac? +X fix from jdf to just set size to 0,0 diff --git a/core/license.txt b/core/license.txt new file mode 100644 index 000000000..16f1ad162 --- /dev/null +++ b/core/license.txt @@ -0,0 +1,456 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[ This is the first released version of the Lesser GPL. + It also counts as the successor of the GNU Library Public + License, version 2, hence the version number 2.1. ] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. \ No newline at end of file diff --git a/core/todo.txt b/core/todo.txt new file mode 100644 index 000000000..3c488fe0a --- /dev/null +++ b/core/todo.txt @@ -0,0 +1,420 @@ +0070 bagel +o check ordering of split() in java vs perl for regexp +X don't include empty chars in font builder +X .vlw font files are enormous with full charset +X check to see if the character exists before adding it to the font +X fixed (unreported) problem with char lookup code +o split() with multiple args (i think this is completed) +X http://processing.org/discourse/yabb/YaBB.cgi?board=Syntax;action=display;num=1078985667 +X trim() not chop().. whups +X random(5, 5) -> return 5, random(6, 4) return error +X http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1083275855;start=0 +X use random.nextFloat() because it's faster + +_ vertex coloring bug with carlos' new line code +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1085348942 + +_ loadStrings has a problem with URLs and a code folder +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1086551792 + +_ mkoser wish list +_ filled polygons working with smooth() +_ z-clipping + +_ dynamically load code for png and others on loadImage/saveFrame? +_ updated png encoder +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Syntax;action=display;num=1083792994 +_ more image file i/o.. more advanced in newer java +_ read uncompressed tiff, read uncompressed tga files. +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_Software;action=display;num=1081190619 +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Tools;action=display;num=1066742994;start=15 + +_ embed all available chars from a font, so japanese, etc works + +_ problems with defining fill(255) vs fill(0xff808080) +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Syntax;action=display;num=1083650609;start=0 +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Syntax;action=display;num=1082481891;start=2 + +_ include in the docs: don't override stop() +_ or handle this some more intelligent way, super.stop() is needed. +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Syntax;action=display;num=1083574943 + +_ include a lerp()? is there one in flash? +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Programs;action=display;num=1083289030 + +_ acos/asin/etc? + +_ how to handle toInt() etc and the casting? +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Syntax;action=display;num=1083453511 + +_ flicker happening on osx java 1.4, but not 1.3: +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1083184297;start=0 + +_ hint(NEW_GRAPHICS) needs to be called before allocate() +_ otherwise stencil buffer and 'triangle' object are null on first run + +_ doesn't work when outside a function: +_ color bg_color = color(255,0,0); +_ colorMode broken for red() green() etc +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1068664455 + +_ keypressed hanging on applets with a code folder +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1081450102 + +_ mousePressed, keyPressed, others.. queue them all +_ queue multiple times +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Syntax;action=display;num=1079555200;start=0 +_ strangeness with key codes on keyPressed +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Syntax;action=display;num=1083406438;start=0 +_ key codes not properly coming through for UP/DOWN/etc +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1075138932;start=0 + +_ cursor() broken in applets on macosx? +_ or is it a java 1.4 versus java 1.3 problem? +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1081645955 + +_ ed's thread re: fullscreen strategies +_ could add a new BApplet that uses BufferStrategy? +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Programs;action=display;num=1081335361;start=15 + +_ angleMode(DEGREES) and angleMode(RADIANS) +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_Software;action=display;num=1075507381;start=0 + +_ weird ellipse bug with an alpha line in same image +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1083221401;start=0 +_ scaled ellipse showing up as a hexagon +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1083674213 + +image stuff +_ loadImage() seems to be caching everything from the jar +_ make a note of how to disable this +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Programs;action=display;num=1078795681#8 +_ get loadImage() to work properly with data folder +_ should probably use the code from loadStream +_ and the url stuff should be an alternate method altogether +_ BImage.smooth() ? +_ bug in BImage.smooth() when resizing an image +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1077669847 + +...lots of other bugs listed below + + +............................................................ + + +0071 or later + +_ colorMode(CMYK) +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Tools;action=display;num=1082055374;start=0 + +_ still some weirdness with thread flickering on the mac +_ and frenetic display updates on the pc +_ little window showing up on macosx when running +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1081284410 + +_ expand() on Object[] is worthless.. fix it with reflection? +_ also, should it be named resize() instead? + +_ make g.depthTest settable as a hint +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_Software;action=display;num=1078428462;start=0 + +_ link only works on absolute URLs +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1081710684;start=0 + +_ saveFrame() to a folder horks things up if a mkdirs() is required +_ on macosx, this makes things hang; on windows it just complains +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1081706752 + +_ also a note about using regexps in p5 +_ would be nice to wrap oro for simple matching + +_ properly sense when applet has focus +_ add a 'focused' variable to the applet +_ code now in zipdecode, maybe just list this as a standard thing? + +_ point() being funneled through beginShape is terribly slow +_ go the other way 'round + +_ when running as an applet, need to loadStream from documentBase +_ problem is that BGraphics has the loadStream code, not BApplet + + +// + + +IMAGE +_ make grayscale image in p5 +_ could be used in conjunction with alpha() to properly set alpha values +void toGrayscale() { + int col,lum; + for(int i=0; i>16&0xff)+151*(col>>8&0xff)+28*(col&0xff))>>8; + pixels[i]=lum<<16 | lum<<8 | lum; + } +} +_ alpha not set on images, so can't be used in photoshop as a layer +_ http://processing.org/discourse/yabb/YaBB.cgi?board=general;action=display;num=1078441623;start=0 +_ more blend() modes +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_Software;action=display;num=1082056702 + + +BUGS / Bagel +_ more weirdness with stroke on rect, prolly not alpha +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1085799526 +_ rect is not getting it's stroke color set +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1073582391;start=0 +_ alpha of zero still draws boogers on screen +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1073329613;start=0 +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1080342288;start=0 +_ new sphere code from toxi +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Syntax;action=display;num=1067005325 +_ weird problem with drawing/filling squares +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1077226984 +_ last vertex on LINE_LOOP fades out +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1076911893 +_ rotated text has a bug for when it goes offscreen +_ patch rotated text (from visualclusto) into bfont +_ mgorbet stroke transparency problem +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1076383048;start=0 +_ put SecurityException things around file i/o for applets +_ rather than checking online(), since applets might be signed +_ shut off the automatic gunzipping + + +BUGS / ellipse +_ problem with the fill +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1077834735 +_ z values not set properly on ellipses? +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1068752615 +_ ellipses are just plain ugly +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1073409011;start=0 + + +NETWORK +_ don't send unicode data +_ when you stop the client, it freezes +_ until you quit the processing running the server +_ (the server starts and stops fine) +_ add constants for building NET, move stuff around in bagel dir +_ some method for just downloading the entire contents of a url +_ add udp support + + +SERIAL +_ launcher.cpp broke serial.. see versions in processing.notcvs + + +VIDEO +_ problems with hanging video when not in the root of the c drive +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1084464062;start=0 +_ video keeps running, cpu load high, even after app killed +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1086800853 +_ selecting input source (wintv board and quickcam installed.. problem) +_ beginVideo not colored +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1069342913;start=0 +_ video commands not color coded +_ things will freeze if winvdig not installed +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1065185464 +_ quicktime 6.4 update breaks p5 on the mac? +_ http://docs.info.apple.com/article.html?artnum=93414&sessionID=anonymous%7C26893096&kbhost=kbase.info.apple.com%3a80%2f +_ "Type quicktime.std.stdQTConstants was not found" +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1066358763 +_ http://docs.info.apple.com/article.html?artnum=120255 +_ split classes to BVideo and BMovie ? +_ video hanging without a camera installed +_ just locks up after running examples, then does the 'can't delete' thing +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1060313779 +_ first row of video pixels are black +_ casey says it may only be his camera +_ video.pixels don't seem to have high bytes set +_ so fill(video.pixels[blah]) doesn't work +_ test against 'pixels' example +_ make BVideo subclass BImage +_ make sure the high bits are getting set as opaque +_ quicktime exporter or image sequence export +_ fairly quick--just use experimental code from dbn +_ this hangs frequently on the mac (prolly a sync issue) +_ karsten's stuff has same problem +_ but the roger's video tracking does not +void videoEvent() +{ + for(int i=0; i> 16 & 0xFF) > threshold) { + blackwhite.pixels[i] = #FFFFFF; + } else { + blackwhite.pixels[i] = #000000; + } + } +} + + +thesis / acg +_ make bagel more usable as standalone +_ breakout BGraphics (have its own BImage) +_ breakout BApplet into BComponent ? (fix out-of-bounds mouse) +_ possible use of BUtils ? +_ write documentation on general use +_ along with how to download from sourceforge (anonpass is blank) +_ needs to be used as a component, without applet +_ but retain functionality, ie. image i/o +_ exports pixels or a BImage or does MemoryImageSource itself +_ move math functions into utility library associated +_ with bagel, because those will be useful on other bagel platforms +_ pApplet will call BagelMath.whatever, so still looks like cos() +_ #ifdef to remove client and server code as well +_ break out BSerial as separate object like BVideo +_ include rxtx and the rest of that setup in subfolder +_ BSerial.flush and BSerial.available in object +_ need to resolve issues between rendering screen/file +_ illustrator-based rendering needs to work for ars projects +_ screen may be 400x400 pixels, but file be 36x36" +_ opengl export / rendering mode +_ currently implemented, but somewhat broken +_ finish this once all the line code is done +_ make possible to use buzz.pl to create versions w/ stuff removed +_ build gl4java for java 1.4 +_ read table/csv formatted data into a matrix +_ pseudo-database format version of this that stores indexes to file +_ rather than loading the whole thing at once +_ more advanced splitting of files into rows/cols uses another class +_ other class also has concept for random access of lines +_ by storing the line positions, can access without loading all +_ into memory because some files will be too large +_ illustrator export / rendering mode +_ also postscript or pdf export? +_ version of Illustrator.java that uses bagel api +_ sorting of polygons/lines on simple painters algorithm +_ better lighting model to show darkness at various depths +_ maybe just ultra-high res bitmaps from gl +_ version of BApplet that replaces g. with ai. or pdf. +_ history.. add my diffs sketch +_ could just include a compiled version of the diff app, ala jikes + + +NEW GRAPHICS +_ stroke not set on flat_rect +_ when drawing fonts w/ sami's code, left edge has problem +_ 8-bit (alpha) textures not blending +_ near-plane clipping currently disabled for triangles, enabled for lines +_ (but culling offscreen triangles works.. but may have been +_ commented out by carlos) +_ sphere code needs only front face polygon +_ all triangles must be counter-clockwise (front-facing) + + +_ some way to properly quit sketch when stopped +_ if people have other threads they've spawned, impossible to stop +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Syntax;action=display;num=1067383998 +_ rewrite bagel code.. +_ for this release, because it will break things along with the lib stuff +_ switch to PImage, PApplet, etc +_ update() mode needs to be hacked in +_ separating of BGraphics and BApplet +_ change copyrights on the files again (to match ?) +_ proper lineweight +_ camera clipping +_ put screenshots into their sketch folder +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Syntax;action=display;num=1046185738;start=0 +_ add a method BApplet.setPath() or something like that +_ add color(gray) and color(gray, alpha) +_ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_Software;action=display;num=1074180764 +_ light(x, y, z, c1, c2, c3, TYPE) +_ also BLight with same constructor, and on() and off() fxn +_ update illustrator code to use bagel api +_ even if not actually working properly.. just in naming of things +_ new networking client from simong +_ simong lighting code +_ screenGrab() at the end of a draw mode program is problematic +_ app might exit before the file has finished writing to disk +_ need to block other activity inside screenGrab until finished +_ catch security exceptions around applet i/o calls +_ not just for saving files, but provide better error msgs when +_ attempting to download from another server + + + +//////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// + +GRAPHICS LIBRARY + +A hybrid of OpenGL (3D Graphics) and some aspects of Postscript (Fill, Stroke) +The graphics library is called Bagel, which is an internal name. + + +BAGEL / Rendering + + b _ picking + b _ what is the API for picking? + b _ ability to write data other than image into the buffer + b _ user can introduce new kinds of buffers at will (!) + b _ lists of names of objects, or the 'line number' buffer + b _ but how to determine *where* on object the hit occurs + + _ add option to sort triangles back to front so alpha works + _ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1076660476;start=0 + + b _ lines + b X rewrite line and stroke code, it's a buggy mess + b X lines become 2 pixels thick after a 3D transform + b X better handling of single-pixel special se + b _ flat_line_retribution is a hack, can go away + b ? make sure line() commands don't try to have a fill + b _ box is not opaque + b X problem is that lines are drawn second + b X one pixel lines have no z value.. argh + b X bug re: 3d depth sorting on lines + b _ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1043894019;start=0 + b _ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1042004618 + b _ translate(58, 48, 0); + b _ rotateY(0.5); + b _ box(40); + b _ line endcaps and line joins. strokeMode() + b _ lower priority, but at least leave room + b _ strokeWeight is still broken + b _ setting stroke width on circle makes odd patterns + b _ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1077013848;start=0 + b _ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1080347160 + + b X clipping objects (clipping planes?) + b _ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1058491568;start=0 + b _ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1052313604;start=0 + b X things are flying into the camera and halting apps + b _ http://processing.org/discourse/yabb/YaBB.cgi?board=Proce55ing_software_bugs;action=display;num=1042699742 + b X NullPointerException apparently when things way offscreen + b _ i.e. glyphrot with scale set to 4 + b _ or at least that things get ridiculously slow + b _ clipping issues here.. but also something in scan converter + b X not clipping areas from offscreen + b _ huge geometry slows things way down + + + +BAGEL / Details + + 1 _ toxi ellipses don't adapt properly with transformations + 1 _ what is the stroked version of a sphere? a circle? + 1 _ non-homogenous coloring for curve vertices + 1 _ properly interpolate + 1 _ too many push() will silently stop the applet inside a loop + 1 _ test winding polygons in different directions + 1 _ test lighting to see how it compares with gl + 1 _ better lockout inside beginShape() to keep other things from happening + 1 _ is quad strip broken or not behaving as expected? + 1 _ may be correct, it worked for nik + 1 _ inside draw() mode, delay() does nothing + 1 _ delay might be a good way to signal drawing to the screen/updating + + +BAGEL / Fonts + + 1 _ sbit font support + 1 _ both reading and building sbit fonts + * _ mac -> vlw (or sbit?) font converter + * _ need to also read the fond for metrics