1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-06-20 21:01:25 +03:00

Importing Processing rev. 5503 (1.0.3).

This commit is contained in:
David A. Mellis
2009-05-31 15:53:33 +00:00
parent 40982627a3
commit 22ed6cdb73
767 changed files with 341874 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,461 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-08 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.core;
import java.awt.Cursor;
import java.awt.event.KeyEvent;
/**
* Numbers shared throughout processing.core.
* <P>
* An attempt is made to keep the constants as short/non-verbose
* as possible. For instance, the constant is TIFF instead of
* FILE_TYPE_TIFF. We'll do this as long as we can get away with it.
*/
public interface PConstants {
static public final int X = 0; // model coords xyz (formerly MX/MY/MZ)
static public final int Y = 1;
static public final int Z = 2;
static public final int R = 3; // actual rgb, after lighting
static public final int G = 4; // fill stored here, transform in place
static public final int B = 5; // TODO don't do that anymore (?)
static public final int A = 6;
static public final int U = 7; // texture
static public final int V = 8;
static public final int NX = 9; // normal
static public final int NY = 10;
static public final int NZ = 11;
static public final int EDGE = 12;
// stroke
/** stroke argb values */
static public final int SR = 13;
static public final int SG = 14;
static public final int SB = 15;
static public final int SA = 16;
/** stroke weight */
static public final int SW = 17;
// transformations (2D and 3D)
static public final int TX = 18; // transformed xyzw
static public final int TY = 19;
static public final int TZ = 20;
static public final int VX = 21; // view space coords
static public final int VY = 22;
static public final int VZ = 23;
static public final int VW = 24;
// material properties
// Ambient color (usually to be kept the same as diffuse)
// fill(_) sets both ambient and diffuse.
static public final int AR = 25;
static public final int AG = 26;
static public final int AB = 27;
// Diffuse is shared with fill.
static public final int DR = 3; // TODO needs to not be shared, this is a material property
static public final int DG = 4;
static public final int DB = 5;
static public final int DA = 6;
// specular (by default kept white)
static public final int SPR = 28;
static public final int SPG = 29;
static public final int SPB = 30;
static public final int SHINE = 31;
// emissive (by default kept black)
static public final int ER = 32;
static public final int EG = 33;
static public final int EB = 34;
// has this vertex been lit yet
static public final int BEEN_LIT = 35;
static public final int VERTEX_FIELD_COUNT = 36;
// renderers known to processing.core
static final String P2D = "processing.core.PGraphics2D";
static final String P3D = "processing.core.PGraphics3D";
static final String JAVA2D = "processing.core.PGraphicsJava2D";
static final String OPENGL = "processing.opengl.PGraphicsOpenGL";
static final String PDF = "processing.pdf.PGraphicsPDF";
static final String DXF = "processing.dxf.RawDXF";
// platform IDs for PApplet.platform
static final int OTHER = 0;
static final int WINDOWS = 1;
static final int MACOSX = 2;
static final int LINUX = 3;
static final String[] platformNames = {
"other", "windows", "macosx", "linux"
};
static final float EPSILON = 0.0001f;
// max/min values for numbers
/**
* Same as Float.MAX_VALUE, but included for parity with MIN_VALUE,
* and to avoid teaching static methods on the first day.
*/
static final float MAX_FLOAT = Float.MAX_VALUE;
/**
* Note that Float.MIN_VALUE is the smallest <EM>positive</EM> value
* for a floating point number, not actually the minimum (negative) value
* for a float. This constant equals 0xFF7FFFFF, the smallest (farthest
* negative) value a float can have before it hits NaN.
*/
static final float MIN_FLOAT = -Float.MAX_VALUE;
/** Largest possible (positive) integer value */
static final int MAX_INT = Integer.MAX_VALUE;
/** Smallest possible (negative) integer value */
static final int MIN_INT = Integer.MIN_VALUE;
// useful goodness
static final float PI = (float) Math.PI;
static final float HALF_PI = PI / 2.0f;
static final float THIRD_PI = PI / 3.0f;
static final float QUARTER_PI = PI / 4.0f;
static final float TWO_PI = PI * 2.0f;
static final float DEG_TO_RAD = PI/180.0f;
static final float RAD_TO_DEG = 180.0f/PI;
// angle modes
//static final int RADIANS = 0;
//static final int DEGREES = 1;
// used by split, all the standard whitespace chars
// (also includes unicode nbsp, that little bostage)
static final String WHITESPACE = " \t\n\r\f\u00A0";
// for colors and/or images
static final int RGB = 1; // image & color
static final int ARGB = 2; // image
static final int HSB = 3; // color
static final int ALPHA = 4; // image
static final int CMYK = 5; // image & color (someday)
// image file types
static final int TIFF = 0;
static final int TARGA = 1;
static final int JPEG = 2;
static final int GIF = 3;
// filter/convert types
static final int BLUR = 11;
static final int GRAY = 12;
static final int INVERT = 13;
static final int OPAQUE = 14;
static final int POSTERIZE = 15;
static final int THRESHOLD = 16;
static final int ERODE = 17;
static final int DILATE = 18;
// blend mode keyword definitions
// @see processing.core.PImage#blendColor(int,int,int)
public final static int REPLACE = 0;
public final static int BLEND = 1 << 0;
public final static int ADD = 1 << 1;
public final static int SUBTRACT = 1 << 2;
public final static int LIGHTEST = 1 << 3;
public final static int DARKEST = 1 << 4;
public final static int DIFFERENCE = 1 << 5;
public final static int EXCLUSION = 1 << 6;
public final static int MULTIPLY = 1 << 7;
public final static int SCREEN = 1 << 8;
public final static int OVERLAY = 1 << 9;
public final static int HARD_LIGHT = 1 << 10;
public final static int SOFT_LIGHT = 1 << 11;
public final static int DODGE = 1 << 12;
public final static int BURN = 1 << 13;
// 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
// shapes
// the low four bits set the variety,
// higher bits set the specific shape type
//static final int GROUP = (1 << 2);
static final int POINT = 2; // shared with light (!)
static final int POINTS = 2;
static final int LINE = 4;
static final int LINES = 4;
static final int TRIANGLE = 8;
static final int TRIANGLES = 9;
static final int TRIANGLE_STRIP = 10;
static final int TRIANGLE_FAN = 11;
static final int QUAD = 16;
static final int QUADS = 16;
static final int QUAD_STRIP = 17;
static final int POLYGON = 20;
static final int PATH = 21;
static final int RECT = 30;
static final int ELLIPSE = 31;
static final int ARC = 32;
static final int SPHERE = 40;
static final int BOX = 41;
// shape closing modes
static final int OPEN = 1;
static final int CLOSE = 2;
// shape drawing modes
/** Draw mode convention to use (x, y) to (width, height) */
static final int CORNER = 0;
/** Draw mode convention to use (x1, y1) to (x2, y2) coordinates */
static final int CORNERS = 1;
/** Draw mode from the center, and using the radius */
static final int RADIUS = 2;
/** @deprecated Use RADIUS instead. */
static final int CENTER_RADIUS = 2;
/**
* Draw from the center, using second pair of values as the diameter.
* Formerly called CENTER_DIAMETER in alpha releases.
*/
static final int CENTER = 3;
/**
* Synonym for the CENTER constant. Draw from the center,
* using second pair of values as the diameter.
*/
static final int DIAMETER = 3;
/** @deprecated Use DIAMETER instead. */
static final int CENTER_DIAMETER = 3;
// vertically alignment modes for text
/** Default vertical alignment for text placement */
static final int BASELINE = 0;
/** Align text to the top */
static final int TOP = 101;
/** Align text from the bottom, using the baseline. */
static final int BOTTOM = 102;
// uv texture orientation modes
/** texture coordinates in 0..1 range */
static final int NORMAL = 1;
/** @deprecated use NORMAL instead */
static final int NORMALIZED = 1;
/** texture coordinates based on image width/height */
static final int IMAGE = 2;
// text placement modes
/**
* textMode(MODEL) is the default, meaning that characters
* will be affected by transformations like any other shapes.
* <p/>
* Changed value in 0093 to not interfere with LEFT, CENTER, and RIGHT.
*/
static final int MODEL = 4;
/**
* textMode(SHAPE) draws text using the the glyph outlines of
* individual characters rather than as textures. If the outlines are
* not available, then textMode(SHAPE) will be ignored and textMode(MODEL)
* will be used instead. For this reason, be sure to call textMode()
* <EM>after</EM> calling textFont().
* <p/>
* Currently, textMode(SHAPE) is only supported by OPENGL mode.
* It also requires Java 1.2 or higher (OPENGL requires 1.4 anyway)
*/
static final int SHAPE = 5;
// text alignment modes
// are inherited from LEFT, CENTER, RIGHT
// stroke modes
static final int SQUARE = 1 << 0; // called 'butt' in the svg spec
static final int ROUND = 1 << 1;
static final int PROJECT = 1 << 2; // called 'square' in the svg spec
static final int MITER = 1 << 3;
static final int BEVEL = 1 << 5;
// lighting
static final int AMBIENT = 0;
static final int DIRECTIONAL = 1;
//static final int POINT = 2; // shared with shape feature
static final int SPOT = 3;
// key constants
// only including the most-used of these guys
// if people need more esoteric keys, they can learn about
// the esoteric java KeyEvent api and of virtual keys
// both key and keyCode will equal these values
// for 0125, these were changed to 'char' values, because they
// can be upgraded to ints automatically by Java, but having them
// as ints prevented split(blah, TAB) from working
static final char BACKSPACE = 8;
static final char TAB = 9;
static final char ENTER = 10;
static final char RETURN = 13;
static final char ESC = 27;
static final char DELETE = 127;
// i.e. if ((key == CODED) && (keyCode == UP))
static final int CODED = 0xffff;
// key will be CODED and keyCode will be this value
static final int UP = KeyEvent.VK_UP;
static final int DOWN = KeyEvent.VK_DOWN;
static final int LEFT = KeyEvent.VK_LEFT;
static final int RIGHT = KeyEvent.VK_RIGHT;
// key will be CODED and keyCode will be this value
static final int ALT = KeyEvent.VK_ALT;
static final int CONTROL = KeyEvent.VK_CONTROL;
static final int SHIFT = KeyEvent.VK_SHIFT;
// cursor types
static final int ARROW = Cursor.DEFAULT_CURSOR;
static final int CROSS = Cursor.CROSSHAIR_CURSOR;
static final int HAND = Cursor.HAND_CURSOR;
static final int MOVE = Cursor.MOVE_CURSOR;
static final int TEXT = Cursor.TEXT_CURSOR;
static final int WAIT = Cursor.WAIT_CURSOR;
// hints - hint values are positive for the alternate version,
// negative of the same value returns to the normal/default state
static final int DISABLE_OPENGL_2X_SMOOTH = 1;
static final int ENABLE_OPENGL_2X_SMOOTH = -1;
static final int ENABLE_OPENGL_4X_SMOOTH = 2;
static final int ENABLE_NATIVE_FONTS = 3;
static final int DISABLE_DEPTH_TEST = 4;
static final int ENABLE_DEPTH_TEST = -4;
static final int ENABLE_DEPTH_SORT = 5;
static final int DISABLE_DEPTH_SORT = -5;
static final int DISABLE_OPENGL_ERROR_REPORT = 6;
static final int ENABLE_OPENGL_ERROR_REPORT = -6;
static final int ENABLE_ACCURATE_TEXTURES = 7;
static final int DISABLE_ACCURATE_TEXTURES = -7;
static final int HINT_COUNT = 10;
// error messages
static final String ERROR_BACKGROUND_IMAGE_SIZE =
"background image must be the same size as your application";
static final String ERROR_BACKGROUND_IMAGE_FORMAT =
"background images should be RGB or ARGB";
static final String ERROR_TEXTFONT_NULL_PFONT =
"A null PFont was passed to textFont()";
static final String ERROR_PUSHMATRIX_OVERFLOW =
"Too many calls to pushMatrix().";
static final String ERROR_PUSHMATRIX_UNDERFLOW =
"Too many calls to popMatrix(), and not enough to pushMatrix().";
}

View File

@ -0,0 +1,711 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-07 Ben Fry & Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.core;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.*;
//import java.lang.reflect.*;
import java.util.Arrays;
/**
* Grayscale bitmap font class used by Processing.
* <P>
* Awful (and by that, I mean awesome) ascii (non)art for how this works:
* <PRE>
* |
* | height is the full used height of the image
* |
* | ..XX.. }
* | ..XX.. }
* | ...... }
* | XXXX.. } topExtent (top y is baseline - topExtent)
* | ..XX.. }
* | ..XX.. } dotted areas are where the image data
* | ..XX.. } is actually located for the character
* +---XXXXXX---- } (it extends to the right and down
* | for power of two texture sizes)
* ^^^^ leftExtent (amount to move over before drawing the image
*
* ^^^^^^^^^^^^^^ setWidth (width displaced by char)
* </PRE>
*/
public class PFont implements PConstants {
public int charCount;
public PImage images[];
/**
* Native Java version of the font. If possible, this allows the
* PGraphics subclass to just use Java's font rendering stuff
* in situations where that's faster.
*/
protected Font font;
protected boolean fontSearched;
/**
* Name of the font as seen by Java when it was created.
* If the font is available, the native version will be used.
*/
public String name;
/**
* Postscript name of the font that this bitmap was created from.
*/
public String psname;
/** "natural" size of the font (most often 48) */
public int size;
/** true if smoothing was enabled for this font, used for native impl */
public boolean smooth;
/** next power of 2 over the max image size (usually 64) */
public int mbox2;
/** floating point width (convenience) */
protected float fwidth;
/** floating point width (convenience) */
protected float fheight;
/** texture width, same as mbox2, but reserved for future use */
public int twidth;
/** texture height, same as mbox2, but reserved for future use */
public int theight;
public int value[]; // char code
public int height[]; // height of the bitmap data
public int width[]; // width of bitmap data
public int setWidth[]; // width displaced by the char
public int topExtent[]; // offset for the top
public int leftExtent[]; // offset for the left
public int ascent;
public int descent;
protected int ascii[]; // quick lookup for the ascii chars
// shared by the text() functions to avoid incessant allocation of memory
//protected char textBuffer[] = new char[8 * 1024];
//protected char widthBuffer[] = new char[8 * 1024];
static protected Font[] fonts;
public PFont() { } // for subclasses
public PFont(InputStream input) throws IOException {
DataInputStream is = new DataInputStream(input);
// number of character images stored in this font
charCount = is.readInt();
// bit count is ignored since this is always 8
//int numBits = is.readInt();
// used to be the bitCount, but now used for version number.
// version 8 is any font before 69, so 9 is anything from 83+
// 9 was buggy so gonna increment to 10.
int version = is.readInt();
// this was formerly ignored, now it's the actual font size
//mbox = is.readInt();
size = is.readInt();
// this was formerly mboxY, the one that was used
// this will make new fonts downward compatible
//mbox2 = is.readInt();
mbox2 = is.readInt();
fwidth = size; //mbox;
fheight = size; //mbox;
// size for image ("texture") is next power of 2
// over the font size. for most vlw fonts, the size is 48
// so the next power of 2 is 64.
// double-check to make sure that mbox2 is a power of 2
// there was a bug in the old font generator that broke this
//mbox2 = (int) Math.pow(2, Math.ceil(Math.log(mbox2) / Math.log(2)));
mbox2 = (int) Math.pow(2, Math.ceil(Math.log(mbox2) / Math.log(2)));
// size for the texture is stored in the font
twidth = theight = mbox2; //mbox2;
ascent = is.readInt(); // formerly baseHt (zero/ignored)
descent = is.readInt(); // formerly ignored struct padding
// allocate enough space for the character info
value = new int[charCount];
height = new int[charCount];
width = new int[charCount];
setWidth = new int[charCount];
topExtent = new int[charCount];
leftExtent = new int[charCount];
ascii = new int[128];
for (int i = 0; i < 128; i++) ascii[i] = -1;
// read the information about the individual characters
for (int i = 0; i < charCount; i++) {
value[i] = is.readInt();
height[i] = is.readInt();
width[i] = is.readInt();
setWidth[i] = is.readInt();
topExtent[i] = is.readInt();
leftExtent[i] = is.readInt();
// pointer in the c version, ignored
is.readInt();
// cache locations of the ascii charset
if (value[i] < 128) ascii[value[i]] = i;
// the values for getAscent() and getDescent() from FontMetrics
// seem to be way too large.. perhaps they're the max?
// as such, use a more traditional marker for ascent/descent
if (value[i] == 'd') {
if (ascent == 0) ascent = topExtent[i];
}
if (value[i] == 'p') {
if (descent == 0) descent = -topExtent[i] + height[i];
}
}
// not a roman font, so throw an error and ask to re-build.
// that way can avoid a bunch of error checking hacks in here.
if ((ascent == 0) && (descent == 0)) {
throw new RuntimeException("Please use \"Create Font\" to " +
"re-create this font.");
}
images = new PImage[charCount];
for (int i = 0; i < charCount; i++) {
images[i] = new PImage(twidth, theight, ALPHA);
int bitmapSize = height[i] * width[i];
byte temp[] = new byte[bitmapSize];
is.readFully(temp);
// convert the bitmap to an alpha channel
int w = width[i];
int h = height[i];
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int valu = temp[y*w + x] & 0xff;
images[i].pixels[y * twidth + x] = valu;
//(valu << 24) | 0xFFFFFF; // windows
//0xFFFFFF00 | valu; // macosx
//System.out.print((images[i].pixels[y*64+x] > 128) ? "*" : ".");
}
//System.out.println();
}
//System.out.println();
}
if (version >= 10) { // includes the font name at the end of the file
name = is.readUTF();
psname = is.readUTF();
}
if (version == 11) {
smooth = is.readBoolean();
}
}
/**
* Set the native complement of this font.
*/
public void setFont(Font font) {
this.font = font;
}
/**
* Return the native java.awt.Font associated with this PFont (if any).
*/
public Font getFont() {
// if (font == null && !fontSearched) {
// font = findFont();
// }
return font;
}
/**
* Attempt to find the native version of this font.
* (Public so that it can be used by OpenGL or other renderers.)
*/
public Font findFont() {
if (font == null) {
if (!fontSearched) {
// this font may or may not be installed
font = new Font(name, Font.PLAIN, size);
// if the ps name matches, then we're in fine shape
if (!font.getPSName().equals(psname)) {
// on osx java 1.4 (not 1.3.. ugh), you can specify the ps name
// of the font, so try that in case this .vlw font was created on pc
// and the name is different, but the ps name is found on the
// java 1.4 mac that's currently running this sketch.
font = new Font(psname, Font.PLAIN, size);
}
// check again, and if still bad, screw em
if (!font.getPSName().equals(psname)) {
font = null;
}
fontSearched = true;
}
}
return font;
}
/**
* Write this PFont to an OutputStream.
* <p>
* This is used by the Create Font tool, or whatever anyone else dreams
* up for messing with fonts themselves.
* <p>
* It is assumed that the calling class will handle closing
* the stream when finished.
*/
public void save(OutputStream output) throws IOException {
DataOutputStream os = new DataOutputStream(output);
os.writeInt(charCount);
if ((name == null) || (psname == null)) {
name = "";
psname = "";
}
// formerly numBits, now used for version number
//os.writeInt((name != null) ? 11 : 8);
os.writeInt(11);
os.writeInt(size); // formerly mboxX (was 64, now 48)
os.writeInt(mbox2); // formerly mboxY (was 64, still 64)
os.writeInt(ascent); // formerly baseHt (was ignored)
os.writeInt(descent); // formerly struct padding for c version
for (int i = 0; i < charCount; i++) {
os.writeInt(value[i]);
os.writeInt(height[i]);
os.writeInt(width[i]);
os.writeInt(setWidth[i]);
os.writeInt(topExtent[i]);
os.writeInt(leftExtent[i]);
os.writeInt(0); // padding
}
for (int i = 0; i < charCount; i++) {
for (int y = 0; y < height[i]; y++) {
for (int x = 0; x < width[i]; x++) {
os.write(images[i].pixels[y * mbox2 + x] & 0xff);
}
}
}
//if (name != null) { // version 11
os.writeUTF(name);
os.writeUTF(psname);
os.writeBoolean(smooth);
//}
os.flush();
}
/**
* Get index for the char (convert from unicode to bagel charset).
* @return index into arrays or -1 if not found
*/
public int index(char c) {
// degenerate case, but the find function will have trouble
// if there are somehow zero chars in the lookup
//if (value.length == 0) return -1;
if (charCount == 0) return -1;
// quicker lookup for the ascii fellers
if (c < 128) return ascii[c];
// some other unicode char, hunt it out
//return index_hunt(c, 0, value.length-1);
return indexHunt(c, 0, charCount-1);
}
protected int indexHunt(int c, int start, int stop) {
int pivot = (start + stop) / 2;
// if this is the char, then return it
if (c == value[pivot]) return pivot;
// char doesn't exist, otherwise would have been the pivot
//if (start == stop) return -1;
if (start >= stop) return -1;
// if it's in the lower half, continue searching that
if (c < value[pivot]) return indexHunt(c, start, pivot-1);
// if it's in the upper half, continue there
return indexHunt(c, pivot+1, stop);
}
/**
* Currently un-implemented for .vlw fonts,
* but honored for layout in case subclasses use it.
*/
public float kern(char a, char b) {
return 0;
}
/**
* Returns the ascent of this font from the baseline.
* The value is based on a font of size 1.
*/
public float ascent() {
return ((float)ascent / fheight);
}
/**
* Returns how far this font descends from the baseline.
* The value is based on a font size of 1.
*/
public float descent() {
return ((float)descent / fheight);
}
/**
* Width of this character for a font of size 1.
*/
public float width(char c) {
if (c == 32) return width('i');
int cc = index(c);
if (cc == -1) return 0;
return ((float)setWidth[cc] / fwidth);
}
//////////////////////////////////////////////////////////////
static final char[] EXTRA_CHARS = {
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
0x00B0, 0x00B1, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00BA,
0x00BB, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5,
0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD,
0x00CE, 0x00CF, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6,
0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DF,
0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8,
0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FF, 0x0102, 0x0103,
0x0104, 0x0105, 0x0106, 0x0107, 0x010C, 0x010D, 0x010E, 0x010F,
0x0110, 0x0111, 0x0118, 0x0119, 0x011A, 0x011B, 0x0131, 0x0139,
0x013A, 0x013D, 0x013E, 0x0141, 0x0142, 0x0143, 0x0144, 0x0147,
0x0148, 0x0150, 0x0151, 0x0152, 0x0153, 0x0154, 0x0155, 0x0158,
0x0159, 0x015A, 0x015B, 0x015E, 0x015F, 0x0160, 0x0161, 0x0162,
0x0163, 0x0164, 0x0165, 0x016E, 0x016F, 0x0170, 0x0171, 0x0178,
0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x0192, 0x02C6,
0x02C7, 0x02D8, 0x02D9, 0x02DA, 0x02DB, 0x02DC, 0x02DD, 0x03A9,
0x03C0, 0x2013, 0x2014, 0x2018, 0x2019, 0x201A, 0x201C, 0x201D,
0x201E, 0x2020, 0x2021, 0x2022, 0x2026, 0x2030, 0x2039, 0x203A,
0x2044, 0x20AC, 0x2122, 0x2202, 0x2206, 0x220F, 0x2211, 0x221A,
0x221E, 0x222B, 0x2248, 0x2260, 0x2264, 0x2265, 0x25CA, 0xF8FF,
0xFB01, 0xFB02
};
/**
* The default Processing character set.
* <P>
* This is the union of the Mac Roman and Windows ANSI (CP1250)
* character sets. ISO 8859-1 Latin 1 is Unicode characters 0x80 -> 0xFF,
* and would seem a good standard, but in practice, most P5 users would
* rather have characters that they expect from their platform's fonts.
* <P>
* 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).
* <P>
* Not that I expect that to happen.
*/
static public char[] DEFAULT_CHARSET;
static {
DEFAULT_CHARSET = new char[126-33+1 + EXTRA_CHARS.length];
int index = 0;
for (int i = 33; i <= 126; i++) {
DEFAULT_CHARSET[index++] = (char)i;
}
for (int i = 0; i < EXTRA_CHARS.length; i++) {
DEFAULT_CHARSET[index++] = EXTRA_CHARS[i];
}
};
/**
* Create a new image-based font on the fly.
*
* @param font the font object to create from
* @param charset array of all unicode chars that should be included
* @param smooth true to enable smoothing/anti-aliasing
*/
public PFont(Font font, boolean smooth, char charset[]) {
// save this so that we can use the native version
this.font = font;
this.smooth = smooth;
name = font.getName();
psname = font.getPSName();
// fix regression from sorting (bug #564)
if (charset != null) {
// charset needs to be sorted to make index lookup run more quickly
// http://dev.processing.org/bugs/show_bug.cgi?id=494
Arrays.sort(charset);
}
// the count gets reset later based on how many of
// the chars are actually found inside the font.
this.charCount = (charset == null) ? 65536 : charset.length;
this.size = font.getSize();
fwidth = fheight = size;
PImage bitmaps[] = new PImage[charCount];
// allocate enough space for the character info
value = new int[charCount];
height = new int[charCount];
width = new int[charCount];
setWidth = new int[charCount];
topExtent = new int[charCount];
leftExtent = new int[charCount];
ascii = new int[128];
for (int i = 0; i < 128; i++) ascii[i] = -1;
int mbox3 = size * 3;
BufferedImage playground =
new BufferedImage(mbox3, mbox3, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) playground.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
smooth ?
RenderingHints.VALUE_ANTIALIAS_ON :
RenderingHints.VALUE_ANTIALIAS_OFF);
g.setFont(font);
FontMetrics metrics = g.getFontMetrics();
int samples[] = new int[mbox3 * mbox3];
int maxWidthHeight = 0;
int index = 0;
for (int i = 0; i < charCount; i++) {
char c = (charset == null) ? (char)i : charset[i];
if (!font.canDisplay(c)) { // skip chars not in the font
continue;
}
g.setColor(Color.white);
g.fillRect(0, 0, mbox3, mbox3);
g.setColor(Color.black);
g.drawString(String.valueOf(c), size, size * 2);
// grabs copy of the current data.. so no updates (do each time)
Raster raster = playground.getData();
raster.getSamples(0, 0, mbox3, mbox3, 0, samples);
int minX = 1000, maxX = 0;
int minY = 1000, maxY = 0;
boolean pixelFound = false;
for (int y = 0; y < mbox3; y++) {
for (int x = 0; x < mbox3; x++) {
//int sample = raster.getSample(x, y, 0); // maybe?
int sample = samples[y * mbox3 + x] & 0xff;
// or int samples[] = raster.getPixel(x, y, null);
//if (sample == 0) { // or just not white? hmm
if (sample != 255) {
if (x < minX) minX = x;
if (y < minY) minY = y;
if (x > maxX) maxX = x;
if (y > maxY) maxY = y;
pixelFound = true;
}
}
}
if (!pixelFound) {
minX = minY = 0;
maxX = maxY = 0;
// 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);
//System.out.println((char)c + " " + setWidth[index]);
// cache locations of the ascii charset
//if (value[i] < 128) ascii[value[i]] = i;
if (c < 128) ascii[c] = index;
// offset from vertical location of baseline
// of where the char was drawn (size*2)
topExtent[index] = size*2 - minY;
// offset from left of where coord was drawn
leftExtent[index] = minX - size;
if (c == 'd') {
ascent = topExtent[index];
}
if (c == 'p') {
descent = -topExtent[index] + height[index];
}
if (width[index] > maxWidthHeight) maxWidthHeight = width[index];
if (height[index] > maxWidthHeight) maxWidthHeight = height[index];
bitmaps[index] = new PImage(width[index], height[index], ALPHA);
for (int y = minY; y <= maxY; y++) {
for (int x = minX; x <= maxX; x++) {
int val = 255 - (samples[y * mbox3 + x] & 0xff);
int pindex = (y - minY) * width[index] + (x - minX);
bitmaps[index].pixels[pindex] = val;
}
}
index++;
}
charCount = index;
// foreign font, so just make ascent the max topExtent
if ((ascent == 0) && (descent == 0)) {
for (int i = 0; i < charCount; i++) {
char cc = (char) value[i];
if (Character.isWhitespace(cc) ||
(cc == '\u00A0') || (cc == '\u2007') || (cc == '\u202F')) {
continue;
}
if (topExtent[i] > ascent) {
ascent = topExtent[i];
}
int d = -topExtent[i] + height[i];
if (d > descent) {
descent = d;
}
}
}
// size for image/texture is next power of 2 over largest char
mbox2 = (int)
Math.pow(2, Math.ceil(Math.log(maxWidthHeight) / Math.log(2)));
twidth = theight = mbox2;
// shove the smaller PImage data into textures of next-power-of-2 size,
// so that this font can be used immediately by p5.
images = new PImage[charCount];
for (int i = 0; i < charCount; i++) {
images[i] = new PImage(mbox2, mbox2, ALPHA);
for (int y = 0; y < height[i]; y++) {
System.arraycopy(bitmaps[i].pixels, y*width[i],
images[i].pixels, y*mbox2,
width[i]);
}
bitmaps[i] = null;
}
}
/**
* Get a list of the fonts installed on the system that can be used
* by Java. Not all fonts can be used in Java, in fact it's mostly
* only TrueType fonts. OpenType fonts with CFF data such as Adobe's
* OpenType fonts seem to have trouble (even though they're sort of
* TrueType fonts as well, or may have a .ttf extension). Regular
* PostScript fonts seem to work OK, however.
* <P>
* Not recommended for use in applets, but this is implemented
* in PFont because the Java methods to access this information
* have changed between 1.1 and 1.4, and the 1.4 method is
* typical of the sort of undergraduate-level over-abstraction
* that the seems to have made its way into the Java API after 1.1.
*/
static public String[] list() {
loadFonts();
String list[] = new String[fonts.length];
for (int i = 0; i < list.length; i++) {
list[i] = fonts[i].getName();
}
return list;
}
static public void loadFonts() {
if (fonts == null) {
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
fonts = ge.getAllFonts();
}
}
/**
* Starting with Java 1.5, Apple broke the ability to specify most fonts.
* This has been filed as bug #4769141 at bugreporter.apple.com. More info at
* <a href="http://dev.processing.org/bugs/show_bug.cgi?id=407">Bug 407</a>.
*/
static public Font findFont(String name) {
loadFonts();
if (PApplet.platform == PConstants.MACOSX) {
for (int i = 0; i < fonts.length; i++) {
if (name.equals(fonts[i].getName())) {
return fonts[i];
}
}
}
return new Font(name, Font.PLAIN, 1);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,150 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2005-08 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.core;
public interface PMatrix {
public void reset();
/**
* Returns a copy of this PMatrix.
*/
public PMatrix get();
/**
* Copies the matrix contents into a float array.
* If target is null (or not the correct size), a new array will be created.
*/
public float[] get(float[] target);
public void set(PMatrix src);
public void set(float[] source);
public void set(float m00, float m01, float m02,
float m10, float m11, float m12);
public void set(float m00, float m01, float m02, float m03,
float m10, float m11, float m12, float m13,
float m20, float m21, float m22, float m23,
float m30, float m31, float m32, float m33);
public void translate(float tx, float ty);
public void translate(float tx, float ty, float tz);
public void rotate(float angle);
public void rotateX(float angle);
public void rotateY(float angle);
public void rotateZ(float angle);
public void rotate(float angle, float v0, float v1, float v2);
public void scale(float s);
public void scale(float sx, float sy);
public void scale(float x, float y, float z);
public void skewX(float angle);
public void skewY(float angle);
/**
* Multiply this matrix by another.
*/
public void apply(PMatrix source);
public void apply(PMatrix2D source);
public void apply(PMatrix3D source);
public void apply(float n00, float n01, float n02,
float n10, float n11, float n12);
public void apply(float n00, float n01, float n02, float n03,
float n10, float n11, float n12, float n13,
float n20, float n21, float n22, float n23,
float n30, float n31, float n32, float n33);
/**
* Apply another matrix to the left of this one.
*/
public void preApply(PMatrix2D left);
public void preApply(PMatrix3D left);
public void preApply(float n00, float n01, float n02,
float n10, float n11, float n12);
public void preApply(float n00, float n01, float n02, float n03,
float n10, float n11, float n12, float n13,
float n20, float n21, float n22, float n23,
float n30, float n31, float n32, float n33);
/**
* Multiply a PVector by this matrix.
*/
public PVector mult(PVector source, PVector target);
/**
* Multiply a multi-element vector against this matrix.
*/
public float[] mult(float[] source, float[] target);
// public float multX(float x, float y);
// public float multY(float x, float y);
// public float multX(float x, float y, float z);
// public float multY(float x, float y, float z);
// public float multZ(float x, float y, float z);
/**
* Transpose this matrix.
*/
public void transpose();
/**
* Invert this matrix.
* @return true if successful
*/
public boolean invert();
/**
* @return the determinant of the matrix
*/
public float determinant();
}

View File

@ -0,0 +1,450 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2005-08 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.core;
/**
* 3x2 affine matrix implementation.
*/
public class PMatrix2D implements PMatrix {
public float m00, m01, m02;
public float m10, m11, m12;
public PMatrix2D() {
reset();
}
public PMatrix2D(float m00, float m01, float m02,
float m10, float m11, float m12) {
set(m00, m01, m02,
m10, m11, m12);
}
public PMatrix2D(PMatrix matrix) {
set(matrix);
}
public void reset() {
set(1, 0, 0,
0, 1, 0);
}
/**
* Returns a copy of this PMatrix.
*/
public PMatrix2D get() {
PMatrix2D outgoing = new PMatrix2D();
outgoing.set(this);
return outgoing;
}
/**
* Copies the matrix contents into a 6 entry float array.
* If target is null (or not the correct size), a new array will be created.
*/
public float[] get(float[] target) {
if ((target == null) || (target.length != 6)) {
target = new float[6];
}
target[0] = m00;
target[1] = m01;
target[2] = m02;
target[3] = m10;
target[4] = m11;
target[5] = m12;
return target;
}
public void set(PMatrix matrix) {
if (matrix instanceof PMatrix2D) {
PMatrix2D src = (PMatrix2D) matrix;
set(src.m00, src.m01, src.m02,
src.m10, src.m11, src.m12);
} else {
throw new IllegalArgumentException("PMatrix2D.set() only accepts PMatrix2D objects.");
}
}
public void set(PMatrix3D src) {
}
public void set(float[] source) {
m00 = source[0];
m01 = source[1];
m02 = source[2];
m10 = source[3];
m11 = source[4];
m12 = source[5];
}
public void set(float m00, float m01, float m02,
float m10, float m11, float m12) {
this.m00 = m00; this.m01 = m01; this.m02 = m02;
this.m10 = m10; this.m11 = m11; this.m12 = m12;
}
public void set(float m00, float m01, float m02, float m03,
float m10, float m11, float m12, float m13,
float m20, float m21, float m22, float m23,
float m30, float m31, float m32, float m33) {
}
public void translate(float tx, float ty) {
m02 = tx*m00 + ty*m01 + m02;
m12 = tx*m10 + ty*m11 + m12;
}
public void translate(float x, float y, float z) {
throw new IllegalArgumentException("Cannot use translate(x, y, z) on a PMatrix2D.");
}
// Implementation roughly based on AffineTransform.
public void rotate(float angle) {
float s = sin(angle);
float c = cos(angle);
float temp1 = m00;
float temp2 = m01;
m00 = c * temp1 + s * temp2;
m01 = -s * temp1 + c * temp2;
temp1 = m10;
temp2 = m11;
m10 = c * temp1 + s * temp2;
m11 = -s * temp1 + c * temp2;
}
public void rotateX(float angle) {
throw new IllegalArgumentException("Cannot use rotateX() on a PMatrix2D.");
}
public void rotateY(float angle) {
throw new IllegalArgumentException("Cannot use rotateY() on a PMatrix2D.");
}
public void rotateZ(float angle) {
rotate(angle);
}
public void rotate(float angle, float v0, float v1, float v2) {
throw new IllegalArgumentException("Cannot use this version of rotate() on a PMatrix2D.");
}
public void scale(float s) {
scale(s, s);
}
public void scale(float sx, float sy) {
m00 *= sx; m01 *= sy;
m10 *= sx; m11 *= sy;
}
public void scale(float x, float y, float z) {
throw new IllegalArgumentException("Cannot use this version of scale() on a PMatrix2D.");
}
public void skewX(float angle) {
apply(1, 0, 1, angle, 0, 0);
}
public void skewY(float angle) {
apply(1, 0, 1, 0, angle, 0);
}
public void apply(PMatrix source) {
if (source instanceof PMatrix2D) {
apply((PMatrix2D) source);
} else if (source instanceof PMatrix3D) {
apply((PMatrix3D) source);
}
}
public void apply(PMatrix2D source) {
apply(source.m00, source.m01, source.m02,
source.m10, source.m11, source.m12);
}
public void apply(PMatrix3D source) {
throw new IllegalArgumentException("Cannot use apply(PMatrix3D) on a PMatrix2D.");
}
public void apply(float n00, float n01, float n02,
float n10, float n11, float n12) {
float t0 = m00;
float t1 = m01;
m00 = n00 * t0 + n10 * t1;
m01 = n01 * t0 + n11 * t1;
m02 += n02 * t0 + n12 * t1;
t0 = m10;
t1 = m11;
m10 = n00 * t0 + n10 * t1;
m11 = n01 * t0 + n11 * t1;
m12 += n02 * t0 + n12 * t1;
}
public void apply(float n00, float n01, float n02, float n03,
float n10, float n11, float n12, float n13,
float n20, float n21, float n22, float n23,
float n30, float n31, float n32, float n33) {
throw new IllegalArgumentException("Cannot use this version of apply() on a PMatrix2D.");
}
/**
* Apply another matrix to the left of this one.
*/
public void preApply(PMatrix2D left) {
preApply(left.m00, left.m01, left.m02,
left.m10, left.m11, left.m12);
}
public void preApply(PMatrix3D left) {
throw new IllegalArgumentException("Cannot use preApply(PMatrix3D) on a PMatrix2D.");
}
public void preApply(float n00, float n01, float n02,
float n10, float n11, float n12) {
float t0 = m02;
float t1 = m12;
n02 += t0 * n00 + t1 * n01;
n12 += t0 * n10 + t1 * n11;
m02 = n02;
m12 = n12;
t0 = m00;
t1 = m10;
m00 = t0 * n00 + t1 * n01;
m10 = t0 * n10 + t1 * n11;
t0 = m01;
t1 = m11;
m01 = t0 * n00 + t1 * n01;
m11 = t0 * n10 + t1 * n11;
}
public void preApply(float n00, float n01, float n02, float n03,
float n10, float n11, float n12, float n13,
float n20, float n21, float n22, float n23,
float n30, float n31, float n32, float n33) {
throw new IllegalArgumentException("Cannot use this version of preApply() on a PMatrix2D.");
}
//////////////////////////////////////////////////////////////
/**
* Multiply the x and y coordinates of a PVector against this matrix.
*/
public PVector mult(PVector source, PVector target) {
if (target == null) {
target = new PVector();
}
target.x = m00*source.x + m01*source.y + m02;
target.y = m10*source.x + m11*source.y + m12;
return target;
}
/**
* Multiply a two element vector against this matrix.
* If out is null or not length four, a new float array will be returned.
* The values for vec and out can be the same (though that's less efficient).
*/
public float[] mult(float vec[], float out[]) {
if (out == null || out.length != 2) {
out = new float[2];
}
if (vec == out) {
float tx = m00*vec[0] + m01*vec[1] + m02;
float ty = m10*vec[0] + m11*vec[1] + m12;
out[0] = tx;
out[1] = ty;
} else {
out[0] = m00*vec[0] + m01*vec[1] + m02;
out[1] = m10*vec[0] + m11*vec[1] + m12;
}
return out;
}
public float multX(float x, float y) {
return m00*x + m01*y + m02;
}
public float multY(float x, float y) {
return m10*x + m11*y + m12;
}
/**
* Transpose this matrix.
*/
public void transpose() {
}
/**
* Invert this matrix. Implementation stolen from OpenJDK.
* @return true if successful
*/
public boolean invert() {
float determinant = determinant();
if (Math.abs(determinant) <= Float.MIN_VALUE) {
return false;
}
float t00 = m00;
float t01 = m01;
float t02 = m02;
float t10 = m10;
float t11 = m11;
float t12 = m12;
m00 = t11 / determinant;
m10 = -t10 / determinant;
m01 = -t01 / determinant;
m11 = t00 / determinant;
m02 = (t01 * t12 - t11 * t02) / determinant;
m12 = (t10 * t02 - t00 * t12) / determinant;
return true;
}
/**
* @return the determinant of the matrix
*/
public float determinant() {
return m00 * m11 - m01 * m10;
}
//////////////////////////////////////////////////////////////
public void print() {
int big = (int) abs(max(PApplet.max(abs(m00), abs(m01), abs(m02)),
PApplet.max(abs(m10), abs(m11), abs(m12))));
int digits = 1;
if (Float.isNaN(big) || Float.isInfinite(big)) { // avoid infinite loop
digits = 5;
} else {
while ((big /= 10) != 0) digits++; // cheap log()
}
System.out.println(PApplet.nfs(m00, digits, 4) + " " +
PApplet.nfs(m01, digits, 4) + " " +
PApplet.nfs(m02, digits, 4));
System.out.println(PApplet.nfs(m10, digits, 4) + " " +
PApplet.nfs(m11, digits, 4) + " " +
PApplet.nfs(m12, digits, 4));
System.out.println();
}
//////////////////////////////////////////////////////////////
// TODO these need to be added as regular API, but the naming and
// implementation needs to be improved first. (e.g. actually keeping track
// of whether the matrix is in fact identity internally.)
protected boolean isIdentity() {
return ((m00 == 1) && (m01 == 0) && (m02 == 0) &&
(m10 == 0) && (m11 == 1) && (m12 == 0));
}
// TODO make this more efficient, or move into PMatrix2D
protected boolean isWarped() {
return ((m00 != 1) || (m01 != 0) &&
(m10 != 0) || (m11 != 1));
}
//////////////////////////////////////////////////////////////
private final float max(float a, float b) {
return (a > b) ? a : b;
}
private final float abs(float a) {
return (a < 0) ? -a : a;
}
private final float sin(float angle) {
return (float)Math.sin(angle);
}
private final float cos(float angle) {
return (float)Math.cos(angle);
}
}

View File

@ -0,0 +1,782 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2005-08 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.core;
/**
* 4x4 matrix implementation.
*/
public final class PMatrix3D implements PMatrix /*, PConstants*/ {
public float m00, m01, m02, m03;
public float m10, m11, m12, m13;
public float m20, m21, m22, m23;
public float m30, m31, m32, m33;
// locally allocated version to avoid creating new memory
protected PMatrix3D inverseCopy;
public PMatrix3D() {
reset();
}
public PMatrix3D(float m00, float m01, float m02,
float m10, float m11, float m12) {
set(m00, m01, m02, 0,
m10, m11, m12, 0,
0, 0, 1, 0,
0, 0, 0, 1);
}
public PMatrix3D(float m00, float m01, float m02, float m03,
float m10, float m11, float m12, float m13,
float m20, float m21, float m22, float m23,
float m30, float m31, float m32, float m33) {
set(m00, m01, m02, m03,
m10, m11, m12, m13,
m20, m21, m22, m23,
m30, m31, m32, m33);
}
public PMatrix3D(PMatrix matrix) {
set(matrix);
}
public void reset() {
set(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
}
/**
* Returns a copy of this PMatrix.
*/
public PMatrix3D get() {
PMatrix3D outgoing = new PMatrix3D();
outgoing.set(this);
return outgoing;
}
/**
* Copies the matrix contents into a 16 entry float array.
* If target is null (or not the correct size), a new array will be created.
*/
public float[] get(float[] target) {
if ((target == null) || (target.length != 16)) {
target = new float[16];
}
target[0] = m00;
target[1] = m01;
target[2] = m02;
target[3] = m03;
target[4] = m10;
target[5] = m11;
target[6] = m12;
target[7] = m13;
target[8] = m20;
target[9] = m21;
target[10] = m22;
target[11] = m23;
target[12] = m30;
target[13] = m31;
target[14] = m32;
target[15] = m33;
return target;
}
public void set(PMatrix matrix) {
if (matrix instanceof PMatrix3D) {
PMatrix3D src = (PMatrix3D) matrix;
set(src.m00, src.m01, src.m02, src.m03,
src.m10, src.m11, src.m12, src.m13,
src.m20, src.m21, src.m22, src.m23,
src.m30, src.m31, src.m32, src.m33);
} else {
PMatrix2D src = (PMatrix2D) matrix;
set(src.m00, src.m01, 0, src.m02,
src.m10, src.m11, 0, src.m12,
0, 0, 1, 0,
0, 0, 0, 1);
}
}
public void set(float[] source) {
if (source.length == 6) {
set(source[0], source[1], source[2],
source[3], source[4], source[5]);
} else if (source.length == 16) {
m00 = source[0];
m01 = source[1];
m02 = source[2];
m03 = source[3];
m10 = source[4];
m11 = source[5];
m12 = source[6];
m13 = source[7];
m20 = source[8];
m21 = source[9];
m22 = source[10];
m23 = source[11];
m30 = source[12];
m31 = source[13];
m32 = source[14];
m33 = source[15];
}
}
public void set(float m00, float m01, float m02,
float m10, float m11, float m12) {
set(m00, m01, 0, m02,
m10, m11, 0, m12,
0, 0, 1, 0,
0, 0, 0, 1);
}
public void set(float m00, float m01, float m02, float m03,
float m10, float m11, float m12, float m13,
float m20, float m21, float m22, float m23,
float m30, float m31, float m32, float m33) {
this.m00 = m00; this.m01 = m01; this.m02 = m02; this.m03 = m03;
this.m10 = m10; this.m11 = m11; this.m12 = m12; this.m13 = m13;
this.m20 = m20; this.m21 = m21; this.m22 = m22; this.m23 = m23;
this.m30 = m30; this.m31 = m31; this.m32 = m32; this.m33 = m33;
}
public void translate(float tx, float ty) {
translate(tx, ty, 0);
}
// public void invTranslate(float tx, float ty) {
// invTranslate(tx, ty, 0);
// }
public void translate(float tx, float ty, float tz) {
m03 += tx*m00 + ty*m01 + tz*m02;
m13 += tx*m10 + ty*m11 + tz*m12;
m23 += tx*m20 + ty*m21 + tz*m22;
m33 += tx*m30 + ty*m31 + tz*m32;
}
public void rotate(float angle) {
rotateZ(angle);
}
public void rotateX(float angle) {
float c = cos(angle);
float s = sin(angle);
apply(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1);
}
public void rotateY(float angle) {
float c = cos(angle);
float s = sin(angle);
apply(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1);
}
public void rotateZ(float angle) {
float c = cos(angle);
float s = sin(angle);
apply(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
}
public void rotate(float angle, float v0, float v1, float v2) {
// TODO should make sure this vector is normalized
float c = cos(angle);
float s = sin(angle);
float t = 1.0f - c;
apply((t*v0*v0) + c, (t*v0*v1) - (s*v2), (t*v0*v2) + (s*v1), 0,
(t*v0*v1) + (s*v2), (t*v1*v1) + c, (t*v1*v2) - (s*v0), 0,
(t*v0*v2) - (s*v1), (t*v1*v2) + (s*v0), (t*v2*v2) + c, 0,
0, 0, 0, 1);
}
public void scale(float s) {
//apply(s, 0, 0, 0, 0, s, 0, 0, 0, 0, s, 0, 0, 0, 0, 1);
scale(s, s, s);
}
public void scale(float sx, float sy) {
//apply(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
scale(sx, sy, 1);
}
public void scale(float x, float y, float z) {
//apply(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1);
m00 *= x; m01 *= y; m02 *= z;
m10 *= x; m11 *= y; m12 *= z;
m20 *= x; m21 *= y; m22 *= z;
m30 *= x; m31 *= y; m32 *= z;
}
public void skewX(float angle) {
float t = (float) Math.tan(angle);
apply(1, t, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
}
public void skewY(float angle) {
float t = (float) Math.tan(angle);
apply(1, 0, 0, 0,
t, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
}
public void apply(PMatrix source) {
if (source instanceof PMatrix2D) {
apply((PMatrix2D) source);
} else if (source instanceof PMatrix3D) {
apply((PMatrix3D) source);
}
}
public void apply(PMatrix2D source) {
apply(source.m00, source.m01, 0, source.m02,
source.m10, source.m11, 0, source.m12,
0, 0, 1, 0,
0, 0, 0, 1);
}
public void apply(PMatrix3D source) {
apply(source.m00, source.m01, source.m02, source.m03,
source.m10, source.m11, source.m12, source.m13,
source.m20, source.m21, source.m22, source.m23,
source.m30, source.m31, source.m32, source.m33);
}
public void apply(float n00, float n01, float n02,
float n10, float n11, float n12) {
apply(n00, n01, 0, n02,
n10, n11, 0, n12,
0, 0, 1, 0,
0, 0, 0, 1);
}
public void apply(float n00, float n01, float n02, float n03,
float n10, float n11, float n12, float n13,
float n20, float n21, float n22, float n23,
float n30, float n31, float n32, float n33) {
float r00 = m00*n00 + m01*n10 + m02*n20 + m03*n30;
float r01 = m00*n01 + m01*n11 + m02*n21 + m03*n31;
float r02 = m00*n02 + m01*n12 + m02*n22 + m03*n32;
float r03 = m00*n03 + m01*n13 + m02*n23 + m03*n33;
float r10 = m10*n00 + m11*n10 + m12*n20 + m13*n30;
float r11 = m10*n01 + m11*n11 + m12*n21 + m13*n31;
float r12 = m10*n02 + m11*n12 + m12*n22 + m13*n32;
float r13 = m10*n03 + m11*n13 + m12*n23 + m13*n33;
float r20 = m20*n00 + m21*n10 + m22*n20 + m23*n30;
float r21 = m20*n01 + m21*n11 + m22*n21 + m23*n31;
float r22 = m20*n02 + m21*n12 + m22*n22 + m23*n32;
float r23 = m20*n03 + m21*n13 + m22*n23 + m23*n33;
float r30 = m30*n00 + m31*n10 + m32*n20 + m33*n30;
float r31 = m30*n01 + m31*n11 + m32*n21 + m33*n31;
float r32 = m30*n02 + m31*n12 + m32*n22 + m33*n32;
float r33 = m30*n03 + m31*n13 + m32*n23 + m33*n33;
m00 = r00; m01 = r01; m02 = r02; m03 = r03;
m10 = r10; m11 = r11; m12 = r12; m13 = r13;
m20 = r20; m21 = r21; m22 = r22; m23 = r23;
m30 = r30; m31 = r31; m32 = r32; m33 = r33;
}
public void preApply(PMatrix2D left) {
preApply(left.m00, left.m01, 0, left.m02,
left.m10, left.m11, 0, left.m12,
0, 0, 1, 0,
0, 0, 0, 1);
}
/**
* Apply another matrix to the left of this one.
*/
public void preApply(PMatrix3D left) {
preApply(left.m00, left.m01, left.m02, left.m03,
left.m10, left.m11, left.m12, left.m13,
left.m20, left.m21, left.m22, left.m23,
left.m30, left.m31, left.m32, left.m33);
}
public void preApply(float n00, float n01, float n02,
float n10, float n11, float n12) {
preApply(n00, n01, 0, n02,
n10, n11, 0, n12,
0, 0, 1, 0,
0, 0, 0, 1);
}
public void preApply(float n00, float n01, float n02, float n03,
float n10, float n11, float n12, float n13,
float n20, float n21, float n22, float n23,
float n30, float n31, float n32, float n33) {
float r00 = n00*m00 + n01*m10 + n02*m20 + n03*m30;
float r01 = n00*m01 + n01*m11 + n02*m21 + n03*m31;
float r02 = n00*m02 + n01*m12 + n02*m22 + n03*m32;
float r03 = n00*m03 + n01*m13 + n02*m23 + n03*m33;
float r10 = n10*m00 + n11*m10 + n12*m20 + n13*m30;
float r11 = n10*m01 + n11*m11 + n12*m21 + n13*m31;
float r12 = n10*m02 + n11*m12 + n12*m22 + n13*m32;
float r13 = n10*m03 + n11*m13 + n12*m23 + n13*m33;
float r20 = n20*m00 + n21*m10 + n22*m20 + n23*m30;
float r21 = n20*m01 + n21*m11 + n22*m21 + n23*m31;
float r22 = n20*m02 + n21*m12 + n22*m22 + n23*m32;
float r23 = n20*m03 + n21*m13 + n22*m23 + n23*m33;
float r30 = n30*m00 + n31*m10 + n32*m20 + n33*m30;
float r31 = n30*m01 + n31*m11 + n32*m21 + n33*m31;
float r32 = n30*m02 + n31*m12 + n32*m22 + n33*m32;
float r33 = n30*m03 + n31*m13 + n32*m23 + n33*m33;
m00 = r00; m01 = r01; m02 = r02; m03 = r03;
m10 = r10; m11 = r11; m12 = r12; m13 = r13;
m20 = r20; m21 = r21; m22 = r22; m23 = r23;
m30 = r30; m31 = r31; m32 = r32; m33 = r33;
}
//////////////////////////////////////////////////////////////
public PVector mult(PVector source, PVector target) {
if (target == null) {
target = new PVector();
}
target.x = m00*source.x + m01*source.y + m02*source.z + m03;
target.y = m10*source.x + m11*source.y + m12*source.z + m13;
target.z = m20*source.x + m21*source.y + m22*source.z + m23;
// float tw = m30*source.x + m31*source.y + m32*source.z + m33;
// if (tw != 0 && tw != 1) {
// target.div(tw);
// }
return target;
}
/*
public PVector cmult(PVector source, PVector target) {
if (target == null) {
target = new PVector();
}
target.x = m00*source.x + m10*source.y + m20*source.z + m30;
target.y = m01*source.x + m11*source.y + m21*source.z + m31;
target.z = m02*source.x + m12*source.y + m22*source.z + m32;
float tw = m03*source.x + m13*source.y + m23*source.z + m33;
if (tw != 0 && tw != 1) {
target.div(tw);
}
return target;
}
*/
/**
* Multiply a three or four element vector against this matrix. If out is
* null or not length 3 or 4, a new float array (length 3) will be returned.
*/
public float[] mult(float[] source, float[] target) {
if (target == null || target.length < 3) {
target = new float[3];
}
if (source == target) {
throw new RuntimeException("The source and target vectors used in " +
"PMatrix3D.mult() cannot be identical.");
}
if (target.length == 3) {
target[0] = m00*source[0] + m01*source[1] + m02*source[2] + m03;
target[1] = m10*source[0] + m11*source[1] + m12*source[2] + m13;
target[2] = m20*source[0] + m21*source[1] + m22*source[2] + m23;
//float w = m30*source[0] + m31*source[1] + m32*source[2] + m33;
//if (w != 0 && w != 1) {
// target[0] /= w; target[1] /= w; target[2] /= w;
//}
} else if (target.length > 3) {
target[0] = m00*source[0] + m01*source[1] + m02*source[2] + m03*source[3];
target[1] = m10*source[0] + m11*source[1] + m12*source[2] + m13*source[3];
target[2] = m20*source[0] + m21*source[1] + m22*source[2] + m23*source[3];
target[3] = m30*source[0] + m31*source[1] + m32*source[2] + m33*source[3];
}
return target;
}
public float multX(float x, float y) {
return m00*x + m01*y + m03;
}
public float multY(float x, float y) {
return m10*x + m11*y + m13;
}
public float multX(float x, float y, float z) {
return m00*x + m01*y + m02*z + m03;
}
public float multY(float x, float y, float z) {
return m10*x + m11*y + m12*z + m13;
}
public float multZ(float x, float y, float z) {
return m20*x + m21*y + m22*z + m23;
}
public float multW(float x, float y, float z) {
return m30*x + m31*y + m32*z + m33;
}
public float multX(float x, float y, float z, float w) {
return m00*x + m01*y + m02*z + m03*w;
}
public float multY(float x, float y, float z, float w) {
return m10*x + m11*y + m12*z + m13*w;
}
public float multZ(float x, float y, float z, float w) {
return m20*x + m21*y + m22*z + m23*w;
}
public float multW(float x, float y, float z, float w) {
return m30*x + m31*y + m32*z + m33*w;
}
/**
* Transpose this matrix.
*/
public void transpose() {
float temp;
temp = m01; m01 = m10; m10 = temp;
temp = m02; m02 = m20; m20 = temp;
temp = m03; m03 = m30; m30 = temp;
temp = m12; m12 = m21; m21 = temp;
temp = m13; m13 = m31; m31 = temp;
temp = m23; m23 = m32; m32 = temp;
}
/**
* Invert this matrix.
* @return true if successful
*/
public boolean invert() {
float determinant = determinant();
if (determinant == 0) {
return false;
}
// first row
float t00 = determinant3x3(m11, m12, m13, m21, m22, m23, m31, m32, m33);
float t01 = -determinant3x3(m10, m12, m13, m20, m22, m23, m30, m32, m33);
float t02 = determinant3x3(m10, m11, m13, m20, m21, m23, m30, m31, m33);
float t03 = -determinant3x3(m10, m11, m12, m20, m21, m22, m30, m31, m32);
// second row
float t10 = -determinant3x3(m01, m02, m03, m21, m22, m23, m31, m32, m33);
float t11 = determinant3x3(m00, m02, m03, m20, m22, m23, m30, m32, m33);
float t12 = -determinant3x3(m00, m01, m03, m20, m21, m23, m30, m31, m33);
float t13 = determinant3x3(m00, m01, m02, m20, m21, m22, m30, m31, m32);
// third row
float t20 = determinant3x3(m01, m02, m03, m11, m12, m13, m31, m32, m33);
float t21 = -determinant3x3(m00, m02, m03, m10, m12, m13, m30, m32, m33);
float t22 = determinant3x3(m00, m01, m03, m10, m11, m13, m30, m31, m33);
float t23 = -determinant3x3(m00, m01, m02, m10, m11, m12, m30, m31, m32);
// fourth row
float t30 = -determinant3x3(m01, m02, m03, m11, m12, m13, m21, m22, m23);
float t31 = determinant3x3(m00, m02, m03, m10, m12, m13, m20, m22, m23);
float t32 = -determinant3x3(m00, m01, m03, m10, m11, m13, m20, m21, m23);
float t33 = determinant3x3(m00, m01, m02, m10, m11, m12, m20, m21, m22);
// transpose and divide by the determinant
m00 = t00 / determinant;
m01 = t10 / determinant;
m02 = t20 / determinant;
m03 = t30 / determinant;
m10 = t01 / determinant;
m11 = t11 / determinant;
m12 = t21 / determinant;
m13 = t31 / determinant;
m20 = t02 / determinant;
m21 = t12 / determinant;
m22 = t22 / determinant;
m23 = t32 / determinant;
m30 = t03 / determinant;
m31 = t13 / determinant;
m32 = t23 / determinant;
m33 = t33 / determinant;
return true;
}
/**
* Calculate the determinant of a 3x3 matrix.
* @return result
*/
private float determinant3x3(float t00, float t01, float t02,
float t10, float t11, float t12,
float t20, float t21, float t22) {
return (t00 * (t11 * t22 - t12 * t21) +
t01 * (t12 * t20 - t10 * t22) +
t02 * (t10 * t21 - t11 * t20));
}
/**
* @return the determinant of the matrix
*/
public float determinant() {
float f =
m00
* ((m11 * m22 * m33 + m12 * m23 * m31 + m13 * m21 * m32)
- m13 * m22 * m31
- m11 * m23 * m32
- m12 * m21 * m33);
f -= m01
* ((m10 * m22 * m33 + m12 * m23 * m30 + m13 * m20 * m32)
- m13 * m22 * m30
- m10 * m23 * m32
- m12 * m20 * m33);
f += m02
* ((m10 * m21 * m33 + m11 * m23 * m30 + m13 * m20 * m31)
- m13 * m21 * m30
- m10 * m23 * m31
- m11 * m20 * m33);
f -= m03
* ((m10 * m21 * m32 + m11 * m22 * m30 + m12 * m20 * m31)
- m12 * m21 * m30
- m10 * m22 * m31
- m11 * m20 * m32);
return f;
}
//////////////////////////////////////////////////////////////
// REVERSE VERSIONS OF MATRIX OPERATIONS
// These functions should not be used, as they will be removed in the future.
protected void invTranslate(float tx, float ty, float tz) {
preApply(1, 0, 0, -tx,
0, 1, 0, -ty,
0, 0, 1, -tz,
0, 0, 0, 1);
}
protected void invRotateX(float angle) {
float c = cos(-angle);
float s = sin(-angle);
preApply(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1);
}
protected void invRotateY(float angle) {
float c = cos(-angle);
float s = sin(-angle);
preApply(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1);
}
protected void invRotateZ(float angle) {
float c = cos(-angle);
float s = sin(-angle);
preApply(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
}
protected void invRotate(float angle, float v0, float v1, float v2) {
//TODO should make sure this vector is normalized
float c = cos(-angle);
float s = sin(-angle);
float t = 1.0f - c;
preApply((t*v0*v0) + c, (t*v0*v1) - (s*v2), (t*v0*v2) + (s*v1), 0,
(t*v0*v1) + (s*v2), (t*v1*v1) + c, (t*v1*v2) - (s*v0), 0,
(t*v0*v2) - (s*v1), (t*v1*v2) + (s*v0), (t*v2*v2) + c, 0,
0, 0, 0, 1);
}
protected void invScale(float x, float y, float z) {
preApply(1/x, 0, 0, 0, 0, 1/y, 0, 0, 0, 0, 1/z, 0, 0, 0, 0, 1);
}
protected boolean invApply(float n00, float n01, float n02, float n03,
float n10, float n11, float n12, float n13,
float n20, float n21, float n22, float n23,
float n30, float n31, float n32, float n33) {
if (inverseCopy == null) {
inverseCopy = new PMatrix3D();
}
inverseCopy.set(n00, n01, n02, n03,
n10, n11, n12, n13,
n20, n21, n22, n23,
n30, n31, n32, n33);
if (!inverseCopy.invert()) {
return false;
}
preApply(inverseCopy);
return true;
}
//////////////////////////////////////////////////////////////
public void print() {
/*
System.out.println(m00 + " " + m01 + " " + m02 + " " + m03 + "\n" +
m10 + " " + m11 + " " + m12 + " " + m13 + "\n" +
m20 + " " + m21 + " " + m22 + " " + m23 + "\n" +
m30 + " " + m31 + " " + m32 + " " + m33 + "\n");
*/
int big = (int) Math.abs(max(max(max(max(abs(m00), abs(m01)),
max(abs(m02), abs(m03))),
max(max(abs(m10), abs(m11)),
max(abs(m12), abs(m13)))),
max(max(max(abs(m20), abs(m21)),
max(abs(m22), abs(m23))),
max(max(abs(m30), abs(m31)),
max(abs(m32), abs(m33))))));
int digits = 1;
if (Float.isNaN(big) || Float.isInfinite(big)) { // avoid infinite loop
digits = 5;
} else {
while ((big /= 10) != 0) digits++; // cheap log()
}
System.out.println(PApplet.nfs(m00, digits, 4) + " " +
PApplet.nfs(m01, digits, 4) + " " +
PApplet.nfs(m02, digits, 4) + " " +
PApplet.nfs(m03, digits, 4));
System.out.println(PApplet.nfs(m10, digits, 4) + " " +
PApplet.nfs(m11, digits, 4) + " " +
PApplet.nfs(m12, digits, 4) + " " +
PApplet.nfs(m13, digits, 4));
System.out.println(PApplet.nfs(m20, digits, 4) + " " +
PApplet.nfs(m21, digits, 4) + " " +
PApplet.nfs(m22, digits, 4) + " " +
PApplet.nfs(m23, digits, 4));
System.out.println(PApplet.nfs(m30, digits, 4) + " " +
PApplet.nfs(m31, digits, 4) + " " +
PApplet.nfs(m32, digits, 4) + " " +
PApplet.nfs(m33, digits, 4));
System.out.println();
}
//////////////////////////////////////////////////////////////
private final float max(float a, float b) {
return (a > b) ? a : b;
}
private final float abs(float a) {
return (a < 0) ? -a : a;
}
private final float sin(float angle) {
return (float) Math.sin(angle);
}
private final float cos(float angle) {
return (float) Math.cos(angle);
}
}

View File

@ -0,0 +1,701 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-08 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.core;
/**
* Z-buffer polygon rendering object used by PGraphics2D.
*/
public class PPolygon implements PConstants {
static final int DEFAULT_SIZE = 64; // this is needed for spheres
float vertices[][] = new float[DEFAULT_SIZE][VERTEX_FIELD_COUNT];
int vertexCount;
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];
protected boolean interpX;
protected boolean interpUV; // is this necessary? could just check timage != null
protected boolean interpARGB;
private int rgba;
private int r2, g2, b2, a2, a2orig;
PGraphics parent;
int[] pixels;
// the parent's width/height,
// or if smooth is enabled, parent's w/h scaled
// up by the smooth dimension
int width, height;
int width1, height1;
PImage timage;
int[] tpixels;
int theight, twidth;
int theight1, twidth1;
int tformat;
// for anti-aliasing
static final int SUBXRES = 8;
static final int SUBXRES1 = 7;
static final int SUBYRES = 8;
static final int SUBYRES1 = 7;
static final int MAX_COVERAGE = SUBXRES * SUBYRES;
boolean smooth;
int firstModY;
int lastModY;
int lastY;
int aaleft[] = new int[SUBYRES];
int aaright[] = new int[SUBYRES];
int aaleftmin, aarightmin;
int aaleftmax, aarightmax;
int aaleftfull, aarightfull;
final private int MODYRES(int y) {
return (y & SUBYRES1);
}
public PPolygon(PGraphics iparent) {
parent = iparent;
reset(0);
}
protected void reset(int count) {
vertexCount = count;
interpX = true;
// interpZ = true;
interpUV = false;
interpARGB = true;
timage = null;
}
protected float[] nextVertex() {
if (vertexCount == vertices.length) {
float temp[][] = new float[vertexCount<<1][VERTEX_FIELD_COUNT];
System.arraycopy(vertices, 0, temp, 0, vertexCount);
vertices = temp;
r = new float[vertices.length];
dr = new float[vertices.length];
l = new float[vertices.length];
dl = new float[vertices.length];
sp = new float[vertices.length];
sdp = new float[vertices.length];
}
return vertices[vertexCount++]; // returns v[0], sets vc to 1
}
/**
* Return true if this vertex is redundant. If so, will also
* decrement the vertex count.
*/
/*
public boolean redundantVertex(float x, float y, float z) {
// because vertexCount will be 2 when setting vertex[1]
if (vertexCount < 2) return false;
// vertexCount-1 is the current vertex that would be used
// vertexCount-2 would be the previous feller
if ((Math.abs(vertices[vertexCount-2][MX] - x) < EPSILON) &&
(Math.abs(vertices[vertexCount-2][MY] - y) < EPSILON) &&
(Math.abs(vertices[vertexCount-2][MZ] - z) < EPSILON)) {
vertexCount--;
return true;
}
return false;
}
*/
protected void texture(PImage image) {
this.timage = image;
if (image != null) {
this.tpixels = image.pixels;
this.twidth = image.width;
this.theight = image.height;
this.tformat = image.format;
twidth1 = twidth - 1;
theight1 = theight - 1;
interpUV = true;
} else {
interpUV = false;
}
}
protected void renderPolygon(float[][] v, int count) {
vertices = v;
vertexCount = count;
if (r.length < vertexCount) {
r = new float[vertexCount]; // storage used by incrementalize
dr = new float[vertexCount];
l = new float[vertexCount]; // more storage for incrementalize
dl = new float[vertexCount];
sp = new float[vertexCount]; // temporary storage for scanline
sdp = new float[vertexCount];
}
render();
checkExpand();
}
protected void renderTriangle(float[] v1, float[] v2, float[] v3) {
// Calling code will have already done reset(3).
// Can't do it here otherwise would nuke any texture settings.
vertices[0] = v1;
vertices[1] = v2;
vertices[2] = v3;
render();
checkExpand();
}
protected void checkExpand() {
if (smooth) {
for (int i = 0; i < vertexCount; i++) {
vertices[i][TX] /= SUBXRES;
vertices[i][TY] /= SUBYRES;
}
}
}
protected void render() {
if (vertexCount < 3) return;
// these may have changed due to a resize()
// so they should be refreshed here
pixels = parent.pixels;
//zbuffer = parent.zbuffer;
// noDepthTest = parent.hints[DISABLE_DEPTH_TEST];
smooth = parent.smooth;
// by default, text turns on smooth for the textures
// themselves. but this should be shut off if the hint
// for DISABLE_TEXT_SMOOTH is set.
// texture_smooth = true;
width = smooth ? parent.width*SUBXRES : parent.width;
height = smooth ? parent.height*SUBYRES : parent.height;
width1 = width - 1;
height1 = height - 1;
if (!interpARGB) {
r2 = (int) (vertices[0][R] * 255);
g2 = (int) (vertices[0][G] * 255);
b2 = (int) (vertices[0][B] * 255);
a2 = (int) (vertices[0][A] * 255);
a2orig = a2; // save an extra copy
rgba = 0xff000000 | (r2 << 16) | (g2 << 8) | b2;
}
for (int i = 0; i < vertexCount; i++) {
r[i] = 0; dr[i] = 0; l[i] = 0; dl[i] = 0;
}
/*
// hack to not make polygons fly into the screen
if (parent.hints[DISABLE_FLYING_POO]) {
float nwidth2 = -width * 2;
float nheight2 = -height * 2;
float width2 = width * 2;
float height2 = height * 2;
for (int i = 0; i < vertexCount; i++) {
if ((vertices[i][TX] < nwidth2) ||
(vertices[i][TX] > width2) ||
(vertices[i][TY] < nheight2) ||
(vertices[i][TY] > height2)) {
return; // this is a bad poly
}
}
}
*/
// for (int i = 0; i < 4; i++) {
// System.out.println(vertices[i][R] + " " + vertices[i][G] + " " + vertices[i][B]);
// }
// System.out.println();
if (smooth) {
for (int i = 0; i < vertexCount; i++) {
vertices[i][TX] *= SUBXRES;
vertices[i][TY] *= SUBYRES;
}
firstModY = -1;
}
// find top vertex (y is zero at top, higher downwards)
int topi = 0;
float ymin = vertices[0][TY];
float ymax = vertices[0][TY]; // fry 031001
for (int i = 1; i < vertexCount; i++) {
if (vertices[i][TY] < ymin) {
ymin = vertices[i][TY];
topi = i;
}
if (vertices[i][TY] > ymax) {
ymax = vertices[i][TY];
}
}
// 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);
incrementalizeY(vertices[lefti], vertices[i], l, dl, y);
lefty = (int) (vertices[i][TY] + 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;
incrementalizeY(vertices[righti], vertices[i], r, dr, y);
righty = (int) (vertices[i][TY] + 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[TX] <= r[TX]) scanline(y, l, r);
else scanline(y, r, l);
//} catch (ArrayIndexOutOfBoundsException e) {
//e.printStackTrace();
//}
}
y++;
// this increment probably needs to be different
// UV and RGB shouldn't be incremented until line is emitted
increment(l, dl);
increment(r, dr);
}
}
//if (smooth) {
//System.out.println("y/lasty/lastmody = " + y + " " + lastY + " " + lastModY);
//}
}
private void scanline(int y, float l[], float r[]) {
//System.out.println("scanline " + y);
for (int i = 0; i < vertexCount; i++) { // should be moved later
sp[i] = 0; sdp[i] = 0;
}
// this rounding doesn't seem to be relevant with smooth
int lx = (int) (l[TX] + 0.49999f); // ceil(l[TX]-.5);
if (lx < 0) lx = 0;
int rx = (int) (r[TX] - 0.5f);
if (rx > width1) rx = width1;
if (lx > rx) return;
if (smooth) {
int mody = MODYRES(y);
aaleft[mody] = lx;
aaright[mody] = rx;
if (firstModY == -1) {
firstModY = mody;
aaleftmin = lx; aaleftmax = lx;
aarightmin = rx; aarightmax = rx;
} else {
if (aaleftmin > aaleft[mody]) aaleftmin = aaleft[mody];
if (aaleftmax < aaleft[mody]) aaleftmax = aaleft[mody];
if (aarightmin > aaright[mody]) aarightmin = aaright[mody];
if (aarightmax < aaright[mody]) aarightmax = aaright[mody];
}
lastModY = mody; // moved up here (before the return) 031001
// not the eighth (or lastY) line, so not scanning this time
if ((mody != SUBYRES1) && (y != lastY)) return;
//lastModY = mody; // eeK! this was missing
//return;
//if (y == lastY) {
//System.out.println("y is lasty");
//}
//lastModY = mody;
aaleftfull = aaleftmax/SUBXRES + 1;
aarightfull = aarightmin/SUBXRES - 1;
}
// this is the setup, based on lx
incrementalizeX(l, r, sp, sdp, lx);
// scan in x, generating pixels
// using parent.width to get actual pixel index
// rather than scaled by smooth factor
int offset = smooth ? parent.width * (y / SUBYRES) : parent.width*y;
int truelx = 0, truerx = 0;
if (smooth) {
truelx = lx / SUBXRES;
truerx = (rx + SUBXRES1) / SUBXRES;
lx = aaleftmin / SUBXRES;
rx = (aarightmax + SUBXRES1) / SUBXRES;
if (lx < 0) lx = 0;
if (rx > parent.width1) rx = parent.width1;
}
interpX = false;
int tr, tg, tb, ta;
// System.out.println("P2D interp uv " + interpUV + " " +
// vertices[2][U] + " " + vertices[2][V]);
for (int x = lx; x <= rx; x++) {
// map texture based on U, V coords in sp[U] and sp[V]
if (interpUV) {
int tu = (int) (sp[U] * twidth);
int tv = (int) (sp[V] * theight);
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;
int tuf1 = (int) (255f * (sp[U]*twidth - (float)tu));
int tvf1 = (int) (255f * (sp[V]*theight - (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;
// calculate alpha component (ta)
if (tformat == ALPHA) {
px0 = (pixel00*tuf + pixel10*tuf1) >> 8;
px1 = (pixel01*tuf + pixel11*tuf1) >> 8;
ta = (((px0*tvf + px1*tvf1) >> 8) *
(interpARGB ? ((int) (sp[A]*255)) : a2orig)) >> 8;
} else if (tformat == ARGB) {
p00 = (pixel00 >> 24) & 0xff;
p01 = (pixel01 >> 24) & 0xff;
p10 = (pixel10 >> 24) & 0xff;
p11 = (pixel11 >> 24) & 0xff;
px0 = (p00*tuf + p10*tuf1) >> 8;
px1 = (p01*tuf + p11*tuf1) >> 8;
ta = (((px0*tvf + px1*tvf1) >> 8) *
(interpARGB ? ((int) (sp[A]*255)) : a2orig)) >> 8;
} else { // RGB image, no alpha
ta = interpARGB ? ((int) (sp[A]*255)) : a2orig;
}
// calculate r,g,b components (tr, tg, tb)
if ((tformat == RGB) || (tformat == ARGB)) {
p00 = (pixel00 >> 16) & 0xff; // red
p01 = (pixel01 >> 16) & 0xff;
p10 = (pixel10 >> 16) & 0xff;
p11 = (pixel11 >> 16) & 0xff;
px0 = (p00*tuf + p10*tuf1) >> 8;
px1 = (p01*tuf + p11*tuf1) >> 8;
tr = (((px0*tvf + px1*tvf1) >> 8) *
(interpARGB ? ((int) (sp[R]*255)) : r2)) >> 8;
p00 = (pixel00 >> 8) & 0xff; // green
p01 = (pixel01 >> 8) & 0xff;
p10 = (pixel10 >> 8) & 0xff;
p11 = (pixel11 >> 8) & 0xff;
px0 = (p00*tuf + p10*tuf1) >> 8;
px1 = (p01*tuf + p11*tuf1) >> 8;
tg = (((px0*tvf + px1*tvf1) >> 8) *
(interpARGB ? ((int) (sp[G]*255)) : g2)) >> 8;
p00 = pixel00 & 0xff; // blue
p01 = pixel01 & 0xff;
p10 = pixel10 & 0xff;
p11 = pixel11 & 0xff;
px0 = (p00*tuf + p10*tuf1) >> 8;
px1 = (p01*tuf + p11*tuf1) >> 8;
tb = (((px0*tvf + px1*tvf1) >> 8) *
(interpARGB ? ((int) (sp[B]*255)) : b2)) >> 8;
} else { // alpha image, only use current fill color
if (interpARGB) {
tr = (int) (sp[R] * 255);
tg = (int) (sp[G] * 255);
tb = (int) (sp[B] * 255);
} else {
tr = r2;
tg = g2;
tb = b2;
}
}
int weight = smooth ? coverage(x) : 255;
if (weight != 255) ta = ta*weight >> 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;
} 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);
}
} else { // no image applied
int weight = smooth ? coverage(x) : 255;
if (interpARGB) {
r2 = (int) (sp[R] * 255);
g2 = (int) (sp[G] * 255);
b2 = (int) (sp[B] * 255);
if (sp[A] != 1) weight = (weight * ((int) (sp[A] * 255))) >> 8;
if (weight == 255) {
rgba = 0xff000000 | (r2 << 16) | (g2 << 8) | b2;
}
} else {
if (a2orig != 255) weight = (weight * a2orig) >> 8;
}
if (weight == 255) {
// no blend, no aa, just the rgba
pixels[offset+x] = rgba;
//zbuffer[offset+x] = sp[Z];
} else {
int r1 = (pixels[offset+x] >> 16) & 0xff;
int g1 = (pixels[offset+x] >> 8) & 0xff;
int b1 = (pixels[offset+x]) & 0xff;
a2 = weight;
int a1 = 255 - a2;
pixels[offset+x] = (0xff000000 |
((r1*a1 + r2*a2) >> 8) << 16 |
// use & instead of >> and << below
((g1*a1 + g2*a2) >> 8) << 8 |
((b1*a1 + b2*a2) >> 8));
}
}
// if smooth enabled, don't increment values
// for the pixel in the stretch out version
// of the scanline used to get smooth edges.
if (!smooth || ((x >= truelx) && (x <= truerx))) {
increment(sp, sdp);
}
}
firstModY = -1;
interpX = true;
}
// x is in screen, not huge 8x coordinates
private int coverage(int x) {
if ((x >= aaleftfull) && (x <= aarightfull) &&
// important since not all SUBYRES lines may have been covered
(firstModY == 0) && (lastModY == SUBYRES1)) {
return 255;
}
int pixelLeft = x*SUBXRES; // huh?
int pixelRight = pixelLeft + 8;
int amt = 0;
for (int i = firstModY; i <= lastModY; i++) {
if ((aaleft[i] > pixelRight) || (aaright[i] < pixelLeft)) {
continue;
}
// does this need a +1 ?
amt += ((aaright[i] < pixelRight ? aaright[i] : pixelRight) -
(aaleft[i] > pixelLeft ? aaleft[i] : pixelLeft));
}
amt <<= 2;
return (amt == 256) ? 255 : amt;
}
private void incrementalizeY(float p1[], float p2[],
float p[], float dp[], int y) {
float delta = p2[TY] - p1[TY];
if (delta == 0) delta = 1;
float fraction = y + 0.5f - p1[TY];
if (interpX) {
dp[TX] = (p2[TX] - p1[TX]) / delta;
p[TX] = p1[TX] + dp[TX] * fraction;
}
if (interpARGB) {
dp[R] = (p2[R] - p1[R]) / delta;
dp[G] = (p2[G] - p1[G]) / delta;
dp[B] = (p2[B] - p1[B]) / delta;
dp[A] = (p2[A] - p1[A]) / delta;
p[R] = p1[R] + dp[R] * fraction;
p[G] = p1[G] + dp[G] * fraction;
p[B] = p1[B] + dp[B] * fraction;
p[A] = p1[A] + dp[A] * fraction;
}
if (interpUV) {
dp[U] = (p2[U] - p1[U]) / delta;
dp[V] = (p2[V] - p1[V]) / delta;
p[U] = p1[U] + dp[U] * fraction;
p[V] = p1[V] + dp[V] * fraction;
}
}
private void incrementalizeX(float p1[], float p2[],
float p[], float dp[], int x) {
float delta = p2[TX] - p1[TX];
if (delta == 0) delta = 1;
float fraction = x + 0.5f - p1[TX];
if (smooth) {
delta /= SUBXRES;
fraction /= SUBXRES;
}
if (interpX) {
dp[TX] = (p2[TX] - p1[TX]) / delta;
p[TX] = p1[TX] + dp[TX] * fraction;
}
if (interpARGB) {
dp[R] = (p2[R] - p1[R]) / delta;
dp[G] = (p2[G] - p1[G]) / delta;
dp[B] = (p2[B] - p1[B]) / delta;
dp[A] = (p2[A] - p1[A]) / delta;
p[R] = p1[R] + dp[R] * fraction;
p[G] = p1[G] + dp[G] * fraction;
p[B] = p1[B] + dp[B] * fraction;
p[A] = p1[A] + dp[A] * fraction;
}
if (interpUV) {
dp[U] = (p2[U] - p1[U]) / delta;
dp[V] = (p2[V] - p1[V]) / delta;
p[U] = p1[U] + dp[U] * fraction;
p[V] = p1[V] + dp[V] * fraction;
}
}
private void increment(float p[], float dp[]) {
if (interpX) p[TX] += dp[TX];
if (interpARGB) {
p[R] += dp[R];
p[G] += dp[G];
p[B] += dp[B];
p[A] += dp[A];
}
if (interpUV) {
p[U] += dp[U];
p[V] += dp[V];
}
}
}

View File

@ -0,0 +1,842 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2006-08 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.core;
import java.util.HashMap;
/**
* In-progress class to handle shape data, currently to be considered of
* alpha or beta quality. Major structural work may be performed on this class
* after the release of Processing 1.0. Such changes may include:
*
* <ul>
* <li> addition of proper accessors to read shape vertex and coloring data
* (this is the second most important part of having a PShape class after all).
* <li> a means of creating PShape objects ala beginShape() and endShape().
* <li> load(), update(), and cache methods ala PImage, so that shapes can
* have renderer-specific optimizations, such as vertex arrays in OpenGL.
* <li> splitting this class into multiple classes to handle different
* varieties of shape data (primitives vs collections of vertices vs paths)
* <li> change of package declaration, for instance moving the code into
* package processing.shape (if the code grows too much).
* </ul>
*
* <p>For the time being, this class and its shape() and loadShape() friends in
* PApplet exist as placeholders for more exciting things to come. If you'd
* like to work with this class, make a subclass (see how PShapeSVG works)
* and you can play with its internal methods all you like.</p>
*
* <p>Library developers are encouraged to create PShape objects when loading
* shape data, so that they can eventually hook into the bounty that will be
* the PShape interface, and the ease of loadShape() and shape().</p>
*/
public class PShape implements PConstants {
protected String name;
protected HashMap<String,PShape> nameTable;
/** Generic, only draws its child objects. */
static public final int GROUP = 0;
/** A line, ellipse, arc, image, etc. */
static public final int PRIMITIVE = 1;
/** A series of vertex, curveVertex, and bezierVertex calls. */
static public final int PATH = 2;
/** Collections of vertices created with beginShape(). */
static public final int GEOMETRY = 3;
/** The shape type, one of GROUP, PRIMITIVE, PATH, or GEOMETRY. */
protected int family;
/** ELLIPSE, LINE, QUAD; TRIANGLE_FAN, QUAD_STRIP; etc. */
protected int kind;
protected PMatrix matrix;
/** Texture or image data associated with this shape. */
protected PImage image;
// boundary box of this shape
//protected float x;
//protected float y;
//protected float width;
//protected float height;
public float width;
public float height;
// set to false if the object is hidden in the layers palette
protected boolean visible = true;
protected boolean stroke;
protected int strokeColor;
protected float strokeWeight; // default is 1
protected int strokeCap;
protected int strokeJoin;
protected boolean fill;
protected int fillColor;
/** Temporary toggle for whether styles should be honored. */
protected boolean style = true;
/** For primitive shapes in particular, parms like x/y/w/h or x1/y1/x2/y2. */
protected float[] params;
protected int vertexCount;
/**
* When drawing POLYGON shapes, the second param is an array of length
* VERTEX_FIELD_COUNT. When drawing PATH shapes, the second param has only
* two variables.
*/
protected float[][] vertices;
static public final int VERTEX = 0;
static public final int BEZIER_VERTEX = 1;
static public final int CURVE_VERTEX = 2;
static public final int BREAK = 3;
/** Array of VERTEX, BEZIER_VERTEX, and CURVE_VERTEXT calls. */
protected int vertexCodeCount;
protected int[] vertexCodes;
/** True if this is a closed path. */
protected boolean close;
// should this be called vertices (consistent with PGraphics internals)
// or does that hurt flexibility?
protected PShape parent;
protected int childCount;
protected PShape[] children;
// POINTS, LINES, xLINE_STRIP, xLINE_LOOP
// TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN
// QUADS, QUAD_STRIP
// xPOLYGON
// static final int PATH = 1; // POLYGON, LINE_LOOP, LINE_STRIP
// static final int GROUP = 2;
// how to handle rectmode/ellipsemode?
// are they bitshifted into the constant?
// CORNER, CORNERS, CENTER, (CENTER_RADIUS?)
// static final int RECT = 3; // could just be QUAD, but would be x1/y1/x2/y2
// static final int ELLIPSE = 4;
//
// static final int VERTEX = 7;
// static final int CURVE = 5;
// static final int BEZIER = 6;
// fill and stroke functions will need a pointer to the parent
// PGraphics object.. may need some kind of createShape() fxn
// or maybe the values are stored until draw() is called?
// attaching images is very tricky.. it's a different type of data
// material parameters will be thrown out,
// except those currently supported (kinds of lights)
// pivot point for transformations
// public float px;
// public float py;
public PShape() {
this.family = GROUP;
}
public PShape(int family) {
this.family = family;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
/**
* Overrides this shape's style information and uses PGraphics styles and
* colors. Identical to ignoreStyles(true). Also disables styles for all
* child shapes.
*/
public void disableStyle() {
style = false;
for (int i = 0; i < childCount; i++) {
children[i].disableStyle();
}
}
/**
* Re-enables style information (fill and stroke) set in the shape.
*/
public void enableStyle() {
style = true;
for (int i = 0; i < childCount; i++) {
children[i].enableStyle();
}
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// protected void checkBounds() {
// if (width == 0 || height == 0) {
// // calculate bounds here (also take kids into account)
// width = 1;
// height = 1;
// }
// }
/**
* Get the width of the drawing area (not necessarily the shape boundary).
*/
public float getWidth() {
//checkBounds();
return width;
}
/**
* Get the height of the drawing area (not necessarily the shape boundary).
*/
public float getHeight() {
//checkBounds();
return height;
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
/*
boolean strokeSaved;
int strokeColorSaved;
float strokeWeightSaved;
int strokeCapSaved;
int strokeJoinSaved;
boolean fillSaved;
int fillColorSaved;
int rectModeSaved;
int ellipseModeSaved;
int shapeModeSaved;
*/
protected void pre(PGraphics g) {
if (matrix != null) {
g.pushMatrix();
g.applyMatrix(matrix);
}
/*
strokeSaved = g.stroke;
strokeColorSaved = g.strokeColor;
strokeWeightSaved = g.strokeWeight;
strokeCapSaved = g.strokeCap;
strokeJoinSaved = g.strokeJoin;
fillSaved = g.fill;
fillColorSaved = g.fillColor;
rectModeSaved = g.rectMode;
ellipseModeSaved = g.ellipseMode;
shapeModeSaved = g.shapeMode;
*/
if (style) {
g.pushStyle();
styles(g);
}
}
protected void styles(PGraphics g) {
// should not be necessary because using only the int version of color
//parent.colorMode(PConstants.RGB, 255);
if (stroke) {
g.stroke(strokeColor);
g.strokeWeight(strokeWeight);
g.strokeCap(strokeCap);
g.strokeJoin(strokeJoin);
} else {
g.noStroke();
}
if (fill) {
//System.out.println("filling " + PApplet.hex(fillColor));
g.fill(fillColor);
} else {
g.noFill();
}
}
public void post(PGraphics g) {
// for (int i = 0; i < childCount; i++) {
// children[i].draw(g);
// }
/*
// TODO this is not sufficient, since not saving fillR et al.
g.stroke = strokeSaved;
g.strokeColor = strokeColorSaved;
g.strokeWeight = strokeWeightSaved;
g.strokeCap = strokeCapSaved;
g.strokeJoin = strokeJoinSaved;
g.fill = fillSaved;
g.fillColor = fillColorSaved;
g.ellipseMode = ellipseModeSaved;
*/
if (matrix != null) {
g.popMatrix();
}
if (style) {
g.popStyle();
}
}
/**
* Called by the following (the shape() command adds the g)
* PShape s = loadShapes("blah.svg");
* shape(s);
*/
public void draw(PGraphics g) {
if (visible) {
pre(g);
drawImpl(g);
post(g);
}
}
/**
* Draws the SVG document.
*/
public void drawImpl(PGraphics g) {
//System.out.println("drawing " + family);
if (family == GROUP) {
drawGroup(g);
} else if (family == PRIMITIVE) {
drawPrimitive(g);
} else if (family == GEOMETRY) {
drawGeometry(g);
} else if (family == PATH) {
drawPath(g);
}
}
protected void drawGroup(PGraphics g) {
for (int i = 0; i < childCount; i++) {
children[i].draw(g);
}
}
protected void drawPrimitive(PGraphics g) {
if (kind == POINT) {
g.point(params[0], params[1]);
} else if (kind == LINE) {
if (params.length == 4) { // 2D
g.line(params[0], params[1],
params[2], params[3]);
} else { // 3D
g.line(params[0], params[1], params[2],
params[3], params[4], params[5]);
}
} else if (kind == TRIANGLE) {
g.triangle(params[0], params[1],
params[2], params[3],
params[4], params[5]);
} else if (kind == QUAD) {
g.quad(params[0], params[1],
params[2], params[3],
params[4], params[5],
params[6], params[7]);
} else if (kind == RECT) {
if (image != null) {
g.imageMode(CORNER);
g.image(image, params[0], params[1], params[2], params[3]);
} else {
g.rectMode(CORNER);
g.rect(params[0], params[1], params[2], params[3]);
}
} else if (kind == ELLIPSE) {
g.ellipseMode(CORNER);
g.ellipse(params[0], params[1], params[2], params[3]);
} else if (kind == ARC) {
g.ellipseMode(CORNER);
g.arc(params[0], params[1], params[2], params[3], params[4], params[5]);
} else if (kind == BOX) {
if (params.length == 1) {
g.box(params[0]);
} else {
g.box(params[0], params[1], params[2]);
}
} else if (kind == SPHERE) {
g.sphere(params[0]);
}
}
protected void drawGeometry(PGraphics g) {
g.beginShape(kind);
if (style) {
for (int i = 0; i < vertexCount; i++) {
g.vertex(vertices[i]);
}
} else {
for (int i = 0; i < vertexCount; i++) {
float[] vert = vertices[i];
if (vert[PGraphics.Z] == 0) {
g.vertex(vert[X], vert[Y]);
} else {
g.vertex(vert[X], vert[Y], vert[Z]);
}
}
}
g.endShape();
}
/*
protected void drawPath(PGraphics g) {
g.beginShape();
for (int j = 0; j < childCount; j++) {
if (j > 0) g.breakShape();
int count = children[j].vertexCount;
float[][] vert = children[j].vertices;
int[] code = children[j].vertexCodes;
for (int i = 0; i < count; i++) {
if (style) {
if (children[j].fill) {
g.fill(vert[i][R], vert[i][G], vert[i][B]);
} else {
g.noFill();
}
if (children[j].stroke) {
g.stroke(vert[i][R], vert[i][G], vert[i][B]);
} else {
g.noStroke();
}
}
g.edge(vert[i][EDGE] == 1);
if (code[i] == VERTEX) {
g.vertex(vert[i]);
} else if (code[i] == BEZIER_VERTEX) {
float z0 = vert[i+0][Z];
float z1 = vert[i+1][Z];
float z2 = vert[i+2][Z];
if (z0 == 0 && z1 == 0 && z2 == 0) {
g.bezierVertex(vert[i+0][X], vert[i+0][Y], z0,
vert[i+1][X], vert[i+1][Y], z1,
vert[i+2][X], vert[i+2][Y], z2);
} else {
g.bezierVertex(vert[i+0][X], vert[i+0][Y],
vert[i+1][X], vert[i+1][Y],
vert[i+2][X], vert[i+2][Y]);
}
} else if (code[i] == CURVE_VERTEX) {
float z = vert[i][Z];
if (z == 0) {
g.curveVertex(vert[i][X], vert[i][Y]);
} else {
g.curveVertex(vert[i][X], vert[i][Y], z);
}
}
}
}
g.endShape();
}
*/
protected void drawPath(PGraphics g) {
// Paths might be empty (go figure)
// http://dev.processing.org/bugs/show_bug.cgi?id=982
if (vertices == null) return;
g.beginShape();
if (vertexCodeCount == 0) { // each point is a simple vertex
if (vertices[0].length == 2) { // drawing 2D vertices
for (int i = 0; i < vertexCount; i++) {
g.vertex(vertices[i][X], vertices[i][Y]);
}
} else { // drawing 3D vertices
for (int i = 0; i < vertexCount; i++) {
g.vertex(vertices[i][X], vertices[i][Y], vertices[i][Z]);
}
}
} else { // coded set of vertices
int index = 0;
if (vertices[0].length == 2) { // drawing a 2D path
for (int j = 0; j < vertexCodeCount; j++) {
switch (vertexCodes[j]) {
case VERTEX:
g.vertex(vertices[index][X], vertices[index][Y]);
index++;
break;
case BEZIER_VERTEX:
g.bezierVertex(vertices[index+0][X], vertices[index+0][Y],
vertices[index+1][X], vertices[index+1][Y],
vertices[index+2][X], vertices[index+2][Y]);
index += 3;
break;
case CURVE_VERTEX:
g.curveVertex(vertices[index][X], vertices[index][Y]);
index++;
case BREAK:
g.breakShape();
}
}
} else { // drawing a 3D path
for (int j = 0; j < vertexCodeCount; j++) {
switch (vertexCodes[j]) {
case VERTEX:
g.vertex(vertices[index][X], vertices[index][Y], vertices[index][Z]);
index++;
break;
case BEZIER_VERTEX:
g.bezierVertex(vertices[index+0][X], vertices[index+0][Y], vertices[index+0][Z],
vertices[index+1][X], vertices[index+1][Y], vertices[index+1][Z],
vertices[index+2][X], vertices[index+2][Y], vertices[index+2][Z]);
index += 3;
break;
case CURVE_VERTEX:
g.curveVertex(vertices[index][X], vertices[index][Y], vertices[index][Z]);
index++;
case BREAK:
g.breakShape();
}
}
}
}
g.endShape(close ? CLOSE : OPEN);
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
public int getChildCount() {
return childCount;
}
public PShape getChild(int index) {
return children[index];
}
public PShape getChild(String target) {
if (name != null && name.equals(target)) {
return this;
}
if (nameTable != null) {
PShape found = nameTable.get(target);
if (found != null) return found;
}
for (int i = 0; i < childCount; i++) {
PShape found = children[i].getChild(target);
if (found != null) return found;
}
return null;
}
/**
* Same as getChild(name), except that it first walks all the way up the
* hierarchy to the farthest parent, so that children can be found anywhere.
*/
public PShape findChild(String target) {
if (parent == null) {
return getChild(target);
} else {
return parent.findChild(target);
}
}
// can't be just 'add' because that suggests additive geometry
public void addChild(PShape who) {
if (children == null) {
children = new PShape[1];
}
if (childCount == children.length) {
children = (PShape[]) PApplet.expand(children);
}
children[childCount++] = who;
who.parent = this;
if (who.getName() != null) {
addName(who.getName(), who);
}
}
/**
* Add a shape to the name lookup table.
*/
protected void addName(String nom, PShape shape) {
if (parent != null) {
parent.addName(nom, shape);
} else {
if (nameTable == null) {
nameTable = new HashMap<String,PShape>();
}
nameTable.put(nom, shape);
}
}
// public PShape createGroup() {
// PShape group = new PShape();
// group.kind = GROUP;
// addChild(group);
// return group;
// }
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// translate, rotate, scale, apply (no push/pop)
// these each call matrix.translate, etc
// if matrix is null when one is called,
// it is created and set to identity
public void translate(float tx, float ty) {
checkMatrix(2);
matrix.translate(tx, ty);
}
public void translate(float tx, float ty, float tz) {
checkMatrix(3);
matrix.translate(tx, ty, 0);
}
public void rotateX(float angle) {
rotate(angle, 1, 0, 0);
}
public void rotateY(float angle) {
rotate(angle, 0, 1, 0);
}
public void rotateZ(float angle) {
rotate(angle, 0, 0, 1);
}
public void rotate(float angle) {
checkMatrix(2); // at least 2...
matrix.rotate(angle);
}
public void rotate(float angle, float v0, float v1, float v2) {
checkMatrix(3);
matrix.rotate(angle, v0, v1, v2);
}
//
public void scale(float s) {
checkMatrix(2); // at least 2...
matrix.scale(s);
}
public void scale(float sx, float sy) {
checkMatrix(2);
matrix.scale(sx, sy);
}
public void scale(float x, float y, float z) {
checkMatrix(3);
matrix.scale(x, y, z);
}
//
public void resetMatrix() {
checkMatrix(2);
matrix.reset();
}
public void applyMatrix(PMatrix source) {
if (source instanceof PMatrix2D) {
applyMatrix((PMatrix2D) source);
} else if (source instanceof PMatrix3D) {
applyMatrix((PMatrix3D) source);
}
}
public void applyMatrix(PMatrix2D source) {
applyMatrix(source.m00, source.m01, 0, source.m02,
source.m10, source.m11, 0, source.m12,
0, 0, 1, 0,
0, 0, 0, 1);
}
public void applyMatrix(float n00, float n01, float n02,
float n10, float n11, float n12) {
checkMatrix(2);
matrix.apply(n00, n01, n02, 0,
n10, n11, n12, 0,
0, 0, 1, 0,
0, 0, 0, 1);
}
public void apply(PMatrix3D source) {
applyMatrix(source.m00, source.m01, source.m02, source.m03,
source.m10, source.m11, source.m12, source.m13,
source.m20, source.m21, source.m22, source.m23,
source.m30, source.m31, source.m32, source.m33);
}
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) {
checkMatrix(3);
matrix.apply(n00, n01, n02, n03,
n10, n11, n12, n13,
n20, n21, n22, n23,
n30, n31, n32, n33);
}
//
/**
* Make sure that the shape's matrix is 1) not null, and 2) has a matrix
* that can handle <em>at least</em> the specified number of dimensions.
*/
protected void checkMatrix(int dimensions) {
if (matrix == null) {
if (dimensions == 2) {
matrix = new PMatrix2D();
} else {
matrix = new PMatrix3D();
}
} else if (dimensions == 3 && (matrix instanceof PMatrix2D)) {
// time for an upgrayedd for a double dose of my pimpin'
matrix = new PMatrix3D(matrix);
}
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
/**
* Center the shape based on its bounding box. Can't assume
* that the bounding box is 0, 0, width, height. Common case will be
* opening a letter size document in Illustrator, and drawing something
* in the middle, then reading it in as an svg file.
* This will also need to flip the y axis (scale(1, -1)) in cases
* like Adobe Illustrator where the coordinates start at the bottom.
*/
// public void center() {
// }
/**
* Set the pivot point for all transformations.
*/
// public void pivot(float x, float y) {
// px = x;
// py = y;
// }
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,968 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-08 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.core;
/**
* Smoothed triangle renderer for P3D.
*
* Based off of the PPolygon class in old versions of Processing.
* Name and location of this class will change in a future release.
*/
public class PSmoothTriangle implements PConstants {
// really this is "debug" but..
private static final boolean EWJORDAN = false;
private static final boolean FRY = false;
// identical to the constants from PGraphics
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;
static final int U = 7; // texture
static final int V = 8;
static final int DEFAULT_SIZE = 64; // this is needed for spheres
float vertices[][] = new float[DEFAULT_SIZE][PGraphics.VERTEX_FIELD_COUNT];
int vertexCount;
// after some fiddling, this seems to produce the best results
static final int ZBUFFER_MIN_COVERAGE = 204;
float r[] = new float[DEFAULT_SIZE]; // storage used by incrementalize
float dr[] = new float[DEFAULT_SIZE];
float l[] = new float[DEFAULT_SIZE]; // more storage for incrementalize
float dl[] = new float[DEFAULT_SIZE];
float sp[] = new float[DEFAULT_SIZE]; // temporary storage for scanline
float sdp[] = new float[DEFAULT_SIZE];
// color and xyz are always interpolated
boolean interpX;
boolean interpZ;
boolean interpUV; // is this necessary? could just check timage != null
boolean interpARGB;
int rgba;
int r2, g2, b2, a2, a2orig;
boolean noDepthTest;
PGraphics3D parent;
int pixels[];
float[] zbuffer;
// the parent's width/height,
// or if smooth is enabled, parent's w/h scaled
// up by the smooth dimension
int width, height;
int width1, height1;
PImage timage;
int tpixels[];
int theight, twidth;
int theight1, twidth1;
int tformat;
// temp fix to behave like SMOOTH_IMAGES
// TODO ewjordan: can probably remove this variable
boolean texture_smooth;
// for anti-aliasing
static final int SUBXRES = 8;
static final int SUBXRES1 = 7;
static final int SUBYRES = 8;
static final int SUBYRES1 = 7;
static final int MAX_COVERAGE = SUBXRES * SUBYRES;
boolean smooth;
int firstModY;
int lastModY;
int lastY;
int aaleft[] = new int[SUBYRES];
int aaright[] = new int[SUBYRES];
int aaleftmin, aarightmin;
int aaleftmax, aarightmax;
int aaleftfull, aarightfull;
/* Variables needed for accurate texturing. */
//private PMatrix textureMatrix = new PMatrix3D();
private float[] camX = new float[3];
private float[] camY = new float[3];
private float[] camZ = new float[3];
private float ax,ay,az;
private float bx,by,bz;
private float cx,cy,cz;
private float nearPlaneWidth, nearPlaneHeight, nearPlaneDepth;
//private float newax, newbx, newcx;
private float xmult, ymult;
final private int MODYRES(int y) {
return (y & SUBYRES1);
}
public PSmoothTriangle(PGraphics3D iparent) {
parent = iparent;
reset(0);
}
public void reset(int count) {
vertexCount = count;
interpX = true;
interpZ = true;
interpUV = false;
interpARGB = true;
timage = null;
}
public float[] nextVertex() {
if (vertexCount == vertices.length) {
//parent.message(CHATTER, "re-allocating for " +
// (vertexCount*2) + " vertices");
float temp[][] = new float[vertexCount<<1][PGraphics.VERTEX_FIELD_COUNT];
System.arraycopy(vertices, 0, temp, 0, vertexCount);
vertices = temp;
r = new float[vertices.length];
dr = new float[vertices.length];
l = new float[vertices.length];
dl = new float[vertices.length];
sp = new float[vertices.length];
sdp = new float[vertices.length];
}
return vertices[vertexCount++]; // returns v[0], sets vc to 1
}
public void texture(PImage image) {
this.timage = image;
this.tpixels = image.pixels;
this.twidth = image.width;
this.theight = image.height;
this.tformat = image.format;
twidth1 = twidth - 1;
theight1 = theight - 1;
interpUV = true;
}
public void render() {
if (vertexCount < 3) return;
smooth = true;//TODO
// these may have changed due to a resize()
// so they should be refreshed here
pixels = parent.pixels;
zbuffer = parent.zbuffer;
noDepthTest = false;//parent.hints[DISABLE_DEPTH_TEST];
// In 0148+, should always be true if this code is called at all
//smooth = parent.smooth;
// by default, text turns on smooth for the textures
// themselves. but this should be shut off if the hint
// for DISABLE_TEXT_SMOOTH is set.
texture_smooth = true;
width = smooth ? parent.width*SUBXRES : parent.width;
height = smooth ? parent.height*SUBYRES : parent.height;
width1 = width - 1;
height1 = height - 1;
if (!interpARGB) {
r2 = (int) (vertices[0][R] * 255);
g2 = (int) (vertices[0][G] * 255);
b2 = (int) (vertices[0][B] * 255);
a2 = (int) (vertices[0][A] * 255);
a2orig = a2; // save an extra copy
rgba = 0xff000000 | (r2 << 16) | (g2 << 8) | b2;
}
for (int i = 0; i < vertexCount; i++) {
r[i] = 0; dr[i] = 0; l[i] = 0; dl[i] = 0;
}
if (smooth) {
for (int i = 0; i < vertexCount; i++) {
vertices[i][X] *= SUBXRES;
vertices[i][Y] *= SUBYRES;
}
firstModY = -1;
}
// find top vertex (y is zero at top, higher downwards)
int topi = 0;
float ymin = vertices[0][Y];
float ymax = vertices[0][Y]; // fry 031001
for (int i = 1; i < vertexCount; i++) {
if (vertices[i][Y] < ymin) {
ymin = vertices[i][Y];
topi = i;
}
if (vertices[i][Y] > ymax) ymax = vertices[i][Y];
}
// the last row is an exceptional case, because there won't
// necessarily be 8 rows of subpixel lines that will force
// the final line to render. so instead, the algo keeps track
// of the lastY (in subpixel resolution) that will be rendered
// and that will force a scanline to happen the same as
// every eighth in the other situations
//lastY = -1; // fry 031001
lastY = (int) (ymax - 0.5f); // global to class bc used by other fxns
int lefti = topi; // li, index of left vertex
int righti = topi; // ri, index of right vertex
int y = (int) (ymin + 0.5f); // current scan line
int lefty = y - 1; // lower end of left edge
int righty = y - 1; // lower end of right edge
interpX = true;
int remaining = vertexCount;
// scan in y, activating new edges on left & right
// as scan line passes over new vertices
while (remaining > 0) {
// advance left edge?
while ((lefty <= y) && (remaining > 0)) {
remaining--;
// step ccw down left side
int i = (lefti != 0) ? (lefti-1) : (vertexCount-1);
incrementalize_y(vertices[lefti], vertices[i], l, dl, y);
lefty = (int) (vertices[i][Y] + 0.5f);
lefti = i;
}
// advance right edge?
while ((righty <= y) && (remaining > 0)) {
remaining--;
// step cw down right edge
int i = (righti != vertexCount-1) ? (righti + 1) : 0;
incrementalize_y(vertices[righti], vertices[i], r, dr, y);
righty = (int) (vertices[i][Y] + 0.5f);
righti = i;
}
// do scanlines till end of l or r edge
while (y < lefty && y < righty) {
// this doesn't work because it's not always set here
//if (remaining == 0) {
//lastY = (lefty < righty) ? lefty-1 : righty-1;
//System.out.println("lastY is " + lastY);
//}
if ((y >= 0) && (y < height)) {
//try { // hopefully this bug is fixed
if (l[X] <= r[X]) scanline(y, l, r);
else scanline(y, r, l);
//} catch (ArrayIndexOutOfBoundsException e) {
//e.printStackTrace();
//}
}
y++;
// this increment probably needs to be different
// UV and RGB shouldn't be incremented until line is emitted
increment(l, dl);
increment(r, dr);
}
}
//if (smooth) {
//System.out.println("y/lasty/lastmody = " + y + " " + lastY + " " + lastModY);
//}
}
public void unexpand() {
if (smooth) {
for (int i = 0; i < vertexCount; i++) {
vertices[i][X] /= SUBXRES;
vertices[i][Y] /= SUBYRES;
}
}
}
private void scanline(int y, float l[], float r[]) {
//System.out.println("scanline " + y);
for (int i = 0; i < vertexCount; i++) { // should be moved later
sp[i] = 0; sdp[i] = 0;
}
// this rounding doesn't seem to be relevant with smooth
int lx = (int) (l[X] + 0.49999f); // ceil(l[X]-.5);
if (lx < 0) lx = 0;
int rx = (int) (r[X] - 0.5f);
if (rx > width1) rx = width1;
if (lx > rx) return;
if (smooth) {
int mody = MODYRES(y);
aaleft[mody] = lx;
aaright[mody] = rx;
if (firstModY == -1) {
firstModY = mody;
aaleftmin = lx; aaleftmax = lx;
aarightmin = rx; aarightmax = rx;
} else {
if (aaleftmin > aaleft[mody]) aaleftmin = aaleft[mody];
if (aaleftmax < aaleft[mody]) aaleftmax = aaleft[mody];
if (aarightmin > aaright[mody]) aarightmin = aaright[mody];
if (aarightmax < aaright[mody]) aarightmax = aaright[mody];
}
lastModY = mody; // moved up here (before the return) 031001
// not the eighth (or lastY) line, so not scanning this time
if ((mody != SUBYRES1) && (y != lastY)) return;
//lastModY = mody; // eeK! this was missing
//return;
//if (y == lastY) {
//System.out.println("y is lasty");
//}
//lastModY = mody;
aaleftfull = aaleftmax/SUBXRES + 1;
aarightfull = aarightmin/SUBXRES - 1;
}
// this is the setup, based on lx
incrementalize_x(l, r, sp, sdp, lx);
//System.out.println(l[V] + " " + r[V] + " " +sp[V] + " " +sdp[V]);
// scan in x, generating pixels
// using parent.width to get actual pixel index
// rather than scaled by smooth factor
int offset = smooth ? parent.width * (y / SUBYRES) : parent.width*y;
int truelx = 0, truerx = 0;
if (smooth) {
truelx = lx / SUBXRES;
truerx = (rx + SUBXRES1) / SUBXRES;
lx = aaleftmin / SUBXRES;
rx = (aarightmax + SUBXRES1) / SUBXRES;
if (lx < 0) lx = 0;
if (rx > parent.width1) rx = parent.width1;
}
// System.out.println("P3D interp uv " + interpUV + " " +
// vertices[2][U] + " " + vertices[2][V]);
interpX = false;
int tr, tg, tb, ta;
//System.out.println("lx: "+lx + "\nrx: "+rx);
for (int x = lx; x <= rx; x++) {
// added == because things on same plane weren't replacing each other
// makes for strangeness in 3D [ewj: yup!], but totally necessary for 2D
//if (noDepthTest || (sp[Z] < zbuffer[offset+x])) {
if (noDepthTest || (sp[Z] <= zbuffer[offset+x])) {
//if (true) {
// map texture based on U, V coords in sp[U] and sp[V]
if (interpUV) {
int tu = (int)sp[U];
int tv = (int)sp[V];
if (tu > twidth1) tu = twidth1;
if (tv > theight1) tv = theight1;
if (tu < 0) tu = 0;
if (tv < 0) tv = 0;
int txy = tv*twidth + tu;
//System.out.println("tu: "+tu+" ; tv: "+tv+" ; txy: "+txy);
float[] uv = new float[2];
txy = getTextureIndex(x, y*1.0f/SUBYRES, uv);
// txy = getTextureIndex(x* 1.0f/SUBXRES, y*1.0f/SUBYRES, uv);
tu = (int)uv[0]; tv = (int)uv[1];
// if (tu > twidth1) tu = twidth1;
// if (tv > theight1) tv = theight1;
// if (tu < 0) tu = 0;
// if (tv < 0) tv = 0;
txy = twidth*tv + tu;
// if (EWJORDAN) System.out.println("x/y/txy:"+x + " " + y + " " +txy);
//PApplet.println(sp);
//smooth = true;
if (smooth || texture_smooth) {
//if (FRY) System.out.println("sp u v = " + sp[U] + " " + sp[V]);
//System.out.println("sp u v = " + sp[U] + " " + sp[V]);
// tuf1/tvf1 is the amount of coverage for the adjacent
// pixel, which is the decimal percentage.
// int tuf1 = (int) (255f * (sp[U] - (float)tu));
// int tvf1 = (int) (255f * (sp[V] - (float)tv));
int tuf1 = (int) (255f * (uv[0] - tu));
int tvf1 = (int) (255f * (uv[1] - 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];
//System.out.println("1: "+pixel00);
//check
int p00, p01, p10, p11;
int px0, px1; //, pxy;
if (tformat == ALPHA) {
px0 = (pixel00*tuf + pixel10*tuf1) >> 8;
px1 = (pixel01*tuf + pixel11*tuf1) >> 8;
ta = (((px0*tvf + px1*tvf1) >> 8) *
(interpARGB ? ((int) (sp[A]*255)) : a2orig)) >> 8;
} else if (tformat == ARGB) {
p00 = (pixel00 >> 24) & 0xff;
p01 = (pixel01 >> 24) & 0xff;
p10 = (pixel10 >> 24) & 0xff;
p11 = (pixel11 >> 24) & 0xff;
px0 = (p00*tuf + p10*tuf1) >> 8;
px1 = (p01*tuf + p11*tuf1) >> 8;
ta = (((px0*tvf + px1*tvf1) >> 8) *
(interpARGB ? ((int) (sp[A]*255)) : a2orig)) >> 8;
} else { // RGB image, no alpha
//ACCTEX: Getting here when smooth is on
ta = interpARGB ? ((int) (sp[A]*255)) : a2orig;
//System.out.println("4: "+ta + " " +interpARGB + " " + sp[A] + " " + a2orig);
//check
}
if ((tformat == RGB) || (tformat == ARGB)) {
p00 = (pixel00 >> 16) & 0xff; // red
p01 = (pixel01 >> 16) & 0xff;
p10 = (pixel10 >> 16) & 0xff;
p11 = (pixel11 >> 16) & 0xff;
px0 = (p00*tuf + p10*tuf1) >> 8;
px1 = (p01*tuf + p11*tuf1) >> 8;
tr = (((px0*tvf + px1*tvf1) >> 8) * (interpARGB ? ((int) (sp[R]*255)) : r2)) >> 8;
p00 = (pixel00 >> 8) & 0xff; // green
p01 = (pixel01 >> 8) & 0xff;
p10 = (pixel10 >> 8) & 0xff;
p11 = (pixel11 >> 8) & 0xff;
px0 = (p00*tuf + p10*tuf1) >> 8;
px1 = (p01*tuf + p11*tuf1) >> 8;
tg = (((px0*tvf + px1*tvf1) >> 8) * (interpARGB ? ((int) (sp[G]*255)) : g2)) >> 8;
p00 = pixel00 & 0xff; // blue
p01 = pixel01 & 0xff;
p10 = pixel10 & 0xff;
p11 = pixel11 & 0xff;
px0 = (p00*tuf + p10*tuf1) >> 8;
px1 = (p01*tuf + p11*tuf1) >> 8;
tb = (((px0*tvf + px1*tvf1) >> 8) * (interpARGB ? ((int) (sp[B]*255)) : b2)) >> 8;
//System.out.println("5: "+tr + " " + tg + " " +tb);
//check
} else { // alpha image, only use current fill color
if (interpARGB) {
tr = (int) (sp[R] * 255);
tg = (int) (sp[G] * 255);
tb = (int) (sp[B] * 255);
} else {
tr = r2;
tg = g2;
tb = b2;
}
}
// get coverage for pixel if smooth
// checks smooth again here because of
// hints[SMOOTH_IMAGES] used up above
int weight = smooth ? coverage(x) : 255;
if (weight != 255) ta = (ta*weight) >> 8;
//System.out.println(ta);
//System.out.println("8");
//check
} else { // no smooth, just get the pixels
int tpixel = tpixels[txy];
// TODO i doubt splitting these guys really gets us
// all that much speed.. is it worth it?
if (tformat == ALPHA) {
ta = tpixel;
if (interpARGB) {
tr = (int) (sp[R]*255);
tg = (int) (sp[G]*255);
tb = (int) (sp[B]*255);
if (sp[A] != 1) {
ta = (((int) (sp[A]*255)) * ta) >> 8;
}
} else {
tr = r2;
tg = g2;
tb = b2;
ta = (a2orig * ta) >> 8;
}
} else { // RGB or ARGB
ta = (tformat == RGB) ? 255 : (tpixel >> 24) & 0xff;
if (interpARGB) {
tr = (((int) (sp[R]*255)) * ((tpixel >> 16) & 0xff)) >> 8;
tg = (((int) (sp[G]*255)) * ((tpixel >> 8) & 0xff)) >> 8;
tb = (((int) (sp[B]*255)) * ((tpixel) & 0xff)) >> 8;
ta = (((int) (sp[A]*255)) * ta) >> 8;
} else {
tr = (r2 * ((tpixel >> 16) & 0xff)) >> 8;
tg = (g2 * ((tpixel >> 8) & 0xff)) >> 8;
tb = (b2 * ((tpixel) & 0xff)) >> 8;
ta = (a2orig * ta) >> 8;
}
}
}
if ((ta == 254) || (ta == 255)) { // if (ta & 0xf8) would be good
// no need to blend
pixels[offset+x] = 0xff000000 | (tr << 16) | (tg << 8) | tb;
zbuffer[offset+x] = sp[Z];
} else {
// blend with pixel on screen
int a1 = 255-ta;
int r1 = (pixels[offset+x] >> 16) & 0xff;
int g1 = (pixels[offset+x] >> 8) & 0xff;
int b1 = (pixels[offset+x]) & 0xff;
pixels[offset+x] =
0xff000000 |
(((tr*ta + r1*a1) >> 8) << 16) |
((tg*ta + g1*a1) & 0xff00) |
((tb*ta + b1*a1) >> 8);
//System.out.println("17");
//check
if (ta > ZBUFFER_MIN_COVERAGE) zbuffer[offset+x] = sp[Z];
}
//System.out.println("18");
//check
} else { // no image applied
int weight = smooth ? coverage(x) : 255;
if (interpARGB) {
r2 = (int) (sp[R] * 255);
g2 = (int) (sp[G] * 255);
b2 = (int) (sp[B] * 255);
if (sp[A] != 1) weight = (weight * ((int) (sp[A] * 255))) >> 8;
if (weight == 255) {
rgba = 0xff000000 | (r2 << 16) | (g2 << 8) | b2;
}
} else {
if (a2orig != 255) weight = (weight * a2orig) >> 8;
}
if (weight == 255) {
// no blend, no aa, just the rgba
pixels[offset+x] = rgba;
zbuffer[offset+x] = sp[Z];
} else {
int r1 = (pixels[offset+x] >> 16) & 0xff;
int g1 = (pixels[offset+x] >> 8) & 0xff;
int b1 = (pixels[offset+x]) & 0xff;
a2 = weight;
int a1 = 255 - a2;
pixels[offset+x] = (0xff000000 |
((r1*a1 + r2*a2) >> 8) << 16 |
// use & instead of >> and << below
((g1*a1 + g2*a2) >> 8) << 8 |
((b1*a1 + b2*a2) >> 8));
if (a2 > ZBUFFER_MIN_COVERAGE) zbuffer[offset+x] = sp[Z];
}
}
}
// if smooth enabled, don't increment values
// for the pixel in the stretch out version
// of the scanline used to get smooth edges.
if (!smooth || ((x >= truelx) && (x <= truerx))) {
//if (!smooth)
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 = 1;
float fraction = y + 0.5f - p1[Y];
if (interpX) {
dp[X] = (p2[X] - p1[X]) / delta;
p[X] = p1[X] + dp[X] * fraction;
}
if (interpZ) {
dp[Z] = (p2[Z] - p1[Z]) / delta;
p[Z] = p1[Z] + dp[Z] * fraction;
}
if (interpARGB) {
dp[R] = (p2[R] - p1[R]) / delta;
dp[G] = (p2[G] - p1[G]) / delta;
dp[B] = (p2[B] - p1[B]) / delta;
dp[A] = (p2[A] - p1[A]) / delta;
p[R] = p1[R] + dp[R] * fraction;
p[G] = p1[G] + dp[G] * fraction;
p[B] = p1[B] + dp[B] * fraction;
p[A] = p1[A] + dp[A] * fraction;
}
if (interpUV) {
dp[U] = (p2[U] - p1[U]) / delta;
dp[V] = (p2[V] - p1[V]) / delta;
//if (smooth) {
//p[U] = p1[U]; //+ dp[U] * fraction;
//p[V] = p1[V]; //+ dp[V] * fraction;
//} else {
p[U] = p1[U] + dp[U] * fraction;
p[V] = p1[V] + dp[V] * fraction;
//}
if (FRY) System.out.println("inc y p[U] p[V] = " + p[U] + " " + p[V]);
}
}
//incrementalize_x(l, r, sp, sdp, lx);
private void incrementalize_x(float p1[], float p2[],
float p[], float dp[], int x) {
float delta = p2[X] - p1[X];
if (delta == 0) delta = 1;
float fraction = x + 0.5f - p1[X];
if (smooth) {
delta /= SUBXRES;
fraction /= SUBXRES;
}
if (interpX) {
dp[X] = (p2[X] - p1[X]) / delta;
p[X] = p1[X] + dp[X] * fraction;
}
if (interpZ) {
dp[Z] = (p2[Z] - p1[Z]) / delta;
p[Z] = p1[Z] + dp[Z] * fraction;
//System.out.println(p2[Z]+" " +p1[Z]+" " +dp[Z]);
}
if (interpARGB) {
dp[R] = (p2[R] - p1[R]) / delta;
dp[G] = (p2[G] - p1[G]) / delta;
dp[B] = (p2[B] - p1[B]) / delta;
dp[A] = (p2[A] - p1[A]) / delta;
p[R] = p1[R] + dp[R] * fraction;
p[G] = p1[G] + dp[G] * fraction;
p[B] = p1[B] + dp[B] * fraction;
p[A] = p1[A] + dp[A] * fraction;
}
if (interpUV) {
if (FRY) System.out.println("delta, frac = " + delta + ", " + fraction);
dp[U] = (p2[U] - p1[U]) / delta;
dp[V] = (p2[V] - p1[V]) / delta;
//if (smooth) {
//p[U] = p1[U];
// offset for the damage that will be done by the
// 8 consecutive calls to scanline
// agh.. this won't work b/c not always 8 calls before render
// maybe lastModY - firstModY + 1 instead?
if (FRY) System.out.println("before inc x p[V] = " + p[V] + " " + p1[V] + " " + p2[V]);
//p[V] = p1[V] - SUBXRES1 * fraction;
//} else {
p[U] = p1[U] + dp[U] * fraction;
p[V] = p1[V] + dp[V] * fraction;
//}
}
}
private void increment(float p[], float dp[]) {
if (interpX) p[X] += dp[X];
if (interpZ) p[Z] += dp[Z];
if (interpARGB) {
p[R] += dp[R];
p[G] += dp[G];
p[B] += dp[B];
p[A] += dp[A];
}
if (interpUV) {
if (FRY) System.out.println("increment() " + p[V] + " " + dp[V]);
p[U] += dp[U];
p[V] += dp[V];
}
}
/**
* Pass camera-space coordinates for the triangle.
* Needed to render if hint(ENABLE_ACCURATE_TEXTURES) enabled.
* Generally this will not need to be called manually,
* currently called from PGraphics3D.render_triangles()
*/
public void setCamVertices(float x0, float y0, float z0,
float x1, float y1, float z1,
float x2, float y2, float z2) {
camX[0] = x0;
camX[1] = x1;
camX[2] = x2;
camY[0] = y0;
camY[1] = y1;
camY[2] = y2;
camZ[0] = z0;
camZ[1] = z1;
camZ[2] = z2;
}
public void setVertices(float x0, float y0, float z0,
float x1, float y1, float z1,
float x2, float y2, float z2) {
vertices[0][X] = x0;
vertices[1][X] = x1;
vertices[2][X] = x2;
vertices[0][Y] = y0;
vertices[1][Y] = y1;
vertices[2][Y] = y2;
vertices[0][Z] = z0;
vertices[1][Z] = z1;
vertices[2][Z] = z2;
}
/**
* Precompute a bunch of variables needed to perform
* texture mapping.
* @return True unless texture mapping is degenerate
*/
boolean precomputeAccurateTexturing() {
int o0 = 0;
int o1 = 1;
int o2 = 2;
PMatrix3D myMatrix = new PMatrix3D(vertices[o0][U], vertices[o0][V], 1, 0,
vertices[o1][U], vertices[o1][V], 1, 0,
vertices[o2][U], vertices[o2][V], 1, 0,
0, 0, 0, 1);
// A 3x3 inversion would be more efficient here,
// given that the fourth r/c are unity
boolean invertSuccess = myMatrix.invert();// = myMatrix.invert();
// If the matrix inversion had trouble, let the caller know.
// Note that this does not catch everything that could go wrong
// here, like if the renderer is in ortho() mode (which really
// must be caught in PGraphics3D instead of here).
if (!invertSuccess) return false;
float m00, m01, m02, m10, m11, m12, m20, m21, m22;
m00 = myMatrix.m00*camX[o0]+myMatrix.m01*camX[o1]+myMatrix.m02*camX[o2];
m01 = myMatrix.m10*camX[o0]+myMatrix.m11*camX[o1]+myMatrix.m12*camX[o2];
m02 = myMatrix.m20*camX[o0]+myMatrix.m21*camX[o1]+myMatrix.m22*camX[o2];
m10 = myMatrix.m00*camY[o0]+myMatrix.m01*camY[o1]+myMatrix.m02*camY[o2];
m11 = myMatrix.m10*camY[o0]+myMatrix.m11*camY[o1]+myMatrix.m12*camY[o2];
m12 = myMatrix.m20*camY[o0]+myMatrix.m21*camY[o1]+myMatrix.m22*camY[o2];
m20 = -(myMatrix.m00*camZ[o0]+myMatrix.m01*camZ[o1]+myMatrix.m02*camZ[o2]);
m21 = -(myMatrix.m10*camZ[o0]+myMatrix.m11*camZ[o1]+myMatrix.m12*camZ[o2]);
m22 = -(myMatrix.m20*camZ[o0]+myMatrix.m21*camZ[o1]+myMatrix.m22*camZ[o2]);
float px = m02;
float py = m12;
float pz = m22;
float TEX_WIDTH = this.twidth;
float TEX_HEIGHT = this.theight;
float resultT0x = m00*TEX_WIDTH+m02;
float resultT0y = m10*TEX_WIDTH+m12;
float resultT0z = m20*TEX_WIDTH+m22;
float result0Tx = m01*TEX_HEIGHT+m02;
float result0Ty = m11*TEX_HEIGHT+m12;
float result0Tz = m21*TEX_HEIGHT+m22;
float mx = resultT0x-m02;
float my = resultT0y-m12;
float mz = resultT0z-m22;
float nx = result0Tx-m02;
float ny = result0Ty-m12;
float nz = result0Tz-m22;
//avec = p x n
ax = (py*nz-pz*ny)*TEX_WIDTH; //F_TEX_WIDTH/HEIGHT?
ay = (pz*nx-px*nz)*TEX_WIDTH;
az = (px*ny-py*nx)*TEX_WIDTH;
//bvec = m x p
bx = (my*pz-mz*py)*TEX_HEIGHT;
by = (mz*px-mx*pz)*TEX_HEIGHT;
bz = (mx*py-my*px)*TEX_HEIGHT;
//cvec = n x m
cx = ny*mz-nz*my;
cy = nz*mx-nx*mz;
cz = nx*my-ny*mx;
//System.out.println("a/b/c: "+ax+" " + ay + " " + az + " " + bx + " " + by + " " + bz + " " + cx + " " + cy + " " + cz);
nearPlaneWidth = (parent.rightScreen-parent.leftScreen);
nearPlaneHeight = (parent.topScreen-parent.bottomScreen);
nearPlaneDepth = parent.nearPlane;
// one pixel width in nearPlane coordinates
xmult = nearPlaneWidth / parent.width;
ymult = nearPlaneHeight / parent.height;
// Extra scalings to map screen plane units to pixel units
// newax = ax*xmult;
// newbx = bx*xmult;
// newcx = cx*xmult;
// System.out.println("nearplane: "+ nearPlaneWidth + " " + nearPlaneHeight + " " + nearPlaneDepth);
// System.out.println("mults: "+ xmult + " " + ymult);
// System.out.println("news: "+ newax + " " + newbx + " " + newcx);
return true;
}
/**
* Get the texture map location based on the current screen
* coordinates. Assumes precomputeAccurateTexturing() has
* been called already for this texture mapping.
* @param sx
* @param sy
* @return
*/
private int getTextureIndex(float sx, float sy, float[] uv) {
if (EWJORDAN) System.out.println("Getting texel at "+sx + ", "+sy);
//System.out.println("Screen: "+ sx + " " + sy);
sx = xmult*(sx-(parent.width/2.0f) +.5f);//+.5f)
sy = ymult*(sy-(parent.height/2.0f)+.5f);//+.5f)
//sx /= SUBXRES;
//sy /= SUBYRES;
float sz = nearPlaneDepth;
float a = sx * ax + sy * ay + sz * az;
float b = sx * bx + sy * by + sz * bz;
float c = sx * cx + sy * cy + sz * cz;
int u = (int)(a / c);
int v = (int)(b / c);
uv[0] = a / c;
uv[1] = b / c;
if (uv[0] < 0) {
uv[0] = u = 0;
}
if (uv[1] < 0) {
uv[1] = v = 0;
}
if (uv[0] >= twidth) {
uv[0] = twidth-1;
u = twidth-1;
}
if (uv[1] >= theight) {
uv[1] = theight-1;
v = theight-1;
}
int result = v*twidth + u;
//System.out.println("a/b/c: "+a + " " + b + " " + c);
//System.out.println("cx/y/z: "+cx + " " + cy + " " + cz);
//if (result < 0) result = 0;
//if (result >= timage.pixels.length-2) result = timage.pixels.length - 2;
if (EWJORDAN) System.out.println("Got texel "+result);
return result;
}
public void setIntensities(float ar, float ag, float ab, float aa,
float br, float bg, float bb, float ba,
float cr, float cg, float cb, float ca) {
vertices[0][R] = ar;
vertices[0][G] = ag;
vertices[0][B] = ab;
vertices[0][A] = aa;
vertices[1][R] = br;
vertices[1][G] = bg;
vertices[1][B] = bb;
vertices[1][A] = ba;
vertices[2][R] = cr;
vertices[2][G] = cg;
vertices[2][B] = cb;
vertices[2][A] = ca;
}
}

View File

@ -0,0 +1,61 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2008 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.core;
public class PStyle implements PConstants {
public int imageMode;
public int rectMode;
public int ellipseMode;
public int shapeMode;
public int colorMode;
public float colorModeX;
public float colorModeY;
public float colorModeZ;
public float colorModeA;
public boolean tint;
public int tintColor;
public boolean fill;
public int fillColor;
public boolean stroke;
public int strokeColor;
public float strokeWeight;
public int strokeCap;
public int strokeJoin;
// TODO these fellas are inconsistent, and may need to go elsewhere
public float ambientR, ambientG, ambientB;
public float specularR, specularG, specularB;
public float emissiveR, emissiveG, emissiveB;
public float shininess;
public PFont textFont;
public int textAlign;
public int textAlignY;
public int textMode;
public float textSize;
public float textLeading;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,559 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 200X Dan Shiffman
Copyright (c) 2008 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.core;
/**
* A class to describe a two or three dimensional vector.
* <p>
* The result of all functions are applied to the vector itself, with the
* exception of cross(), which returns a new PVector (or writes to a specified
* 'target' PVector). That is, add() will add the contents of one vector to
* this one. Using add() with additional parameters allows you to put the
* result into a new PVector. Functions that act on multiple vectors also
* include static versions. Because creating new objects can be computationally
* expensive, most functions include an optional 'target' PVector, so that a
* new PVector object is not created with each operation.
* <p>
* Initially based on the Vector3D class by <a href="http://www.shiffman.net">Dan Shiffman</a>.
*/
public class PVector {
/** The x component of the vector. */
public float x;
/** The y component of the vector. */
public float y;
/** The z component of the vector. */
public float z;
/** Array so that this can be temporarily used in an array context */
protected float[] array;
/**
* Constructor for an empty vector: x, y, and z are set to 0.
*/
public PVector() {
}
/**
* Constructor for a 3D vector.
*
* @param x the x coordinate.
* @param y the y coordinate.
* @param z the y coordinate.
*/
public PVector(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
/**
* Constructor for a 2D vector: z coordinate is set to 0.
*
* @param x the x coordinate.
* @param y the y coordinate.
*/
public PVector(float x, float y) {
this.x = x;
this.y = y;
this.z = 0;
}
/**
* Set x, y, and z coordinates.
*
* @param x the x coordinate.
* @param y the y coordinate.
* @param z the z coordinate.
*/
public void set(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
/**
* Set x, y, and z coordinates from a Vector3D object.
*
* @param v the PVector object to be copied
*/
public void set(PVector v) {
x = v.x;
y = v.y;
z = v.z;
}
/**
* Set the x, y (and maybe z) coordinates using a float[] array as the source.
* @param source array to copy from
*/
public void set(float[] source) {
if (source.length >= 2) {
x = source[0];
y = source[1];
}
if (source.length >= 3) {
z = source[2];
}
}
/**
* Get a copy of this vector.
*/
public PVector get() {
return new PVector(x, y, z);
}
public float[] get(float[] target) {
if (target == null) {
return new float[] { x, y, z };
}
if (target.length >= 2) {
target[0] = x;
target[1] = y;
}
if (target.length >= 3) {
target[2] = z;
}
return target;
}
/**
* Calculate the magnitude (length) of the vector
* @return the magnitude of the vector
*/
public float mag() {
return (float) Math.sqrt(x*x + y*y + z*z);
}
/**
* Add a vector to this vector
* @param v the vector to be added
*/
public void add(PVector v) {
x += v.x;
y += v.y;
z += v.z;
}
public void add(float x, float y, float z) {
this.x += x;
this.y += y;
this.z += z;
}
/**
* Add two vectors
* @param v1 a vector
* @param v2 another vector
* @return a new vector that is the sum of v1 and v2
*/
static public PVector add(PVector v1, PVector v2) {
return add(v1, v2, null);
}
/**
* Add two vectors into a target vector
* @param v1 a vector
* @param v2 another vector
* @param target the target vector (if null, a new vector will be created)
* @return a new vector that is the sum of v1 and v2
*/
static public PVector add(PVector v1, PVector v2, PVector target) {
if (target == null) {
target = new PVector(v1.x + v2.x,v1.y + v2.y, v1.z + v2.z);
} else {
target.set(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
}
return target;
}
/**
* Subtract a vector from this vector
* @param v the vector to be subtracted
*/
public void sub(PVector v) {
x -= v.x;
y -= v.y;
z -= v.z;
}
public void sub(float x, float y, float z) {
this.x -= x;
this.y -= y;
this.z -= z;
}
/**
* Subtract one vector from another
* @param v1 a vector
* @param v2 another vector
* @return a new vector that is v1 - v2
*/
static public PVector sub(PVector v1, PVector v2) {
return sub(v1, v2, null);
}
static public PVector sub(PVector v1, PVector v2, PVector target) {
if (target == null) {
target = new PVector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
} else {
target.set(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
}
return target;
}
/**
* Multiply this vector by a scalar
* @param n the value to multiply by
*/
public void mult(float n) {
x *= n;
y *= n;
z *= n;
}
/**
* Multiply a vector by a scalar
* @param v a vector
* @param n scalar
* @return a new vector that is v1 * n
*/
static public PVector mult(PVector v, float n) {
return mult(v, n, null);
}
/**
* Multiply a vector by a scalar, and write the result into a target PVector.
* @param v a vector
* @param n scalar
* @param target PVector to store the result
* @return the target vector, now set to v1 * n
*/
static public PVector mult(PVector v, float n, PVector target) {
if (target == null) {
target = new PVector(v.x*n, v.y*n, v.z*n);
} else {
target.set(v.x*n, v.y*n, v.z*n);
}
return target;
}
/**
* Multiply each element of one vector by the elements of another vector.
* @param v the vector to multiply by
*/
public void mult(PVector v) {
x *= v.x;
y *= v.y;
z *= v.z;
}
/**
* Multiply each element of one vector by the individual elements of another
* vector, and return the result as a new PVector.
*/
static public PVector mult(PVector v1, PVector v2) {
return mult(v1, v2, null);
}
/**
* Multiply each element of one vector by the individual elements of another
* vector, and write the result into a target vector.
* @param v1 the first vector
* @param v2 the second vector
* @param target PVector to store the result
*/
static public PVector mult(PVector v1, PVector v2, PVector target) {
if (target == null) {
target = new PVector(v1.x*v2.x, v1.y*v2.y, v1.z*v2.z);
} else {
target.set(v1.x*v2.x, v1.y*v2.y, v1.z*v2.z);
}
return target;
}
/**
* Divide this vector by a scalar
* @param n the value to divide by
*/
public void div(float n) {
x /= n;
y /= n;
z /= n;
}
/**
* Divide a vector by a scalar and return the result in a new vector.
* @param v a vector
* @param n scalar
* @return a new vector that is v1 / n
*/
static public PVector div(PVector v, float n) {
return div(v, n, null);
}
static public PVector div(PVector v, float n, PVector target) {
if (target == null) {
target = new PVector(v.x/n, v.y/n, v.z/n);
} else {
target.set(v.x/n, v.y/n, v.z/n);
}
return target;
}
/**
* Divide each element of one vector by the elements of another vector.
*/
public void div(PVector v) {
x /= v.x;
y /= v.y;
z /= v.z;
}
/**
* Multiply each element of one vector by the individual elements of another
* vector, and return the result as a new PVector.
*/
static public PVector div(PVector v1, PVector v2) {
return div(v1, v2, null);
}
/**
* Divide each element of one vector by the individual elements of another
* vector, and write the result into a target vector.
* @param v1 the first vector
* @param v2 the second vector
* @param target PVector to store the result
*/
static public PVector div(PVector v1, PVector v2, PVector target) {
if (target == null) {
target = new PVector(v1.x/v2.x, v1.y/v2.y, v1.z/v2.z);
} else {
target.set(v1.x/v2.x, v1.y/v2.y, v1.z/v2.z);
}
return target;
}
/**
* Calculate the Euclidean distance between two points (considering a point as a vector object)
* @param v another vector
* @return the Euclidean distance between
*/
public float dist(PVector v) {
float dx = x - v.x;
float dy = y - v.y;
float dz = z - v.z;
return (float) Math.sqrt(dx*dx + dy*dy + dz*dz);
}
/**
* Calculate the Euclidean distance between two points (considering a point as a vector object)
* @param v1 a vector
* @param v2 another vector
* @return the Euclidean distance between v1 and v2
*/
static public float dist(PVector v1, PVector v2) {
float dx = v1.x - v2.x;
float dy = v1.y - v2.y;
float dz = v1.z - v2.z;
return (float) Math.sqrt(dx*dx + dy*dy + dz*dz);
}
/**
* Calculate the dot product with another vector
* @return the dot product
*/
public float dot(PVector v) {
return x*v.x + y*v.y + z*v.z;
}
public float dot(float x, float y, float z) {
return this.x*x + this.y*y + this.z*z;
}
/**
* Return a vector composed of the cross product between this and another.
*/
public PVector cross(PVector v) {
return cross(v, null);
}
/**
* Perform cross product between this and another vector, and store the
* result in 'target'. If target is null, a new vector is created.
*/
public PVector cross(PVector v, PVector target) {
float crossX = y * v.z - v.y * z;
float crossY = z * v.x - v.z * x;
float crossZ = x * v.y - v.x * y;
if (target == null) {
target = new PVector(crossX, crossY, crossZ);
} else {
target.set(crossX, crossY, crossZ);
}
return target;
}
static public PVector cross(PVector v1, PVector v2, PVector target) {
float crossX = v1.y * v2.z - v2.y * v1.z;
float crossY = v1.z * v2.x - v2.z * v1.x;
float crossZ = v1.x * v2.y - v2.x * v1.y;
if (target == null) {
target = new PVector(crossX, crossY, crossZ);
} else {
target.set(crossX, crossY, crossZ);
}
return target;
}
/**
* Normalize the vector to length 1 (make it a unit vector)
*/
public void normalize() {
float m = mag();
if (m != 0 && m != 1) {
div(m);
}
}
/**
* Normalize this vector, storing the result in another vector.
* @param target Set to null to create a new vector
* @return a new vector (if target was null), or target
*/
public PVector normalize(PVector target) {
if (target == null) {
target = new PVector();
}
float m = mag();
if (m > 0) {
target.set(x/m, y/m, z/m);
} else {
target.set(x, y, z);
}
return target;
}
/**
* Limit the magnitude of this vector
* @param max the maximum length to limit this vector
*/
public void limit(float max) {
if (mag() > max) {
normalize();
mult(max);
}
}
/**
* Calculate the angle of rotation for this vector (only 2D vectors)
* @return the angle of rotation
*/
public float heading2D() {
float angle = (float) Math.atan2(-y, x);
return -1*angle;
}
/**
* Calculate the angle between two vectors, using the dot product
* @param v1 a vector
* @param v2 another vector
* @return the angle between the vectors
*/
static public float angleBetween(PVector v1, PVector v2) {
float dot = v1.dot(v2);
float theta = (float) Math.acos(dot / (v1.mag() * v2.mag()));
return theta;
}
public String toString() {
return "[ " + x + ", " + y + ", " + z + " ]";
}
/**
* Return a representation of this vector as a float array. This is only for
* temporary use. If used in any other fashion, the contents should be copied
* by using the get() command to copy into your own array.
*/
public float[] array() {
if (array == null) {
array = new float[3];
}
array[0] = x;
array[1] = y;
array[2] = z;
return array;
}
}

View File

@ -0,0 +1,193 @@
/* CDATAReader.java NanoXML/Java
*
* $Revision: 1.3 $
* $Date: 2002/01/04 21:03:28 $
* $Name: RELEASE_2_2_1 $
*
* This file is part of NanoXML 2 for Java.
* Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
package processing.xml;
import java.io.Reader;
import java.io.IOException;
/**
* This reader reads data from another reader until the end of a CDATA section
* (]]&gt;) has been encountered.
*
* @author Marc De Scheemaecker
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.3 $
*/
class CDATAReader
extends Reader
{
/**
* The encapsulated reader.
*/
private StdXMLReader reader;
/**
* Saved char.
*/
private char savedChar;
/**
* True if the end of the stream has been reached.
*/
private boolean atEndOfData;
/**
* Creates the reader.
*
* @param reader the encapsulated reader
*/
CDATAReader(StdXMLReader reader)
{
this.reader = reader;
this.savedChar = 0;
this.atEndOfData = false;
}
/**
* Cleans up the object when it's destroyed.
*/
protected void finalize()
throws Throwable
{
this.reader = null;
super.finalize();
}
/**
* Reads a block of data.
*
* @param buffer where to put the read data
* @param offset first position in buffer to put the data
* @param size maximum number of chars to read
*
* @return the number of chars read, or -1 if at EOF
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
public int read(char[] buffer,
int offset,
int size)
throws IOException
{
int charsRead = 0;
if (this.atEndOfData) {
return -1;
}
if ((offset + size) > buffer.length) {
size = buffer.length - offset;
}
while (charsRead < size) {
char ch = this.savedChar;
if (ch == 0) {
ch = this.reader.read();
} else {
this.savedChar = 0;
}
if (ch == ']') {
char ch2 = this.reader.read();
if (ch2 == ']') {
char ch3 = this.reader.read();
if (ch3 == '>') {
this.atEndOfData = true;
break;
}
this.savedChar = ch2;
this.reader.unread(ch3);
} else {
this.reader.unread(ch2);
}
}
buffer[charsRead] = ch;
charsRead++;
}
if (charsRead == 0) {
charsRead = -1;
}
return charsRead;
}
/**
* Skips remaining data and closes the stream.
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
public void close()
throws IOException
{
while (! this.atEndOfData) {
char ch = this.savedChar;
if (ch == 0) {
ch = this.reader.read();
} else {
this.savedChar = 0;
}
if (ch == ']') {
char ch2 = this.reader.read();
if (ch2 == ']') {
char ch3 = this.reader.read();
if (ch3 == '>') {
break;
}
this.savedChar = ch2;
this.reader.unread(ch3);
} else {
this.reader.unread(ch2);
}
}
}
this.atEndOfData = true;
}
}

View File

@ -0,0 +1,212 @@
/* ContentReader.java NanoXML/Java
*
* $Revision: 1.4 $
* $Date: 2002/01/04 21:03:28 $
* $Name: RELEASE_2_2_1 $
*
* This file is part of NanoXML 2 for Java.
* Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
package processing.xml;
import java.io.Reader;
import java.io.IOException;
/**
* This reader reads data from another reader until a new element has
* been encountered.
*
* @author Marc De Scheemaecker
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
*/
class ContentReader
extends Reader
{
/**
* The encapsulated reader.
*/
private StdXMLReader reader;
/**
* Buffer.
*/
private String buffer;
/**
* Pointer into the buffer.
*/
private int bufferIndex;
/**
* The entity resolver.
*/
private XMLEntityResolver resolver;
/**
* Creates the reader.
*
* @param reader the encapsulated reader
* @param resolver the entity resolver
* @param buffer data that has already been read from <code>reader</code>
*/
ContentReader(StdXMLReader reader,
XMLEntityResolver resolver,
String buffer)
{
this.reader = reader;
this.resolver = resolver;
this.buffer = buffer;
this.bufferIndex = 0;
}
/**
* Cleans up the object when it's destroyed.
*/
protected void finalize()
throws Throwable
{
this.reader = null;
this.resolver = null;
this.buffer = null;
super.finalize();
}
/**
* Reads a block of data.
*
* @param outputBuffer where to put the read data
* @param offset first position in buffer to put the data
* @param size maximum number of chars to read
*
* @return the number of chars read, or -1 if at EOF
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
public int read(char[] outputBuffer,
int offset,
int size)
throws IOException
{
try {
int charsRead = 0;
int bufferLength = this.buffer.length();
if ((offset + size) > outputBuffer.length) {
size = outputBuffer.length - offset;
}
while (charsRead < size) {
String str = "";
char ch;
if (this.bufferIndex >= bufferLength) {
str = XMLUtil.read(this.reader, '&');
ch = str.charAt(0);
} else {
ch = this.buffer.charAt(this.bufferIndex);
this.bufferIndex++;
outputBuffer[charsRead] = ch;
charsRead++;
continue; // don't interprete chars in the buffer
}
if (ch == '<') {
this.reader.unread(ch);
break;
}
if ((ch == '&') && (str.length() > 1)) {
if (str.charAt(1) == '#') {
ch = XMLUtil.processCharLiteral(str);
} else {
XMLUtil.processEntity(str, this.reader, this.resolver);
continue;
}
}
outputBuffer[charsRead] = ch;
charsRead++;
}
if (charsRead == 0) {
charsRead = -1;
}
return charsRead;
} catch (XMLParseException e) {
throw new IOException(e.getMessage());
}
}
/**
* Skips remaining data and closes the stream.
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
public void close()
throws IOException
{
try {
int bufferLength = this.buffer.length();
for (;;) {
String str = "";
char ch;
if (this.bufferIndex >= bufferLength) {
str = XMLUtil.read(this.reader, '&');
ch = str.charAt(0);
} else {
ch = this.buffer.charAt(this.bufferIndex);
this.bufferIndex++;
continue; // don't interprete chars in the buffer
}
if (ch == '<') {
this.reader.unread(ch);
break;
}
if ((ch == '&') && (str.length() > 1)) {
if (str.charAt(1) != '#') {
XMLUtil.processEntity(str, this.reader, this.resolver);
}
}
}
} catch (XMLParseException e) {
throw new IOException(e.getMessage());
}
}
}

View File

@ -0,0 +1,157 @@
/* PIReader.java NanoXML/Java
*
* $Revision: 1.3 $
* $Date: 2002/01/04 21:03:28 $
* $Name: RELEASE_2_2_1 $
*
* This file is part of NanoXML 2 for Java.
* Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
package processing.xml;
import java.io.Reader;
import java.io.IOException;
/**
* This reader reads data from another reader until the end of a processing
* instruction (?&gt;) has been encountered.
*
* @author Marc De Scheemaecker
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.3 $
*/
class PIReader
extends Reader
{
/**
* The encapsulated reader.
*/
private StdXMLReader reader;
/**
* True if the end of the stream has been reached.
*/
private boolean atEndOfData;
/**
* Creates the reader.
*
* @param reader the encapsulated reader
*/
PIReader(StdXMLReader reader)
{
this.reader = reader;
this.atEndOfData = false;
}
/**
* Cleans up the object when it's destroyed.
*/
protected void finalize()
throws Throwable
{
this.reader = null;
super.finalize();
}
/**
* Reads a block of data.
*
* @param buffer where to put the read data
* @param offset first position in buffer to put the data
* @param size maximum number of chars to read
*
* @return the number of chars read, or -1 if at EOF
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
public int read(char[] buffer,
int offset,
int size)
throws IOException
{
if (this.atEndOfData) {
return -1;
}
int charsRead = 0;
if ((offset + size) > buffer.length) {
size = buffer.length - offset;
}
while (charsRead < size) {
char ch = this.reader.read();
if (ch == '?') {
char ch2 = this.reader.read();
if (ch2 == '>') {
this.atEndOfData = true;
break;
}
this.reader.unread(ch2);
}
buffer[charsRead] = ch;
charsRead++;
}
if (charsRead == 0) {
charsRead = -1;
}
return charsRead;
}
/**
* Skips remaining data and closes the stream.
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
public void close()
throws IOException
{
while (! this.atEndOfData) {
char ch = this.reader.read();
if (ch == '?') {
char ch2 = this.reader.read();
if (ch2 == '>') {
this.atEndOfData = true;
}
}
}
}
}

View File

@ -0,0 +1,356 @@
/* StdXMLBuilder.java NanoXML/Java
*
* $Revision: 1.3 $
* $Date: 2002/01/04 21:03:28 $
* $Name: RELEASE_2_2_1 $
*
* This file is part of NanoXML 2 for Java.
* Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
package processing.xml;
import java.io.IOException;
import java.io.Reader;
import java.util.Stack;
/**
* StdXMLBuilder is a concrete implementation of IXMLBuilder which creates a
* tree of IXMLElement from an XML data source.
*
* @see processing.xml.XMLElement
*
* @author Marc De Scheemaecker
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.3 $
*/
public class StdXMLBuilder
{
/**
* This stack contains the current element and its parents.
*/
private Stack<XMLElement> stack;
/**
* The root element of the parsed XML tree.
*/
private XMLElement root;
private XMLElement parent;
/**
* Prototype element for creating the tree.
*/
//private XMLElement prototype;
/**
* Creates the builder.
*/
public StdXMLBuilder()
{
this.stack = null;
this.root = null;
//this(new XMLElement());
}
public StdXMLBuilder(XMLElement parent)
{
this.parent = parent;
}
/**
* Creates the builder.
*
* @param prototype the prototype to use when building the tree.
*/
// public StdXMLBuilder(XMLElement prototype)
// {
// this.stack = null;
// this.root = null;
// this.prototype = prototype;
// }
/**
* Cleans up the object when it's destroyed.
*/
protected void finalize()
throws Throwable
{
//this.prototype = null;
this.root = null;
this.stack.clear();
this.stack = null;
super.finalize();
}
/**
* This method is called before the parser starts processing its input.
*
* @param systemID the system ID of the XML data source.
* @param lineNr the line on which the parsing starts.
*/
public void startBuilding(String systemID,
int lineNr)
{
this.stack = new Stack<XMLElement>();
this.root = null;
}
/**
* This method is called when a processing instruction is encountered.
* PIs with target "xml" are handled by the parser.
*
* @param target the PI target.
* @param reader to read the data from the PI.
*/
public void newProcessingInstruction(String target,
Reader reader)
{
// nothing to do
}
/**
* This method is called when a new XML element is encountered.
*
* @see #endElement
*
* @param name the name of the element.
* @param nsPrefix the prefix used to identify the namespace. If no
* namespace has been specified, this parameter is null.
* @param nsURI the URI associated with the namespace. If no
* namespace has been specified, or no URI is
* associated with nsPrefix, this parameter is null.
* @param systemID the system ID of the XML data source.
* @param lineNr the line in the source where the element starts.
*/
public void startElement(String name,
String nsPrefix,
String nsURI,
String systemID,
int lineNr)
{
String fullName = name;
if (nsPrefix != null) {
fullName = nsPrefix + ':' + name;
}
//XMLElement elt = this.prototype.createElement(fullName, nsURI,
// systemID, lineNr);
// XMLElement elt = new XMLElement(fullName, nsURI, systemID, lineNr);
//
// if (this.stack.empty()) {
// this.root = elt;
// } else {
// XMLElement top = (XMLElement) this.stack.peek();
// top.addChild(elt);
// }
// stack.push(elt);
if (this.stack.empty()) {
//System.out.println("setting root");
parent.set(fullName, nsURI, systemID, lineNr);
stack.push(parent);
root = parent;
} else {
XMLElement top = (XMLElement) this.stack.peek();
//System.out.println("stack has " + top.getName());
XMLElement elt = new XMLElement(fullName, nsURI, systemID, lineNr);
top.addChild(elt);
stack.push(elt);
}
}
/**
* This method is called when the attributes of an XML element have been
* processed.
*
* @see #startElement
* @see #addAttribute
*
* @param name the name of the element.
* @param nsPrefix the prefix used to identify the namespace. If no
* namespace has been specified, this parameter is null.
* @param nsURI the URI associated with the namespace. If no
* namespace has been specified, or no URI is
* associated with nsPrefix, this parameter is null.
*/
public void elementAttributesProcessed(String name,
String nsPrefix,
String nsURI)
{
// nothing to do
}
/**
* This method is called when the end of an XML elemnt is encountered.
*
* @see #startElement
*
* @param name the name of the element.
* @param nsPrefix the prefix used to identify the namespace. If no
* namespace has been specified, this parameter is null.
* @param nsURI the URI associated with the namespace. If no
* namespace has been specified, or no URI is
* associated with nsPrefix, this parameter is null.
*/
public void endElement(String name,
String nsPrefix,
String nsURI)
{
XMLElement elt = (XMLElement) this.stack.pop();
if (elt.getChildCount() == 1) {
XMLElement child = elt.getChildAtIndex(0);
if (child.getLocalName() == null) {
elt.setContent(child.getContent());
elt.removeChildAtIndex(0);
}
}
}
/**
* This method is called when a new attribute of an XML element is
* encountered.
*
* @param key the key (name) of the attribute.
* @param nsPrefix the prefix used to identify the namespace. If no
* namespace has been specified, this parameter is null.
* @param nsURI the URI associated with the namespace. If no
* namespace has been specified, or no URI is
* associated with nsPrefix, this parameter is null.
* @param value the value of the attribute.
* @param type the type of the attribute. If no type is known,
* "CDATA" is returned.
*
* @throws java.lang.Exception
* If an exception occurred while processing the event.
*/
public void addAttribute(String key,
String nsPrefix,
String nsURI,
String value,
String type)
throws Exception
{
String fullName = key;
if (nsPrefix != null) {
fullName = nsPrefix + ':' + key;
}
XMLElement top = (XMLElement) this.stack.peek();
if (top.hasAttribute(fullName)) {
throw new XMLParseException(top.getSystemID(),
top.getLineNr(),
"Duplicate attribute: " + key);
}
if (nsPrefix != null) {
top.setAttribute(fullName, nsURI, value);
} else {
top.setAttribute(fullName, value);
}
}
/**
* This method is called when a PCDATA element is encountered. A Java
* reader is supplied from which you can read the data. The reader will
* only read the data of the element. You don't need to check for
* boundaries. If you don't read the full element, the rest of the data
* is skipped. You also don't have to care about entities; they are
* resolved by the parser.
*
* @param reader the Java reader from which you can retrieve the data.
* @param systemID the system ID of the XML data source.
* @param lineNr the line in the source where the element starts.
*/
public void addPCData(Reader reader,
String systemID,
int lineNr)
{
int bufSize = 2048;
int sizeRead = 0;
StringBuffer str = new StringBuffer(bufSize);
char[] buf = new char[bufSize];
for (;;) {
if (sizeRead >= bufSize) {
bufSize *= 2;
str.ensureCapacity(bufSize);
}
int size;
try {
size = reader.read(buf);
} catch (IOException e) {
break;
}
if (size < 0) {
break;
}
str.append(buf, 0, size);
sizeRead += size;
}
//XMLElement elt = this.prototype.createElement(null, systemID, lineNr);
XMLElement elt = new XMLElement(null, null, systemID, lineNr);
elt.setContent(str.toString());
if (! this.stack.empty()) {
XMLElement top = (XMLElement) this.stack.peek();
top.addChild(elt);
}
}
/**
* Returns the result of the building process. This method is called just
* before the <I>parse</I> method of StdXMLParser returns.
*
* @return the result of the building process.
*/
public Object getResult()
{
return this.root;
}
}

View File

@ -0,0 +1,684 @@
/* StdXMLParser.java NanoXML/Java
*
* $Revision: 1.5 $
* $Date: 2002/03/24 11:37:00 $
* $Name: RELEASE_2_2_1 $
*
* This file is part of NanoXML 2 for Java.
* Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
package processing.xml;
import java.io.Reader;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;
/**
* StdXMLParser is the core parser of NanoXML.
*
* @author Marc De Scheemaecker
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.5 $
*/
public class StdXMLParser {
/**
* The builder which creates the logical structure of the XML data.
*/
private StdXMLBuilder builder;
/**
* The reader from which the parser retrieves its data.
*/
private StdXMLReader reader;
/**
* The entity resolver.
*/
private XMLEntityResolver entityResolver;
/**
* The validator that will process entity references and validate the XML
* data.
*/
private XMLValidator validator;
/**
* Creates a new parser.
*/
public StdXMLParser()
{
this.builder = null;
this.validator = null;
this.reader = null;
this.entityResolver = new XMLEntityResolver();
}
/**
* Cleans up the object when it's destroyed.
*/
protected void finalize()
throws Throwable
{
this.builder = null;
this.reader = null;
this.entityResolver = null;
this.validator = null;
super.finalize();
}
/**
* Sets the builder which creates the logical structure of the XML data.
*
* @param builder the non-null builder
*/
public void setBuilder(StdXMLBuilder builder)
{
this.builder = builder;
}
/**
* Returns the builder which creates the logical structure of the XML data.
*
* @return the builder
*/
public StdXMLBuilder getBuilder()
{
return this.builder;
}
/**
* Sets the validator that validates the XML data.
*
* @param validator the non-null validator
*/
public void setValidator(XMLValidator validator)
{
this.validator = validator;
}
/**
* Returns the validator that validates the XML data.
*
* @return the validator
*/
public XMLValidator getValidator()
{
return this.validator;
}
/**
* Sets the entity resolver.
*
* @param resolver the non-null resolver
*/
public void setResolver(XMLEntityResolver resolver)
{
this.entityResolver = resolver;
}
/**
* Returns the entity resolver.
*
* @return the non-null resolver
*/
public XMLEntityResolver getResolver()
{
return this.entityResolver;
}
/**
* Sets the reader from which the parser retrieves its data.
*
* @param reader the reader
*/
public void setReader(StdXMLReader reader)
{
this.reader = reader;
}
/**
* Returns the reader from which the parser retrieves its data.
*
* @return the reader
*/
public StdXMLReader getReader()
{
return this.reader;
}
/**
* Parses the data and lets the builder create the logical data structure.
*
* @return the logical structure built by the builder
*
* @throws net.n3.nanoxml.XMLException
* if an error occurred reading or parsing the data
*/
public Object parse()
throws XMLException
{
try {
this.builder.startBuilding(this.reader.getSystemID(),
this.reader.getLineNr());
this.scanData();
return this.builder.getResult();
} catch (XMLException e) {
throw e;
} catch (Exception e) {
throw new XMLException(e);
}
}
/**
* Scans the XML data for elements.
*
* @throws java.lang.Exception
* if something went wrong
*/
protected void scanData() throws Exception {
while ((! this.reader.atEOF()) && (this.builder.getResult() == null)) {
String str = XMLUtil.read(this.reader, '&');
char ch = str.charAt(0);
if (ch == '&') {
XMLUtil.processEntity(str, this.reader, this.entityResolver);
continue;
}
switch (ch) {
case '<':
this.scanSomeTag(false, // don't allow CDATA
null, // no default namespace
new Properties());
break;
case ' ':
case '\t':
case '\r':
case '\n':
// skip whitespace
break;
default:
XMLUtil.errorInvalidInput(reader.getSystemID(),
reader.getLineNr(),
"`" + ch + "' (0x"
+ Integer.toHexString((int) ch)
+ ')');
}
}
}
/**
* Scans an XML tag.
*
* @param allowCDATA true if CDATA sections are allowed at this point
* @param defaultNamespace the default namespace URI (or null)
* @param namespaces list of defined namespaces
*
* @throws java.lang.Exception
* if something went wrong
*/
protected void scanSomeTag(boolean allowCDATA,
String defaultNamespace,
Properties namespaces)
throws Exception
{
String str = XMLUtil.read(this.reader, '&');
char ch = str.charAt(0);
if (ch == '&') {
XMLUtil.errorUnexpectedEntity(reader.getSystemID(),
reader.getLineNr(),
str);
}
switch (ch) {
case '?':
this.processPI();
break;
case '!':
this.processSpecialTag(allowCDATA);
break;
default:
this.reader.unread(ch);
this.processElement(defaultNamespace, namespaces);
}
}
/**
* Processes a "processing instruction".
*
* @throws java.lang.Exception
* if something went wrong
*/
protected void processPI()
throws Exception
{
XMLUtil.skipWhitespace(this.reader, null);
String target = XMLUtil.scanIdentifier(this.reader);
XMLUtil.skipWhitespace(this.reader, null);
Reader r = new PIReader(this.reader);
if (!target.equalsIgnoreCase("xml")) {
this.builder.newProcessingInstruction(target, r);
}
r.close();
}
/**
* Processes a tag that starts with a bang (&lt;!...&gt;).
*
* @param allowCDATA true if CDATA sections are allowed at this point
*
* @throws java.lang.Exception
* if something went wrong
*/
protected void processSpecialTag(boolean allowCDATA)
throws Exception
{
String str = XMLUtil.read(this.reader, '&');
char ch = str.charAt(0);
if (ch == '&') {
XMLUtil.errorUnexpectedEntity(reader.getSystemID(),
reader.getLineNr(),
str);
}
switch (ch) {
case '[':
if (allowCDATA) {
this.processCDATA();
} else {
XMLUtil.errorUnexpectedCDATA(reader.getSystemID(),
reader.getLineNr());
}
return;
case 'D':
this.processDocType();
return;
case '-':
XMLUtil.skipComment(this.reader);
return;
}
}
/**
* Processes a CDATA section.
*
* @throws java.lang.Exception
* if something went wrong
*/
protected void processCDATA() throws Exception {
if (! XMLUtil.checkLiteral(this.reader, "CDATA[")) {
XMLUtil.errorExpectedInput(reader.getSystemID(),
reader.getLineNr(),
"<![[CDATA[");
}
this.validator.PCDataAdded(this.reader.getSystemID(),
this.reader.getLineNr());
Reader r = new CDATAReader(this.reader);
this.builder.addPCData(r, this.reader.getSystemID(),
this.reader.getLineNr());
r.close();
}
/**
* Processes a document type declaration.
*
* @throws java.lang.Exception
* if an error occurred reading or parsing the data
*/
protected void processDocType() throws Exception {
if (! XMLUtil.checkLiteral(this.reader, "OCTYPE")) {
XMLUtil.errorExpectedInput(reader.getSystemID(),
reader.getLineNr(),
"<!DOCTYPE");
return;
}
XMLUtil.skipWhitespace(this.reader, null);
String systemID = null;
StringBuffer publicID = new StringBuffer();
/*String rootElement =*/ XMLUtil.scanIdentifier(this.reader);
//System.out.println("rootElement is " + rootElement);
XMLUtil.skipWhitespace(this.reader, null);
char ch = this.reader.read();
if (ch == 'P') {
systemID = XMLUtil.scanPublicID(publicID, reader);
XMLUtil.skipWhitespace(this.reader, null);
ch = this.reader.read();
} else if (ch == 'S') {
systemID = XMLUtil.scanSystemID(reader);
XMLUtil.skipWhitespace(this.reader, null);
ch = this.reader.read();
}
if (ch == '[') {
this.validator.parseDTD(publicID.toString(),
this.reader,
this.entityResolver,
false);
XMLUtil.skipWhitespace(this.reader, null);
ch = this.reader.read();
}
if (ch != '>') {
XMLUtil.errorExpectedInput(reader.getSystemID(),
reader.getLineNr(),
"`>'");
}
// TODO DTD checking is currently disabled, because it breaks
// applications that don't have access to a net connection
// (since it insists on going and checking out the DTD).
if (false) {
if (systemID != null) {
Reader r = this.reader.openStream(publicID.toString(), systemID);
this.reader.startNewStream(r);
this.reader.setSystemID(systemID);
this.reader.setPublicID(publicID.toString());
this.validator.parseDTD(publicID.toString(),
this.reader,
this.entityResolver,
true);
}
}
}
/**
* Processes a regular element.
*
* @param defaultNamespace the default namespace URI (or null)
* @param namespaces list of defined namespaces
*
* @throws java.lang.Exception
* if something went wrong
*/
protected void processElement(String defaultNamespace,
Properties namespaces)
throws Exception
{
String fullName = XMLUtil.scanIdentifier(this.reader);
String name = fullName;
XMLUtil.skipWhitespace(this.reader, null);
String prefix = null;
int colonIndex = name.indexOf(':');
if (colonIndex > 0) {
prefix = name.substring(0, colonIndex);
name = name.substring(colonIndex + 1);
}
Vector<String> attrNames = new Vector<String>();
Vector<String> attrValues = new Vector<String>();
Vector<String> attrTypes = new Vector<String>();
this.validator.elementStarted(fullName,
this.reader.getSystemID(),
this.reader.getLineNr());
char ch;
for (;;) {
ch = this.reader.read();
if ((ch == '/') || (ch == '>')) {
break;
}
this.reader.unread(ch);
this.processAttribute(attrNames, attrValues, attrTypes);
XMLUtil.skipWhitespace(this.reader, null);
}
Properties extraAttributes = new Properties();
this.validator.elementAttributesProcessed(fullName,
extraAttributes,
this.reader.getSystemID(),
this.reader.getLineNr());
Enumeration<?> en = extraAttributes.keys();
while (en.hasMoreElements()) {
String key = (String) en.nextElement();
String value = extraAttributes.getProperty(key);
attrNames.addElement(key);
attrValues.addElement(value);
attrTypes.addElement("CDATA");
}
for (int i = 0; i < attrNames.size(); i++) {
String key = (String) attrNames.elementAt(i);
String value = (String) attrValues.elementAt(i);
//String type = (String) attrTypes.elementAt(i);
if (key.equals("xmlns")) {
defaultNamespace = value;
} else if (key.startsWith("xmlns:")) {
namespaces.put(key.substring(6), value);
}
}
if (prefix == null) {
this.builder.startElement(name, prefix, defaultNamespace,
this.reader.getSystemID(),
this.reader.getLineNr());
} else {
this.builder.startElement(name, prefix,
namespaces.getProperty(prefix),
this.reader.getSystemID(),
this.reader.getLineNr());
}
for (int i = 0; i < attrNames.size(); i++) {
String key = (String) attrNames.elementAt(i);
if (key.startsWith("xmlns")) {
continue;
}
String value = (String) attrValues.elementAt(i);
String type = (String) attrTypes.elementAt(i);
colonIndex = key.indexOf(':');
if (colonIndex > 0) {
String attPrefix = key.substring(0, colonIndex);
key = key.substring(colonIndex + 1);
this.builder.addAttribute(key, attPrefix,
namespaces.getProperty(attPrefix),
value, type);
} else {
this.builder.addAttribute(key, null, null, value, type);
}
}
if (prefix == null) {
this.builder.elementAttributesProcessed(name, prefix,
defaultNamespace);
} else {
this.builder.elementAttributesProcessed(name, prefix,
namespaces
.getProperty(prefix));
}
if (ch == '/') {
if (this.reader.read() != '>') {
XMLUtil.errorExpectedInput(reader.getSystemID(),
reader.getLineNr(),
"`>'");
}
this.validator.elementEnded(name,
this.reader.getSystemID(),
this.reader.getLineNr());
if (prefix == null) {
this.builder.endElement(name, prefix, defaultNamespace);
} else {
this.builder.endElement(name, prefix,
namespaces.getProperty(prefix));
}
return;
}
StringBuffer buffer = new StringBuffer(16);
for (;;) {
buffer.setLength(0);
String str;
for (;;) {
XMLUtil.skipWhitespace(this.reader, buffer);
str = XMLUtil.read(this.reader, '&');
if ((str.charAt(0) == '&') && (str.charAt(1) != '#')) {
XMLUtil.processEntity(str, this.reader,
this.entityResolver);
} else {
break;
}
}
if (str.charAt(0) == '<') {
str = XMLUtil.read(this.reader, '\0');
if (str.charAt(0) == '/') {
XMLUtil.skipWhitespace(this.reader, null);
str = XMLUtil.scanIdentifier(this.reader);
if (! str.equals(fullName)) {
XMLUtil.errorWrongClosingTag(reader.getSystemID(),
reader.getLineNr(),
name, str);
}
XMLUtil.skipWhitespace(this.reader, null);
if (this.reader.read() != '>') {
XMLUtil.errorClosingTagNotEmpty(reader.getSystemID(),
reader.getLineNr());
}
this.validator.elementEnded(fullName,
this.reader.getSystemID(),
this.reader.getLineNr());
if (prefix == null) {
this.builder.endElement(name, prefix, defaultNamespace);
} else {
this.builder.endElement(name, prefix,
namespaces.getProperty(prefix));
}
break;
} else { // <[^/]
this.reader.unread(str.charAt(0));
this.scanSomeTag(true, //CDATA allowed
defaultNamespace,
(Properties) namespaces.clone());
}
} else { // [^<]
if (str.charAt(0) == '&') {
ch = XMLUtil.processCharLiteral(str);
buffer.append(ch);
} else {
reader.unread(str.charAt(0));
}
this.validator.PCDataAdded(this.reader.getSystemID(),
this.reader.getLineNr());
Reader r = new ContentReader(this.reader,
this.entityResolver,
buffer.toString());
this.builder.addPCData(r, this.reader.getSystemID(),
this.reader.getLineNr());
r.close();
}
}
}
/**
* Processes an attribute of an element.
*
* @param attrNames contains the names of the attributes.
* @param attrValues contains the values of the attributes.
* @param attrTypes contains the types of the attributes.
*
* @throws java.lang.Exception
* if something went wrong
*/
protected void processAttribute(Vector<String> attrNames,
Vector<String> attrValues,
Vector<String> attrTypes)
throws Exception
{
String key = XMLUtil.scanIdentifier(this.reader);
XMLUtil.skipWhitespace(this.reader, null);
if (! XMLUtil.read(this.reader, '&').equals("=")) {
XMLUtil.errorExpectedInput(reader.getSystemID(),
reader.getLineNr(),
"`='");
}
XMLUtil.skipWhitespace(this.reader, null);
String value = XMLUtil.scanString(this.reader, '&',
this.entityResolver);
attrNames.addElement(key);
attrValues.addElement(value);
attrTypes.addElement("CDATA");
this.validator.attributeAdded(key, value,
this.reader.getSystemID(),
this.reader.getLineNr());
}
}

View File

@ -0,0 +1,626 @@
/* StdXMLReader.java NanoXML/Java
*
* $Revision: 1.4 $
* $Date: 2002/01/04 21:03:28 $
* $Name: RELEASE_2_2_1 $
*
* This file is part of NanoXML 2 for Java.
* Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
package processing.xml;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
//import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.LineNumberReader;
import java.io.PushbackReader;
import java.io.PushbackInputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Stack;
/**
* StdXMLReader reads the data to be parsed.
*
* @author Marc De Scheemaecker
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
*/
public class StdXMLReader
{
/**
* A stacked reader.
*
* @author Marc De Scheemaecker
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
*/
private class StackedReader
{
PushbackReader pbReader;
LineNumberReader lineReader;
URL systemId;
String publicId;
}
/**
* The stack of readers.
*/
private Stack<StackedReader> readers;
/**
* The current push-back reader.
*/
private StackedReader currentReader;
/**
* Creates a new reader using a string as input.
*
* @param str the string containing the XML data
*/
public static StdXMLReader stringReader(String str)
{
return new StdXMLReader(new StringReader(str));
}
/**
* Creates a new reader using a file as input.
*
* @param filename the name of the file containing the XML data
*
* @throws java.io.FileNotFoundException
* if the file could not be found
* @throws java.io.IOException
* if an I/O error occurred
*/
public static StdXMLReader fileReader(String filename)
throws FileNotFoundException,
IOException
{
StdXMLReader r = new StdXMLReader(new FileInputStream(filename));
r.setSystemID(filename);
for (int i = 0; i < r.readers.size(); i++) {
StackedReader sr = (StackedReader) r.readers.elementAt(i);
sr.systemId = r.currentReader.systemId;
}
return r;
}
/**
* Initializes the reader from a system and public ID.
*
* @param publicID the public ID which may be null.
* @param systemID the non-null system ID.
*
* @throws MalformedURLException
* if the system ID does not contain a valid URL
* @throws FileNotFoundException
* if the system ID refers to a local file which does not exist
* @throws IOException
* if an error occurred opening the stream
*/
public StdXMLReader(String publicID,
String systemID)
throws MalformedURLException,
FileNotFoundException,
IOException
{
URL systemIDasURL = null;
try {
systemIDasURL = new URL(systemID);
} catch (MalformedURLException e) {
systemID = "file:" + systemID;
try {
systemIDasURL = new URL(systemID);
} catch (MalformedURLException e2) {
throw e;
}
}
this.currentReader = new StackedReader();
this.readers = new Stack<StackedReader>();
Reader reader = this.openStream(publicID, systemIDasURL.toString());
this.currentReader.lineReader = new LineNumberReader(reader);
this.currentReader.pbReader
= new PushbackReader(this.currentReader.lineReader, 2);
}
/**
* Initializes the XML reader.
*
* @param reader the input for the XML data.
*/
public StdXMLReader(Reader reader)
{
this.currentReader = new StackedReader();
this.readers = new Stack<StackedReader>();
this.currentReader.lineReader = new LineNumberReader(reader);
this.currentReader.pbReader
= new PushbackReader(this.currentReader.lineReader, 2);
this.currentReader.publicId = "";
try {
this.currentReader.systemId = new URL("file:.");
} catch (MalformedURLException e) {
// never happens
}
}
/**
* Cleans up the object when it's destroyed.
*/
protected void finalize()
throws Throwable
{
this.currentReader.lineReader = null;
this.currentReader.pbReader = null;
this.currentReader.systemId = null;
this.currentReader.publicId = null;
this.currentReader = null;
this.readers.clear();
super.finalize();
}
/**
* Scans the encoding from an &lt;?xml...?&gt; tag.
*
* @param str the first tag in the XML data.
*
* @return the encoding, or null if no encoding has been specified.
*/
protected String getEncoding(String str)
{
if (! str.startsWith("<?xml")) {
return null;
}
int index = 5;
while (index < str.length()) {
StringBuffer key = new StringBuffer();
while ((index < str.length()) && (str.charAt(index) <= ' ')) {
index++;
}
while ((index < str.length())
&& (str.charAt(index) >= 'a')
&& (str.charAt(index) <= 'z')) {
key.append(str.charAt(index));
index++;
}
while ((index < str.length()) && (str.charAt(index) <= ' ')) {
index++;
}
if ((index >= str.length()) || (str.charAt(index) != '=')) {
break;
}
while ((index < str.length()) && (str.charAt(index) != '\'')
&& (str.charAt(index) != '"')) {
index++;
}
if (index >= str.length()) {
break;
}
char delimiter = str.charAt(index);
index++;
int index2 = str.indexOf(delimiter, index);
if (index2 < 0) {
break;
}
if (key.toString().equals("encoding")) {
return str.substring(index, index2);
}
index = index2 + 1;
}
return null;
}
/**
* Converts a stream to a reader while detecting the encoding.
*
* @param stream the input for the XML data.
* @param charsRead buffer where to put characters that have been read
*
* @throws java.io.IOException
* if an I/O error occurred
*/
protected Reader stream2reader(InputStream stream,
StringBuffer charsRead)
throws IOException
{
PushbackInputStream pbstream = new PushbackInputStream(stream);
int b = pbstream.read();
switch (b) {
case 0x00:
case 0xFE:
case 0xFF:
pbstream.unread(b);
return new InputStreamReader(pbstream, "UTF-16");
case 0xEF:
for (int i = 0; i < 2; i++) {
pbstream.read();
}
return new InputStreamReader(pbstream, "UTF-8");
case 0x3C:
b = pbstream.read();
charsRead.append('<');
while ((b > 0) && (b != 0x3E)) {
charsRead.append((char) b);
b = pbstream.read();
}
if (b > 0) {
charsRead.append((char) b);
}
String encoding = this.getEncoding(charsRead.toString());
if (encoding == null) {
return new InputStreamReader(pbstream, "UTF-8");
}
charsRead.setLength(0);
try {
return new InputStreamReader(pbstream, encoding);
} catch (UnsupportedEncodingException e) {
return new InputStreamReader(pbstream, "UTF-8");
}
default:
charsRead.append((char) b);
return new InputStreamReader(pbstream, "UTF-8");
}
}
/**
* Initializes the XML reader.
*
* @param stream the input for the XML data.
*
* @throws java.io.IOException
* if an I/O error occurred
*/
public StdXMLReader(InputStream stream)
throws IOException
{
// unused?
//PushbackInputStream pbstream = new PushbackInputStream(stream);
StringBuffer charsRead = new StringBuffer();
Reader reader = this.stream2reader(stream, charsRead);
this.currentReader = new StackedReader();
this.readers = new Stack<StackedReader>();
this.currentReader.lineReader = new LineNumberReader(reader);
this.currentReader.pbReader
= new PushbackReader(this.currentReader.lineReader, 2);
this.currentReader.publicId = "";
try {
this.currentReader.systemId = new URL("file:.");
} catch (MalformedURLException e) {
// never happens
}
this.startNewStream(new StringReader(charsRead.toString()));
}
/**
* Reads a character.
*
* @return the character
*
* @throws java.io.IOException
* if no character could be read
*/
public char read()
throws IOException
{
int ch = this.currentReader.pbReader.read();
while (ch < 0) {
if (this.readers.empty()) {
throw new IOException("Unexpected EOF");
}
this.currentReader.pbReader.close();
this.currentReader = (StackedReader) this.readers.pop();
ch = this.currentReader.pbReader.read();
}
return (char) ch;
}
/**
* Returns true if the current stream has no more characters left to be
* read.
*
* @throws java.io.IOException
* if an I/O error occurred
*/
public boolean atEOFOfCurrentStream()
throws IOException
{
int ch = this.currentReader.pbReader.read();
if (ch < 0) {
return true;
} else {
this.currentReader.pbReader.unread(ch);
return false;
}
}
/**
* Returns true if there are no more characters left to be read.
*
* @throws java.io.IOException
* if an I/O error occurred
*/
public boolean atEOF()
throws IOException
{
int ch = this.currentReader.pbReader.read();
while (ch < 0) {
if (this.readers.empty()) {
return true;
}
this.currentReader.pbReader.close();
this.currentReader = (StackedReader) this.readers.pop();
ch = this.currentReader.pbReader.read();
}
this.currentReader.pbReader.unread(ch);
return false;
}
/**
* Pushes the last character read back to the stream.
*
* @param ch the character to push back.
*
* @throws java.io.IOException
* if an I/O error occurred
*/
public void unread(char ch)
throws IOException
{
this.currentReader.pbReader.unread(ch);
}
/**
* Opens a stream from a public and system ID.
*
* @param publicID the public ID, which may be null
* @param systemID the system ID, which is never null
*
* @throws java.net.MalformedURLException
* if the system ID does not contain a valid URL
* @throws java.io.FileNotFoundException
* if the system ID refers to a local file which does not exist
* @throws java.io.IOException
* if an error occurred opening the stream
*/
public Reader openStream(String publicID,
String systemID)
throws MalformedURLException,
FileNotFoundException,
IOException
{
URL url = new URL(this.currentReader.systemId, systemID);
if (url.getRef() != null) {
String ref = url.getRef();
if (url.getFile().length() > 0) {
url = new URL(url.getProtocol(), url.getHost(), url.getPort(),
url.getFile());
url = new URL("jar:" + url + '!' + ref);
} else {
url = StdXMLReader.class.getResource(ref);
}
}
this.currentReader.publicId = publicID;
this.currentReader.systemId = url;
StringBuffer charsRead = new StringBuffer();
Reader reader = this.stream2reader(url.openStream(), charsRead);
if (charsRead.length() == 0) {
return reader;
}
String charsReadStr = charsRead.toString();
PushbackReader pbreader = new PushbackReader(reader,
charsReadStr.length());
for (int i = charsReadStr.length() - 1; i >= 0; i--) {
pbreader.unread(charsReadStr.charAt(i));
}
return pbreader;
}
/**
* Starts a new stream from a Java reader. The new stream is used
* temporary to read data from. If that stream is exhausted, control
* returns to the parent stream.
*
* @param reader the non-null reader to read the new data from
*/
public void startNewStream(Reader reader)
{
this.startNewStream(reader, false);
}
/**
* Starts a new stream from a Java reader. The new stream is used
* temporary to read data from. If that stream is exhausted, control
* returns to the parent stream.
*
* @param reader the non-null reader to read the new data from
* @param isInternalEntity true if the reader is produced by resolving
* an internal entity
*/
public void startNewStream(Reader reader,
boolean isInternalEntity)
{
StackedReader oldReader = this.currentReader;
this.readers.push(this.currentReader);
this.currentReader = new StackedReader();
if (isInternalEntity) {
this.currentReader.lineReader = null;
this.currentReader.pbReader = new PushbackReader(reader, 2);
} else {
this.currentReader.lineReader = new LineNumberReader(reader);
this.currentReader.pbReader
= new PushbackReader(this.currentReader.lineReader, 2);
}
this.currentReader.systemId = oldReader.systemId;
this.currentReader.publicId = oldReader.publicId;
}
/**
* Returns the current "level" of the stream on the stack of streams.
*/
public int getStreamLevel()
{
return this.readers.size();
}
/**
* Returns the line number of the data in the current stream.
*/
public int getLineNr()
{
if (this.currentReader.lineReader == null) {
StackedReader sr = (StackedReader) this.readers.peek();
if (sr.lineReader == null) {
return 0;
} else {
return sr.lineReader.getLineNumber() + 1;
}
}
return this.currentReader.lineReader.getLineNumber() + 1;
}
/**
* Sets the system ID of the current stream.
*
* @param systemID the system ID
*
* @throws java.net.MalformedURLException
* if the system ID does not contain a valid URL
*/
public void setSystemID(String systemID)
throws MalformedURLException
{
this.currentReader.systemId = new URL(this.currentReader.systemId,
systemID);
}
/**
* Sets the public ID of the current stream.
*
* @param publicID the public ID
*/
public void setPublicID(String publicID)
{
this.currentReader.publicId = publicID;
}
/**
* Returns the current system ID.
*/
public String getSystemID()
{
return this.currentReader.systemId.toString();
}
/**
* Returns the current public ID.
*/
public String getPublicID()
{
return this.currentReader.publicId;
}
}

View File

@ -0,0 +1,153 @@
/* XMLAttribute.java NanoXML/Java
*
* $Revision: 1.4 $
* $Date: 2002/01/04 21:03:29 $
* $Name: RELEASE_2_2_1 $
*
* This file is part of NanoXML 2 for Java.
* Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
package processing.xml;
/**
* An attribute in an XML element. This is an internal class.
*
* @see net.n3.nanoxml.XMLElement
*
* @author Marc De Scheemaecker
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
*/
class XMLAttribute
{
/**
* The full name of the attribute.
*/
private String fullName;
/**
* The short name of the attribute.
*/
private String name;
/**
* The namespace URI of the attribute.
*/
private String namespace;
/**
* The value of the attribute.
*/
private String value;
/**
* The type of the attribute.
*/
private String type;
/**
* Creates a new attribute.
*
* @param fullName the non-null full name
* @param name the non-null short name
* @param namespace the namespace URI, which may be null
* @param value the value of the attribute
* @param type the type of the attribute
*/
XMLAttribute(String fullName,
String name,
String namespace,
String value,
String type)
{
this.fullName = fullName;
this.name = name;
this.namespace = namespace;
this.value = value;
this.type = type;
}
/**
* Returns the full name of the attribute.
*/
String getFullName()
{
return this.fullName;
}
/**
* Returns the short name of the attribute.
*/
String getName()
{
return this.name;
}
/**
* Returns the namespace of the attribute.
*/
String getNamespace()
{
return this.namespace;
}
/**
* Returns the value of the attribute.
*/
String getValue()
{
return this.value;
}
/**
* Sets the value of the attribute.
*
* @param value the new value.
*/
void setValue(String value)
{
this.value = value;
}
/**
* Returns the type of the attribute.
*
* @param type the new type.
*/
String getType()
{
return this.type;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,173 @@
/* XMLEntityResolver.java NanoXML/Java
*
* $Revision: 1.4 $
* $Date: 2002/01/04 21:03:29 $
* $Name: RELEASE_2_2_1 $
*
* This file is part of NanoXML 2 for Java.
* Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
package processing.xml;
import java.util.Hashtable;
import java.io.Reader;
import java.io.StringReader;
/**
* An XMLEntityResolver resolves entities.
*
* @author Marc De Scheemaecker
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
*/
public class XMLEntityResolver
{
/**
* The entities.
*/
private Hashtable<String, Object> entities;
/**
* Initializes the resolver.
*/
public XMLEntityResolver()
{
this.entities = new Hashtable<String, Object>();
this.entities.put("amp", "&#38;");
this.entities.put("quot", "&#34;");
this.entities.put("apos", "&#39;");
this.entities.put("lt", "&#60;");
this.entities.put("gt", "&#62;");
}
/**
* Cleans up the object when it's destroyed.
*/
protected void finalize()
throws Throwable
{
this.entities.clear();
this.entities = null;
super.finalize();
}
/**
* Adds an internal entity.
*
* @param name the name of the entity.
* @param value the value of the entity.
*/
public void addInternalEntity(String name,
String value)
{
if (! this.entities.containsKey(name)) {
this.entities.put(name, value);
}
}
/**
* Adds an external entity.
*
* @param name the name of the entity.
* @param publicID the public ID of the entity, which may be null.
* @param systemID the system ID of the entity.
*/
public void addExternalEntity(String name,
String publicID,
String systemID)
{
if (! this.entities.containsKey(name)) {
this.entities.put(name, new String[] { publicID, systemID } );
}
}
/**
* Returns a Java reader containing the value of an entity.
*
* @param xmlReader the current XML reader
* @param name the name of the entity.
*
* @return the reader, or null if the entity could not be resolved.
*/
public Reader getEntity(StdXMLReader xmlReader,
String name)
throws XMLParseException
{
Object obj = this.entities.get(name);
if (obj == null) {
return null;
} else if (obj instanceof java.lang.String) {
return new StringReader((String)obj);
} else {
String[] id = (String[]) obj;
return this.openExternalEntity(xmlReader, id[0], id[1]);
}
}
/**
* Returns true if an entity is external.
*
* @param name the name of the entity.
*/
public boolean isExternalEntity(String name)
{
Object obj = this.entities.get(name);
return ! (obj instanceof java.lang.String);
}
/**
* Opens an external entity.
*
* @param xmlReader the current XML reader
* @param publicID the public ID, which may be null
* @param systemID the system ID
*
* @return the reader, or null if the reader could not be created/opened
*/
protected Reader openExternalEntity(StdXMLReader xmlReader,
String publicID,
String systemID)
throws XMLParseException
{
String parentSystemID = xmlReader.getSystemID();
try {
return xmlReader.openStream(publicID, systemID);
} catch (Exception e) {
throw new XMLParseException(parentSystemID,
xmlReader.getLineNr(),
"Could not open external entity "
+ "at system ID: " + systemID);
}
}
}

View File

@ -0,0 +1,286 @@
/* XMLException.java NanoXML/Java
*
* $Revision: 1.4 $
* $Date: 2002/01/04 21:03:29 $
* $Name: RELEASE_2_2_1 $
*
* This file is part of NanoXML 2 for Java.
* Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
package processing.xml;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
* An XMLException is thrown when an exception occurred while processing the
* XML data.
*
* @author Marc De Scheemaecker
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
*/
public class XMLException
extends Exception
{
/**
* The message of the exception.
*/
private String msg;
/**
* The system ID of the XML data where the exception occurred.
*/
private String systemID;
/**
* The line number in the XML data where the exception occurred.
*/
private int lineNr;
/**
* Encapsulated exception.
*/
private Exception encapsulatedException;
/**
* Creates a new exception.
*
* @param msg the message of the exception.
*/
public XMLException(String msg)
{
this(null, -1, null, msg, false);
}
/**
* Creates a new exception.
*
* @param e the encapsulated exception.
*/
public XMLException(Exception e)
{
this(null, -1, e, "Nested Exception", false);
}
/**
* Creates a new exception.
*
* @param systemID the system ID of the XML data where the exception
* occurred
* @param lineNr the line number in the XML data where the exception
* occurred.
* @param e the encapsulated exception.
*/
public XMLException(String systemID,
int lineNr,
Exception e)
{
this(systemID, lineNr, e, "Nested Exception", true);
}
/**
* Creates a new exception.
*
* @param systemID the system ID of the XML data where the exception
* occurred
* @param lineNr the line number in the XML data where the exception
* occurred.
* @param msg the message of the exception.
*/
public XMLException(String systemID,
int lineNr,
String msg)
{
this(systemID, lineNr, null, msg, true);
}
/**
* Creates a new exception.
*
* @param systemID the system ID from where the data came
* @param lineNr the line number in the XML data where the exception
* occurred.
* @param e the encapsulated exception.
* @param msg the message of the exception.
* @param reportParams true if the systemID, lineNr and e params need to be
* appended to the message
*/
public XMLException(String systemID,
int lineNr,
Exception e,
String msg,
boolean reportParams)
{
super(XMLException.buildMessage(systemID, lineNr, e, msg,
reportParams));
this.systemID = systemID;
this.lineNr = lineNr;
this.encapsulatedException = e;
this.msg = XMLException.buildMessage(systemID, lineNr, e, msg,
reportParams);
}
/**
* Builds the exception message
*
* @param systemID the system ID from where the data came
* @param lineNr the line number in the XML data where the exception
* occurred.
* @param e the encapsulated exception.
* @param msg the message of the exception.
* @param reportParams true if the systemID, lineNr and e params need to be
* appended to the message
*/
private static String buildMessage(String systemID,
int lineNr,
Exception e,
String msg,
boolean reportParams)
{
String str = msg;
if (reportParams) {
if (systemID != null) {
str += ", SystemID='" + systemID + "'";
}
if (lineNr >= 0) {
str += ", Line=" + lineNr;
}
if (e != null) {
str += ", Exception: " + e;
}
}
return str;
}
/**
* Cleans up the object when it's destroyed.
*/
protected void finalize()
throws Throwable
{
this.systemID = null;
this.encapsulatedException = null;
super.finalize();
}
/**
* Returns the system ID of the XML data where the exception occurred.
* If there is no system ID known, null is returned.
*/
public String getSystemID()
{
return this.systemID;
}
/**
* Returns the line number in the XML data where the exception occurred.
* If there is no line number known, -1 is returned.
*/
public int getLineNr()
{
return this.lineNr;
}
/**
* Returns the encapsulated exception, or null if no exception is
* encapsulated.
*/
public Exception getException()
{
return this.encapsulatedException;
}
/**
* Dumps the exception stack to a print writer.
*
* @param writer the print writer
*/
public void printStackTrace(PrintWriter writer)
{
super.printStackTrace(writer);
if (this.encapsulatedException != null) {
writer.println("*** Nested Exception:");
this.encapsulatedException.printStackTrace(writer);
}
}
/**
* Dumps the exception stack to an output stream.
*
* @param stream the output stream
*/
public void printStackTrace(PrintStream stream)
{
super.printStackTrace(stream);
if (this.encapsulatedException != null) {
stream.println("*** Nested Exception:");
this.encapsulatedException.printStackTrace(stream);
}
}
/**
* Dumps the exception stack to System.err.
*/
public void printStackTrace()
{
super.printStackTrace();
if (this.encapsulatedException != null) {
System.err.println("*** Nested Exception:");
this.encapsulatedException.printStackTrace();
}
}
/**
* Returns a string representation of the exception.
*/
public String toString()
{
return this.msg;
}
}

View File

@ -0,0 +1,69 @@
/* XMLParseException.java NanoXML/Java
*
* $Revision: 1.3 $
* $Date: 2002/01/04 21:03:29 $
* $Name: RELEASE_2_2_1 $
*
* This file is part of NanoXML 2 for Java.
* Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
package processing.xml;
/**
* An XMLParseException is thrown when the XML passed to the XML parser is not
* well-formed.
*
* @author Marc De Scheemaecker
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.3 $
*/
public class XMLParseException
extends XMLException
{
/**
* Creates a new exception.
*
* @param msg the message of the exception.
*/
public XMLParseException(String msg)
{
super(msg);
}
/**
* Creates a new exception.
*
* @param systemID the system ID from where the data came
* @param lineNr the line number in the XML data where the exception
* occurred.
* @param msg the message of the exception.
*/
public XMLParseException(String systemID,
int lineNr,
String msg)
{
super(systemID, lineNr, null, msg, true);
}
}

View File

@ -0,0 +1,758 @@
/* XMLUtil.java NanoXML/Java
*
* $Revision: 1.5 $
* $Date: 2002/02/03 21:19:38 $
* $Name: RELEASE_2_2_1 $
*
* This file is part of NanoXML 2 for Java.
* Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
package processing.xml;
import java.io.IOException;
import java.io.Reader;
/**
* Utility methods for NanoXML.
*
* @author Marc De Scheemaecker
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.5 $
*/
class XMLUtil
{
/**
* Skips the remainder of a comment.
* It is assumed that &lt;!- is already read.
*
* @param reader the reader
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
static void skipComment(StdXMLReader reader)
throws IOException,
XMLParseException
{
if (reader.read() != '-') {
XMLUtil.errorExpectedInput(reader.getSystemID(),
reader.getLineNr(),
"<!--");
}
int dashesRead = 0;
for (;;) {
char ch = reader.read();
switch (ch) {
case '-':
dashesRead++;
break;
case '>':
if (dashesRead == 2) {
return;
}
dashesRead = 0;
break;
default:
dashesRead = 0;
}
}
}
/**
* Skips the remainder of the current XML tag.
*
* @param reader the reader
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
static void skipTag(StdXMLReader reader)
throws IOException,
XMLParseException
{
int level = 1;
while (level > 0) {
char ch = reader.read();
switch (ch) {
case '<':
++level;
break;
case '>':
--level;
break;
}
}
}
/**
* Scans a public ID.
*
* @param publicID will contain the public ID
* @param reader the reader
*
* @return the system ID
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
static String scanPublicID(StringBuffer publicID,
StdXMLReader reader)
throws IOException,
XMLParseException
{
if (! XMLUtil.checkLiteral(reader, "UBLIC")) {
return null;
}
XMLUtil.skipWhitespace(reader, null);
publicID.append(XMLUtil.scanString(reader, '\0', null));
XMLUtil.skipWhitespace(reader, null);
return XMLUtil.scanString(reader, '\0', null);
}
/**
* Scans a system ID.
*
* @param reader the reader
*
* @return the system ID
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
static String scanSystemID(StdXMLReader reader)
throws IOException,
XMLParseException
{
if (! XMLUtil.checkLiteral(reader, "YSTEM")) {
return null;
}
XMLUtil.skipWhitespace(reader, null);
return XMLUtil.scanString(reader, '\0', null);
}
/**
* Retrieves an identifier from the data.
*
* @param reader the reader
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
static String scanIdentifier(StdXMLReader reader)
throws IOException,
XMLParseException
{
StringBuffer result = new StringBuffer();
for (;;) {
char ch = reader.read();
if ((ch == '_') || (ch == ':') || (ch == '-') || (ch == '.')
|| ((ch >= 'a') && (ch <= 'z'))
|| ((ch >= 'A') && (ch <= 'Z'))
|| ((ch >= '0') && (ch <= '9')) || (ch > '\u007E')) {
result.append(ch);
} else {
reader.unread(ch);
break;
}
}
return result.toString();
}
/**
* Retrieves a delimited string from the data.
*
* @param reader the reader
* @param entityChar the escape character (&amp; or %)
* @param entityResolver the entity resolver
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
static String scanString(StdXMLReader reader,
char entityChar,
XMLEntityResolver entityResolver)
throws IOException,
XMLParseException
{
StringBuffer result = new StringBuffer();
int startingLevel = reader.getStreamLevel();
char delim = reader.read();
if ((delim != '\'') && (delim != '"')) {
XMLUtil.errorExpectedInput(reader.getSystemID(),
reader.getLineNr(),
"delimited string");
}
for (;;) {
String str = XMLUtil.read(reader, entityChar);
char ch = str.charAt(0);
if (ch == entityChar) {
if (str.charAt(1) == '#') {
result.append(XMLUtil.processCharLiteral(str));
} else {
XMLUtil.processEntity(str, reader, entityResolver);
}
} else if (ch == '&') {
reader.unread(ch);
str = XMLUtil.read(reader, '&');
if (str.charAt(1) == '#') {
result.append(XMLUtil.processCharLiteral(str));
} else {
result.append(str);
}
} else if (reader.getStreamLevel() == startingLevel) {
if (ch == delim) {
break;
} else if ((ch == 9) || (ch == 10) || (ch == 13)) {
result.append(' ');
} else {
result.append(ch);
}
} else {
result.append(ch);
}
}
return result.toString();
}
/**
* Processes an entity.
*
* @param entity the entity
* @param reader the reader
* @param entityResolver the entity resolver
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
static void processEntity(String entity,
StdXMLReader reader,
XMLEntityResolver entityResolver)
throws IOException,
XMLParseException
{
entity = entity.substring(1, entity.length() - 1);
Reader entityReader = entityResolver.getEntity(reader, entity);
if (entityReader == null) {
XMLUtil.errorInvalidEntity(reader.getSystemID(),
reader.getLineNr(),
entity);
}
boolean externalEntity = entityResolver.isExternalEntity(entity);
reader.startNewStream(entityReader, !externalEntity);
}
/**
* Processes a character literal.
*
* @param entity the entity
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
static char processCharLiteral(String entity)
throws IOException,
XMLParseException
{
if (entity.charAt(2) == 'x') {
entity = entity.substring(3, entity.length() - 1);
return (char) Integer.parseInt(entity, 16);
} else {
entity = entity.substring(2, entity.length() - 1);
return (char) Integer.parseInt(entity, 10);
}
}
/**
* Skips whitespace from the reader.
*
* @param reader the reader
* @param buffer where to put the whitespace; null if the
* whitespace does not have to be stored.
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
static void skipWhitespace(StdXMLReader reader,
StringBuffer buffer)
throws IOException
{
char ch;
if (buffer == null) {
do {
ch = reader.read();
} while ((ch == ' ') || (ch == '\t') || (ch == '\n'));
} else {
for (;;) {
ch = reader.read();
if ((ch != ' ') && (ch != '\t') && (ch != '\n')) {
break;
}
if (ch == '\n') {
buffer.append('\n');
} else {
buffer.append(' ');
}
}
}
reader.unread(ch);
}
/**
* Reads a character from the reader.
*
* @param reader the reader
* @param entityChar the escape character (&amp; or %) used to indicate
* an entity
*
* @return the character, or an entity expression (like e.g. &amp;lt;)
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
static String read(StdXMLReader reader,
char entityChar) throws IOException, XMLParseException {
char ch = reader.read();
StringBuffer buf = new StringBuffer();
buf.append(ch);
if (ch == entityChar) {
while (ch != ';') {
ch = reader.read();
buf.append(ch);
}
}
return buf.toString();
}
/**
* Reads a character from the reader disallowing entities.
*
* @param reader the reader
* @param entityChar the escape character (&amp; or %) used to indicate
* an entity
*/
static char readChar(StdXMLReader reader,
char entityChar) throws IOException, XMLParseException {
String str = XMLUtil.read(reader, entityChar);
char ch = str.charAt(0);
if (ch == entityChar) {
XMLUtil.errorUnexpectedEntity(reader.getSystemID(),
reader.getLineNr(),
str);
}
return ch;
}
/**
* Returns true if the data starts with <I>literal</I>.
* Enough chars are read to determine this result.
*
* @param reader the reader
* @param literal the literal to check
*
* @throws java.io.IOException
* if an error occurred reading the data
*/
static boolean checkLiteral(StdXMLReader reader,
String literal)
throws IOException,
XMLParseException
{
for (int i = 0; i < literal.length(); i++) {
if (reader.read() != literal.charAt(i)) {
return false;
}
}
return true;
}
/**
* Throws an XMLParseException to indicate that an expected string is not
* encountered.
*
* @param systemID the system ID of the data source
* @param lineNr the line number in the data source
* @param expectedString the string that is expected
*/
static void errorExpectedInput(String systemID,
int lineNr,
String expectedString)
throws XMLParseException
{
throw new XMLParseException(systemID, lineNr,
"Expected: " + expectedString);
}
/**
* Throws an XMLParseException to indicate that an entity could not be
* resolved.
*
* @param systemID the system ID of the data source
* @param lineNr the line number in the data source
* @param entity the name of the entity
*/
static void errorInvalidEntity(String systemID,
int lineNr,
String entity)
throws XMLParseException
{
throw new XMLParseException(systemID, lineNr,
"Invalid entity: `&" + entity + ";'");
}
/**
* Throws an XMLParseException to indicate that an entity reference is
* unexpected at this point.
*
* @param systemID the system ID of the data source
* @param lineNr the line number in the data source
* @param entity the name of the entity
*/
static void errorUnexpectedEntity(String systemID,
int lineNr,
String entity)
throws XMLParseException
{
throw new XMLParseException(systemID, lineNr,
"No entity reference is expected here ("
+ entity + ")");
}
/**
* Throws an XMLParseException to indicate that a CDATA section is
* unexpected at this point.
*
* @param systemID the system ID of the data source
* @param lineNr the line number in the data source
*/
static void errorUnexpectedCDATA(String systemID,
int lineNr)
throws XMLParseException
{
throw new XMLParseException(systemID, lineNr,
"No CDATA section is expected here");
}
/**
* Throws an XMLParseException to indicate that a string is not expected
* at this point.
*
* @param systemID the system ID of the data source
* @param lineNr the line number in the data source
* @param unexpectedString the string that is unexpected
*/
static void errorInvalidInput(String systemID,
int lineNr,
String unexpectedString)
throws XMLParseException
{
throw new XMLParseException(systemID, lineNr,
"Invalid input: " + unexpectedString);
}
/**
* Throws an XMLParseException to indicate that the closing tag of an
* element does not match the opening tag.
*
* @param systemID the system ID of the data source
* @param lineNr the line number in the data source
* @param expectedName the name of the opening tag
* @param wrongName the name of the closing tag
*/
static void errorWrongClosingTag(String systemID,
int lineNr,
String expectedName,
String wrongName)
throws XMLParseException
{
throw new XMLParseException(systemID, lineNr,
"Closing tag does not match opening tag: `"
+ wrongName + "' != `" + expectedName
+ "'");
}
/**
* Throws an XMLParseException to indicate that extra data is encountered
* in a closing tag.
*
* @param systemID the system ID of the data source
* @param lineNr the line number in the data source
*/
static void errorClosingTagNotEmpty(String systemID,
int lineNr)
throws XMLParseException
{
throw new XMLParseException(systemID, lineNr,
"Closing tag must be empty");
}
/**
* Throws an XMLValidationException to indicate that an element is missing.
*
* @param systemID the system ID of the data source
* @param lineNr the line number in the data source
* @param parentElementName the name of the parent element
* @param missingElementName the name of the missing element
*/
static void errorMissingElement(String systemID,
int lineNr,
String parentElementName,
String missingElementName)
throws XMLValidationException
{
throw new XMLValidationException(
XMLValidationException.MISSING_ELEMENT,
systemID, lineNr,
missingElementName,
/*attributeName*/ null,
/*attributeValue*/ null,
"Element " + parentElementName
+ " expects to have a " + missingElementName);
}
/**
* Throws an XMLValidationException to indicate that an element is
* unexpected.
*
* @param systemID the system ID of the data source
* @param lineNr the line number in the data source
* @param parentElementName the name of the parent element
* @param unexpectedElementName the name of the unexpected element
*/
static void errorUnexpectedElement(String systemID,
int lineNr,
String parentElementName,
String unexpectedElementName)
throws XMLValidationException
{
throw new XMLValidationException(
XMLValidationException.UNEXPECTED_ELEMENT,
systemID, lineNr,
unexpectedElementName,
/*attributeName*/ null,
/*attributeValue*/ null,
"Unexpected " + unexpectedElementName + " in a "
+ parentElementName);
}
/**
* Throws an XMLValidationException to indicate that an attribute is
* missing.
*
* @param systemID the system ID of the data source
* @param lineNr the line number in the data source
* @param elementName the name of the element
* @param attributeName the name of the missing attribute
*/
static void errorMissingAttribute(String systemID,
int lineNr,
String elementName,
String attributeName)
throws XMLValidationException
{
throw new XMLValidationException(
XMLValidationException.MISSING_ATTRIBUTE,
systemID, lineNr,
elementName,
attributeName,
/*attributeValue*/ null,
"Element " + elementName + " expects an attribute named "
+ attributeName);
}
/**
* Throws an XMLValidationException to indicate that an attribute is
* unexpected.
*
* @param systemID the system ID of the data source
* @param lineNr the line number in the data source
* @param elementName the name of the element
* @param attributeName the name of the unexpected attribute
*/
static void errorUnexpectedAttribute(String systemID,
int lineNr,
String elementName,
String attributeName)
throws XMLValidationException
{
throw new XMLValidationException(
XMLValidationException.UNEXPECTED_ATTRIBUTE,
systemID, lineNr,
elementName,
attributeName,
/*attributeValue*/ null,
"Element " + elementName + " did not expect an attribute "
+ "named " + attributeName);
}
/**
* Throws an XMLValidationException to indicate that an attribute has an
* invalid value.
*
* @param systemID the system ID of the data source
* @param lineNr the line number in the data source
* @param elementName the name of the element
* @param attributeName the name of the attribute
* @param attributeValue the value of that attribute
*/
static void errorInvalidAttributeValue(String systemID,
int lineNr,
String elementName,
String attributeName,
String attributeValue)
throws XMLValidationException
{
throw new XMLValidationException(
XMLValidationException.ATTRIBUTE_WITH_INVALID_VALUE,
systemID, lineNr,
elementName,
attributeName,
attributeValue,
"Invalid value for attribute " + attributeName);
}
/**
* Throws an XMLValidationException to indicate that a #PCDATA element was
* missing.
*
* @param systemID the system ID of the data source
* @param lineNr the line number in the data source
* @param parentElementName the name of the parent element
*/
static void errorMissingPCData(String systemID,
int lineNr,
String parentElementName)
throws XMLValidationException
{
throw new XMLValidationException(
XMLValidationException.MISSING_PCDATA,
systemID, lineNr,
/*elementName*/ null,
/*attributeName*/ null,
/*attributeValue*/ null,
"Missing #PCDATA in element " + parentElementName);
}
/**
* Throws an XMLValidationException to indicate that a #PCDATA element was
* unexpected.
*
* @param systemID the system ID of the data source
* @param lineNr the line number in the data source
* @param parentElementName the name of the parent element
*/
static void errorUnexpectedPCData(String systemID,
int lineNr,
String parentElementName)
throws XMLValidationException
{
throw new XMLValidationException(
XMLValidationException.UNEXPECTED_PCDATA,
systemID, lineNr,
/*elementName*/ null,
/*attributeName*/ null,
/*attributeValue*/ null,
"Unexpected #PCDATA in element " + parentElementName);
}
/**
* Throws an XMLValidationException.
*
* @param systemID the system ID of the data source
* @param lineNr the line number in the data source
* @param message the error message
* @param elementName the name of the element
* @param attributeName the name of the attribute
* @param attributeValue the value of that attribute
*/
static void validationError(String systemID,
int lineNr,
String message,
String elementName,
String attributeName,
String attributeValue)
throws XMLValidationException
{
throw new XMLValidationException(XMLValidationException.MISC_ERROR,
systemID, lineNr,
elementName,
attributeName,
attributeValue,
message);
}
}

View File

@ -0,0 +1,190 @@
/* XMLValidationException.java NanoXML/Java
*
* $Revision: 1.3 $
* $Date: 2002/01/04 21:03:29 $
* $Name: RELEASE_2_2_1 $
*
* This file is part of NanoXML 2 for Java.
* Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
package processing.xml;
/**
* An XMLValidationException is thrown when the XML passed to the XML parser is
* well-formed but not valid.
*
* @author Marc De Scheemaecker
* @version $Name: RELEASE_2_2_1 $, $Revision: 1.3 $
*/
public class XMLValidationException
extends XMLException
{
/**
* An element was missing.
*/
public static final int MISSING_ELEMENT = 1;
/**
* An unexpected element was encountered.
*/
public static final int UNEXPECTED_ELEMENT = 2;
/**
* An attribute was missing.
*/
public static final int MISSING_ATTRIBUTE = 3;
/**
* An unexpected attribute was encountered.
*/
public static final int UNEXPECTED_ATTRIBUTE = 4;
/**
* An attribute has an invalid value.
*/
public static final int ATTRIBUTE_WITH_INVALID_VALUE = 5;
/**
* A PCDATA element was missing.
*/
public static final int MISSING_PCDATA = 6;
/**
* An unexpected PCDATA element was encountered.
*/
public static final int UNEXPECTED_PCDATA = 7;
/**
* Another error than those specified in this class was encountered.
*/
public static final int MISC_ERROR = 0;
/**
* Which error occurred.
*/
//private int errorType;
/**
* The name of the element where the exception occurred.
*/
private String elementName;
/**
* The name of the attribute where the exception occurred.
*/
private String attributeName;
/**
* The value of the attribute where the exception occurred.
*/
private String attributeValue;
/**
* Creates a new exception.
*
* @param errorType the type of validity error
* @param systemID the system ID from where the data came
* @param lineNr the line number in the XML data where the
* exception occurred.
* @param elementName the name of the offending element
* @param attributeName the name of the offending attribute
* @param attributeValue the value of the offending attribute
* @param msg the message of the exception.
*/
public XMLValidationException(int errorType,
String systemID,
int lineNr,
String elementName,
String attributeName,
String attributeValue,
String msg)
{
super(systemID, lineNr, null,
msg + ((elementName == null) ? "" : (", element=" + elementName))
+ ((attributeName == null) ? ""
: (", attribute=" + attributeName))
+ ((attributeValue == null) ? ""
: (", value='" + attributeValue + "'")),
false);
this.elementName = elementName;
this.attributeName = attributeName;
this.attributeValue = attributeValue;
}
/**
* Cleans up the object when it's destroyed.
*/
protected void finalize()
throws Throwable
{
this.elementName = null;
this.attributeName = null;
this.attributeValue = null;
super.finalize();
}
/**
* Returns the name of the element in which the validation is violated.
* If there is no current element, null is returned.
*/
public String getElementName()
{
return this.elementName;
}
/**
* Returns the name of the attribute in which the validation is violated.
* If there is no current attribute, null is returned.
*/
public String getAttributeName()
{
return this.attributeName;
}
/**
* Returns the value of the attribute in which the validation is violated.
* If there is no current attribute, null is returned.
*/
public String getAttributeValue()
{
return this.attributeValue;
}
}

View File

@ -0,0 +1,631 @@
/* NonValidator.java NanoXML/Java
*
* $Revision: 1.4 $
* $Date: 2002/02/03 21:19:38 $
* $Name: RELEASE_2_2_1 $
*
* This file is part of NanoXML 2 for Java.
* Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
package processing.xml;
import java.io.Reader;
import java.io.StringReader;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Stack;
/**
* XMLValidator implementation based on NonValidator (which implemented
* IXMLValidator in the original NanoXML).
* This implementation processes the DTD and handles entity definitions.
* It does not do any validation itself.
*
* @author Marc De Scheemaecker
* @author processing.org
*/
public class XMLValidator
{
/**
* The parameter entity resolver.
*/
protected XMLEntityResolver parameterEntityResolver;
/**
* Contains the default values for attributes for the different element
* types.
*/
protected Hashtable<String, Properties> attributeDefaultValues;
/**
* The stack of elements to be processed.
*/
protected Stack<Properties> currentElements;
/**
* Creates the &quot;validator&quot;.
*/
public XMLValidator()
{
this.attributeDefaultValues = new Hashtable<String, Properties>();
this.currentElements = new Stack<Properties>();
this.parameterEntityResolver = new XMLEntityResolver();
}
/**
* Cleans up the object when it's destroyed.
*/
protected void finalize()
throws Throwable
{
this.parameterEntityResolver = null;
this.attributeDefaultValues.clear();
this.attributeDefaultValues = null;
this.currentElements.clear();
this.currentElements = null;
super.finalize();
}
/**
* Sets the parameter entity resolver.
*
* @param resolver the entity resolver.
*/
public void setParameterEntityResolver(XMLEntityResolver resolver)
{
this.parameterEntityResolver = resolver;
}
/**
* Returns the parameter entity resolver.
*
* @return the entity resolver.
*/
public XMLEntityResolver getParameterEntityResolver()
{
return this.parameterEntityResolver;
}
/**
* Parses the DTD. The validator object is responsible for reading the
* full DTD.
*
* @param publicID the public ID, which may be null.
* @param reader the reader to read the DTD from.
* @param entityResolver the entity resolver.
* @param external true if the DTD is external.
*
* @throws java.lang.Exception
* If something went wrong.
*/
public void parseDTD(String publicID,
StdXMLReader reader,
XMLEntityResolver entityResolver,
boolean external)
throws Exception
{
XMLUtil.skipWhitespace(reader, null);
int origLevel = reader.getStreamLevel();
for (;;) {
String str = XMLUtil.read(reader, '%');
char ch = str.charAt(0);
if (ch == '%') {
XMLUtil.processEntity(str, reader,
this.parameterEntityResolver);
continue;
} else if (ch == '<') {
this.processElement(reader, entityResolver);
} else if (ch == ']') {
return; // end internal DTD
} else {
XMLUtil.errorInvalidInput(reader.getSystemID(),
reader.getLineNr(),
str);
}
do {
ch = reader.read();
if (external && (reader.getStreamLevel() < origLevel)) {
reader.unread(ch);
return; // end external DTD
}
} while ((ch == ' ') || (ch == '\t') || (ch == '\n')
|| (ch == '\r'));
reader.unread(ch);
}
}
/**
* Processes an element in the DTD.
*
* @param reader the reader to read data from.
* @param entityResolver the entity resolver.
*
* @throws java.lang.Exception
* If something went wrong.
*/
protected void processElement(StdXMLReader reader,
XMLEntityResolver entityResolver)
throws Exception
{
String str = XMLUtil.read(reader, '%');
char ch = str.charAt(0);
if (ch != '!') {
XMLUtil.skipTag(reader);
return;
}
str = XMLUtil.read(reader, '%');
ch = str.charAt(0);
switch (ch) {
case '-':
XMLUtil.skipComment(reader);
break;
case '[':
this.processConditionalSection(reader, entityResolver);
break;
case 'E':
this.processEntity(reader, entityResolver);
break;
case 'A':
this.processAttList(reader, entityResolver);
break;
default:
XMLUtil.skipTag(reader);
}
}
/**
* Processes a conditional section.
*
* @param reader the reader to read data from.
* @param entityResolver the entity resolver.
*
* @throws java.lang.Exception
* If something went wrong.
*/
protected void processConditionalSection(StdXMLReader reader,
XMLEntityResolver entityResolver)
throws Exception
{
XMLUtil.skipWhitespace(reader, null);
String str = XMLUtil.read(reader, '%');
char ch = str.charAt(0);
if (ch != 'I') {
XMLUtil.skipTag(reader);
return;
}
str = XMLUtil.read(reader, '%');
ch = str.charAt(0);
switch (ch) {
case 'G':
this.processIgnoreSection(reader, entityResolver);
return;
case 'N':
break;
default:
XMLUtil.skipTag(reader);
return;
}
if (! XMLUtil.checkLiteral(reader, "CLUDE")) {
XMLUtil.skipTag(reader);
return;
}
XMLUtil.skipWhitespace(reader, null);
str = XMLUtil.read(reader, '%');
ch = str.charAt(0);
if (ch != '[') {
XMLUtil.skipTag(reader);
return;
}
Reader subreader = new CDATAReader(reader);
StringBuffer buf = new StringBuffer(1024);
for (;;) {
int ch2 = subreader.read();
if (ch2 < 0) {
break;
}
buf.append((char) ch2);
}
subreader.close();
reader.startNewStream(new StringReader(buf.toString()));
}
/**
* Processes an ignore section.
*
* @param reader the reader to read data from.
* @param entityResolver the entity resolver.
*
* @throws java.lang.Exception
* If something went wrong.
*/
protected void processIgnoreSection(StdXMLReader reader,
XMLEntityResolver entityResolver)
throws Exception
{
if (! XMLUtil.checkLiteral(reader, "NORE")) {
XMLUtil.skipTag(reader);
return;
}
XMLUtil.skipWhitespace(reader, null);
String str = XMLUtil.read(reader, '%');
char ch = str.charAt(0);
if (ch != '[') {
XMLUtil.skipTag(reader);
return;
}
Reader subreader = new CDATAReader(reader);
subreader.close();
}
/**
* Processes an ATTLIST element.
*
* @param reader the reader to read data from.
* @param entityResolver the entity resolver.
*
* @throws java.lang.Exception
* If something went wrong.
*/
protected void processAttList(StdXMLReader reader,
XMLEntityResolver entityResolver)
throws Exception
{
if (! XMLUtil.checkLiteral(reader, "TTLIST")) {
XMLUtil.skipTag(reader);
return;
}
XMLUtil.skipWhitespace(reader, null);
String str = XMLUtil.read(reader, '%');
char ch = str.charAt(0);
while (ch == '%') {
XMLUtil.processEntity(str, reader,
this.parameterEntityResolver);
str = XMLUtil.read(reader, '%');
ch = str.charAt(0);
}
reader.unread(ch);
String elementName = XMLUtil.scanIdentifier(reader);
XMLUtil.skipWhitespace(reader, null);
str = XMLUtil.read(reader, '%');
ch = str.charAt(0);
while (ch == '%') {
XMLUtil.processEntity(str, reader,
this.parameterEntityResolver);
str = XMLUtil.read(reader, '%');
ch = str.charAt(0);
}
Properties props = new Properties();
while (ch != '>') {
reader.unread(ch);
String attName = XMLUtil.scanIdentifier(reader);
XMLUtil.skipWhitespace(reader, null);
str = XMLUtil.read(reader, '%');
ch = str.charAt(0);
while (ch == '%') {
XMLUtil.processEntity(str, reader,
this.parameterEntityResolver);
str = XMLUtil.read(reader, '%');
ch = str.charAt(0);
}
if (ch == '(') {
while (ch != ')') {
str = XMLUtil.read(reader, '%');
ch = str.charAt(0);
while (ch == '%') {
XMLUtil.processEntity(str, reader,
this.parameterEntityResolver);
str = XMLUtil.read(reader, '%');
ch = str.charAt(0);
}
}
} else {
reader.unread(ch);
XMLUtil.scanIdentifier(reader);
}
XMLUtil.skipWhitespace(reader, null);
str = XMLUtil.read(reader, '%');
ch = str.charAt(0);
while (ch == '%') {
XMLUtil.processEntity(str, reader,
this.parameterEntityResolver);
str = XMLUtil.read(reader, '%');
ch = str.charAt(0);
}
if (ch == '#') {
str = XMLUtil.scanIdentifier(reader);
XMLUtil.skipWhitespace(reader, null);
if (! str.equals("FIXED")) {
XMLUtil.skipWhitespace(reader, null);
str = XMLUtil.read(reader, '%');
ch = str.charAt(0);
while (ch == '%') {
XMLUtil.processEntity(str, reader,
this.parameterEntityResolver);
str = XMLUtil.read(reader, '%');
ch = str.charAt(0);
}
continue;
}
} else {
reader.unread(ch);
}
String value = XMLUtil.scanString(reader, '%',
this.parameterEntityResolver);
props.put(attName, value);
XMLUtil.skipWhitespace(reader, null);
str = XMLUtil.read(reader, '%');
ch = str.charAt(0);
while (ch == '%') {
XMLUtil.processEntity(str, reader,
this.parameterEntityResolver);
str = XMLUtil.read(reader, '%');
ch = str.charAt(0);
}
}
if (! props.isEmpty()) {
this.attributeDefaultValues.put(elementName, props);
}
}
/**
* Processes an ENTITY element.
*
* @param reader the reader to read data from.
* @param entityResolver the entity resolver.
*
* @throws java.lang.Exception
* If something went wrong.
*/
protected void processEntity(StdXMLReader reader,
XMLEntityResolver entityResolver)
throws Exception
{
if (! XMLUtil.checkLiteral(reader, "NTITY")) {
XMLUtil.skipTag(reader);
return;
}
XMLUtil.skipWhitespace(reader, null);
char ch = XMLUtil.readChar(reader, '\0');
if (ch == '%') {
XMLUtil.skipWhitespace(reader, null);
entityResolver = this.parameterEntityResolver;
} else {
reader.unread(ch);
}
String key = XMLUtil.scanIdentifier(reader);
XMLUtil.skipWhitespace(reader, null);
ch = XMLUtil.readChar(reader, '%');
String systemID = null;
String publicID = null;
switch (ch) {
case 'P':
if (! XMLUtil.checkLiteral(reader, "UBLIC")) {
XMLUtil.skipTag(reader);
return;
}
XMLUtil.skipWhitespace(reader, null);
publicID = XMLUtil.scanString(reader, '%',
this.parameterEntityResolver);
XMLUtil.skipWhitespace(reader, null);
systemID = XMLUtil.scanString(reader, '%',
this.parameterEntityResolver);
XMLUtil.skipWhitespace(reader, null);
XMLUtil.readChar(reader, '%');
break;
case 'S':
if (! XMLUtil.checkLiteral(reader, "YSTEM")) {
XMLUtil.skipTag(reader);
return;
}
XMLUtil.skipWhitespace(reader, null);
systemID = XMLUtil.scanString(reader, '%',
this.parameterEntityResolver);
XMLUtil.skipWhitespace(reader, null);
XMLUtil.readChar(reader, '%');
break;
case '"':
case '\'':
reader.unread(ch);
String value = XMLUtil.scanString(reader, '%',
this.parameterEntityResolver);
entityResolver.addInternalEntity(key, value);
XMLUtil.skipWhitespace(reader, null);
XMLUtil.readChar(reader, '%');
break;
default:
XMLUtil.skipTag(reader);
}
if (systemID != null) {
entityResolver.addExternalEntity(key, publicID, systemID);
}
}
/**
* Indicates that an element has been started.
*
* @param name the name of the element.
* @param systemId the system ID of the XML data of the element.
* @param lineNr the line number in the XML data of the element.
*/
public void elementStarted(String name,
String systemId,
int lineNr)
{
Properties attribs
= (Properties) this.attributeDefaultValues.get(name);
if (attribs == null) {
attribs = new Properties();
} else {
attribs = (Properties) attribs.clone();
}
this.currentElements.push(attribs);
}
/**
* Indicates that the current element has ended.
*
* @param name the name of the element.
* @param systemId the system ID of the XML data of the element.
* @param lineNr the line number in the XML data of the element.
*/
public void elementEnded(String name,
String systemId,
int lineNr)
{
// nothing to do
}
/**
* This method is called when the attributes of an XML element have been
* processed.
* If there are attributes with a default value which have not been
* specified yet, they have to be put into <I>extraAttributes</I>.
*
* @param name the name of the element.
* @param extraAttributes where to put extra attributes.
* @param systemId the system ID of the XML data of the element.
* @param lineNr the line number in the XML data of the element.
*/
public void elementAttributesProcessed(String name,
Properties extraAttributes,
String systemId,
int lineNr)
{
Properties props = (Properties) this.currentElements.pop();
Enumeration<?> en = props.keys();
while (en.hasMoreElements()) {
String key = (String) en.nextElement();
extraAttributes.put(key, props.get(key));
}
}
/**
* Indicates that an attribute has been added to the current element.
*
* @param key the name of the attribute.
* @param value the value of the attribute.
* @param systemId the system ID of the XML data of the element.
* @param lineNr the line number in the XML data of the element.
*/
public void attributeAdded(String key,
String value,
String systemId,
int lineNr)
{
Properties props = (Properties) this.currentElements.peek();
if (props.containsKey(key)) {
props.remove(key);
}
}
/**
* Indicates that a new #PCDATA element has been encountered.
*
* @param systemId the system ID of the XML data of the element.
* @param lineNr the line number in the XML data of the element.
*/
public void PCDataAdded(String systemId,
int lineNr)
{
// nothing to do
}
}

View File

@ -0,0 +1,307 @@
/* XMLWriter.java NanoXML/Java
*
* $Revision: 1.4 $
* $Date: 2002/03/24 11:37:51 $
* $Name: RELEASE_2_2_1 $
*
* This file is part of NanoXML 2 for Java.
* Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from the
* use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
package processing.xml;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Enumeration;
import java.util.Vector;
/**
* An XMLWriter writes XML data to a stream.
*
* @author Marc De Scheemaecker
*/
public class XMLWriter
{
/**
* Where to write the output to.
*/
private PrintWriter writer;
/**
* Creates a new XML writer.
*
* @param writer where to write the output to.
*/
public XMLWriter(Writer writer)
{
if (writer instanceof PrintWriter) {
this.writer = (PrintWriter) writer;
} else {
this.writer = new PrintWriter(writer);
}
}
/**
* Creates a new XML writer.
*
* @param stream where to write the output to.
*/
public XMLWriter(OutputStream stream)
{
this.writer = new PrintWriter(stream);
}
/**
* Cleans up the object when it's destroyed.
*/
protected void finalize()
throws Throwable
{
this.writer = null;
super.finalize();
}
/**
* Writes an XML element.
*
* @param xml the non-null XML element to write.
*/
public void write(XMLElement xml)
throws IOException
{
this.write(xml, false, 0, true);
}
/**
* Writes an XML element.
*
* @param xml the non-null XML element to write.
* @param prettyPrint if spaces need to be inserted to make the output more
* readable
*/
public void write(XMLElement xml,
boolean prettyPrint)
throws IOException
{
this.write(xml, prettyPrint, 0, true);
}
/**
* Writes an XML element.
*
* @param xml the non-null XML element to write.
* @param prettyPrint if spaces need to be inserted to make the output more
* readable
* @param indent how many spaces to indent the element.
*/
public void write(XMLElement xml,
boolean prettyPrint,
int indent)
throws IOException
{
this.write(xml, prettyPrint, indent, true);
}
/**
* Writes an XML element.
*
* @param xml the non-null XML element to write.
* @param prettyPrint if spaces need to be inserted to make the output more
* readable
* @param indent how many spaces to indent the element.
*/
public void write(XMLElement xml,
boolean prettyPrint,
int indent,
boolean collapseEmptyElements)
throws IOException
{
if (prettyPrint) {
for (int i = 0; i < indent; i++) {
this.writer.print(' ');
}
}
if (xml.getLocalName() == null) {
if (xml.getContent() != null) {
if (prettyPrint) {
this.writeEncoded(xml.getContent().trim());
writer.println();
} else {
this.writeEncoded(xml.getContent());
}
}
} else {
this.writer.print('<');
this.writer.print(xml.getName());
Vector<String> nsprefixes = new Vector<String>();
if (xml.getNamespace() != null) {
if (xml.getLocalName().equals(xml.getName())) {
this.writer.print(" xmlns=\"" + xml.getNamespace() + '"');
} else {
String prefix = xml.getName();
prefix = prefix.substring(0, prefix.indexOf(':'));
nsprefixes.addElement(prefix);
this.writer.print(" xmlns:" + prefix);
this.writer.print("=\"" + xml.getNamespace() + "\"");
}
}
Enumeration<?> en = xml.enumerateAttributeNames();
while (en.hasMoreElements()) {
String key = (String) en.nextElement();
int index = key.indexOf(':');
if (index >= 0) {
String namespace = xml.getAttributeNamespace(key);
if (namespace != null) {
String prefix = key.substring(0, index);
if (! nsprefixes.contains(prefix)) {
this.writer.print(" xmlns:" + prefix);
this.writer.print("=\"" + namespace + '"');
nsprefixes.addElement(prefix);
}
}
}
}
en = xml.enumerateAttributeNames();
while (en.hasMoreElements()) {
String key = (String) en.nextElement();
String value = xml.getAttribute(key, null);
this.writer.print(" " + key + "=\"");
this.writeEncoded(value);
this.writer.print('"');
}
if ((xml.getContent() != null)
&& (xml.getContent().length() > 0)) {
writer.print('>');
this.writeEncoded(xml.getContent());
writer.print("</" + xml.getName() + '>');
if (prettyPrint) {
writer.println();
}
} else if (xml.hasChildren() || (! collapseEmptyElements)) {
writer.print('>');
if (prettyPrint) {
writer.println();
}
en = xml.enumerateChildren();
while (en.hasMoreElements()) {
XMLElement child = (XMLElement) en.nextElement();
this.write(child, prettyPrint, indent + 4,
collapseEmptyElements);
}
if (prettyPrint) {
for (int i = 0; i < indent; i++) {
this.writer.print(' ');
}
}
this.writer.print("</" + xml.getName() + ">");
if (prettyPrint) {
writer.println();
}
} else {
this.writer.print("/>");
if (prettyPrint) {
writer.println();
}
}
}
this.writer.flush();
}
/**
* Writes a string encoding reserved characters.
*
* @param str the string to write.
*/
private void writeEncoded(String str)
{
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
switch (c) {
case 0x0A:
this.writer.print(c);
break;
case '<':
this.writer.print("&lt;");
break;
case '>':
this.writer.print("&gt;");
break;
case '&':
this.writer.print("&amp;");
break;
case '\'':
this.writer.print("&apos;");
break;
case '"':
this.writer.print("&quot;");
break;
default:
if ((c < ' ') || (c > 0x7E)) {
this.writer.print("&#x");
this.writer.print(Integer.toString(c, 16));
this.writer.print(';');
} else {
this.writer.print(c);
}
}
}
}
}