/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2006-10 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 version 2.1 as published by the Free Software Foundation.
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;
import java.util.Map;
import processing.core.PApplet;
/**
* ( begin auto-generated from PShape.xml )
*
* Datatype for storing shapes. Processing can currently load and display
* SVG (Scalable Vector Graphics) shapes. Before a shape is used, it must
* be loaded with the loadShape() function. The shape()
* function is used to draw the shape to the display window. The
* PShape object contain a group of methods, linked below, that can
* operate on the shape data.
*
* The loadShape() function supports SVG files created with Inkscape
* and Adobe Illustrator. It is not a full SVG implementation, but offers
* some straightforward support for handling vector data.
*
* ( end auto-generated )
*
Advanced
*
* 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:
*
*
* - 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).
*
- a means of creating PShape objects ala beginShape() and endShape().
*
- load(), update(), and cache methods ala PImage, so that shapes can
* have renderer-specific optimizations, such as vertex arrays in OpenGL.
*
- splitting this class into multiple classes to handle different
* varieties of shape data (primitives vs collections of vertices vs paths)
*
- change of package declaration, for instance moving the code into
* package processing.shape (if the code grows too much).
*
*
* 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.
*
* 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().
*
* @webref shape
* @usage Web & Application
* @see PApplet#loadShape(String)
* @see PApplet#createShape()
* @see PApplet#shapeMode(int)
* @instanceName sh any variable of type PShape
*/
public class PShape implements PConstants {
/**
*
*/
protected String name;
/**
*
*/
protected Map nameTable;
// /** Generic, only draws its child objects. */
// static public final int GROUP = 0;
// GROUP now inherited from PConstants, and is still zero
// These constants were updated in 3.0b6 so that they could be distinguished
// from others in PConstants and improve how some typos were handled.
// https://github.com/processing/processing/issues/3776
/** A line, ellipse, arc, image, etc. */
static public final int PRIMITIVE = 101;
/** A series of vertex, curveVertex, and bezierVertex calls. */
static public final int PATH = 102;
/** Collections of vertices created with beginShape(). */
static public final int GEOMETRY = 103;
/** 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;
/**
*
*/
protected int textureMode;
/** Texture or image data associated with this shape. */
protected PImage image;
/**
*
*/
public static final String OUTSIDE_BEGIN_END_ERROR =
"%1$s can only be called between beginShape() and endShape()";
/**
*
*/
public static final String INSIDE_BEGIN_END_ERROR =
"%1$s can only be called outside beginShape() and endShape()";
/**
*
*/
public static final String NO_SUCH_VERTEX_ERROR =
"%1$s vertex index does not exist";
/**
*
*/
static public final String NO_VERTICES_ERROR =
"getVertexCount() only works with PATH or GEOMETRY shapes";
/**
*
*/
public static final String NOT_A_SIMPLE_VERTEX =
"%1$s can not be called on quadratic or bezier vertices";
/**
*
*/
static public final String PER_VERTEX_UNSUPPORTED =
"This renderer does not support %1$s for individual vertices";
/**
* ( begin auto-generated from PShape_width.xml )
*
* The width of the PShape document.
*
* ( end auto-generated )
* @webref pshape:field
* @usage web_application
* @brief Shape document width
* @see PShape#height
*/
public float width;
/**
* ( begin auto-generated from PShape_height.xml )
*
* The height of the PShape document.
*
* ( end auto-generated )
* @webref pshape:field
* @usage web_application
* @brief Shape document height
* @see PShape#width
*/
public float height;
/**
*
*/
public float depth;
PGraphics g;
// set to false if the object is hidden in the layers palette
/**
*
*/
protected boolean visible = true;
/** Retained shape being created with beginShape/endShape */
protected boolean openShape = false;
/**
*
*/
protected boolean openContour = false;
/**
*
*/
protected boolean stroke;
/**
*
*/
protected int strokeColor;
/**
*
*/
protected float strokeWeight; // default is 1
/**
*
*/
protected int strokeCap;
/**
*
*/
protected int strokeJoin;
/**
*
*/
protected boolean fill;
/**
*
*/
protected int fillColor;
/**
*
*/
protected boolean tint;
/**
*
*/
protected int tintColor;
/**
*
*/
protected int ambientColor;
/**
*
*/
protected boolean setAmbient;
/**
*
*/
protected int specularColor;
/**
*
*/
protected int emissiveColor;
/**
*
*/
protected float shininess;
/**
*
*/
protected int sphereDetailU,
/**
*
*/
sphereDetailV;
/**
*
*/
protected int rectMode;
/**
*
*/
protected int ellipseMode;
/** Temporary toggle for whether styles should be honored. */
protected boolean style = true;
/** For primitive shapes in particular, params 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;
/**
*
*/
protected PShape parent;
/**
*
*/
protected int childCount;
/**
*
*/
protected PShape[] children;
/** Array of VERTEX, BEZIER_VERTEX, and CURVE_VERTEX calls. */
protected int vertexCodeCount;
/**
*
*/
protected int[] vertexCodes;
/** True if this is a closed path. */
protected boolean close;
// ........................................................
// internal color for setting/calculating
/**
*
*/
protected float calcR,
/**
*
*/
calcG,
/**
*
*/
calcB,
/**
*
*/
calcA;
/**
*
*/
protected int calcRi,
/**
*
*/
calcGi,
/**
*
*/
calcBi,
/**
*
*/
calcAi;
/**
*
*/
protected int calcColor;
/**
*
*/
protected boolean calcAlpha;
/** The current colorMode */
public int colorMode; // = RGB;
/** Max value for red (or hue) set by colorMode */
public float colorModeX; // = 255;
/** Max value for green (or saturation) set by colorMode */
public float colorModeY; // = 255;
/** Max value for blue (or value) set by colorMode */
public float colorModeZ; // = 255;
/** Max value for alpha set by colorMode */
public float colorModeA; // = 255;
/** True if colors are not in the range 0..1 */
boolean colorModeScale; // = true;
/** True if colorMode(RGB, 255) */
boolean colorModeDefault; // = true;
/** True if contains 3D data */
protected boolean is3D = false;
/**
*
*/
protected boolean perVertexStyles = false;
// should this be called vertices (consistent with PGraphics internals)
// or does that hurt flexibility?
// 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;
/**
* @nowebref
*/
public PShape() {
this.family = GROUP;
}
/**
* @param family
* @nowebref
*/
public PShape(int family) {
this.family = family;
}
/**
* @param g
* @param family
* @nowebref
*/
public PShape(PGraphics g, int family) {
this.g = g;
this.family = family;
// Style parameters are retrieved from the current values in the renderer.
textureMode = g.textureMode;
colorMode(g.colorMode,
g.colorModeX, g.colorModeY, g.colorModeZ, g.colorModeA);
// Initial values for fill, stroke and tint colors are also imported from
// the renderer. This is particular relevant for primitive shapes, since is
// not possible to set their color separately when creating them, and their
// input vertices are actually generated at rendering time, by which the
// color configuration of the renderer might have changed.
fill = g.fill;
fillColor = g.fillColor;
stroke = g.stroke;
strokeColor = g.strokeColor;
strokeWeight = g.strokeWeight;
strokeCap = g.strokeCap;
strokeJoin = g.strokeJoin;
tint = g.tint;
tintColor = g.tintColor;
setAmbient = g.setAmbient;
ambientColor = g.ambientColor;
specularColor = g.specularColor;
emissiveColor = g.emissiveColor;
shininess = g.shininess;
sphereDetailU = g.sphereDetailU;
sphereDetailV = g.sphereDetailV;
// bezierDetail = pg.bezierDetail;
// curveDetail = pg.curveDetail;
// curveTightness = pg.curveTightness;
rectMode = g.rectMode;
ellipseMode = g.ellipseMode;
// normalX = normalY = 0;
// normalZ = 1;
//
// normalMode = NORMAL_MODE_AUTO;
// To make sure that the first vertex is marked as a break.
// Same behavior as in the immediate mode.
// breakShape = false;
if (family == GROUP) {
// GROUP shapes are always marked as ended.
// shapeCreated = true;
// TODO why was this commented out?
}
}
/**
*
* @param g
* @param kind
* @param params
*/
public PShape(PGraphics g, int kind, float... params) {
this(g, PRIMITIVE);
setKind(kind);
setParams(params);
}
/**
*
* @param family
*/
public void setFamily(int family) {
this.family = family;
}
/**
*
* @param kind
*/
public void setKind(int kind) {
this.kind = kind;
}
/**
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
*
* @return
*/
public String getName() {
return name;
}
/**
* ( begin auto-generated from PShape_isVisible.xml )
*
* Returns a boolean value "true" if the image is set to be visible,
* "false" if not. This is modified with the setVisible() parameter.
*
* The visibility of a shape is usually controlled by whatever program
* created the SVG file. For instance, this parameter is controlled by
* showing or hiding the shape in the layers palette in Adobe Illustrator.
*
* ( end auto-generated )
* @return
* @webref pshape:method
* @usage web_application
* @brief Returns a boolean value "true" if the image is set to be visible, "false" if not
* @see PShape#setVisible(boolean)
*/
public boolean isVisible() {
return visible;
}
/**
* ( begin auto-generated from PShape_setVisible.xml )
*
* Sets the shape to be visible or invisible. This is determined by the
* value of the visible parameter.
*
* The visibility of a shape is usually controlled by whatever program
* created the SVG file. For instance, this parameter is controlled by
* showing or hiding the shape in the layers palette in Adobe Illustrator.
*
* ( end auto-generated )
* @webref pshape:mathod
* @usage web_application
* @brief Sets the shape to be visible or invisible
* @param visible "false" makes the shape invisible and "true" makes it visible
* @see PShape#isVisible()
*/
public void setVisible(boolean visible) {
this.visible = visible;
}
/**
* ( begin auto-generated from PShape_disableStyle.xml )
*
* Disables the shape's style data and uses Processing's current styles.
* Styles include attributes such as colors, stroke weight, and stroke
* joints.
*
* ( end auto-generated )
* Advanced
* Overrides this shape's style information and uses PGraphics styles and
* colors. Identical to ignoreStyles(true). Also disables styles for all
* child shapes.
* @webref pshape:method
* @usage web_application
* @brief Disables the shape's style data and uses Processing styles
* @see PShape#enableStyle()
*/
public void disableStyle() {
style = false;
for (int i = 0; i < childCount; i++) {
children[i].disableStyle();
}
}
/**
* ( begin auto-generated from PShape_enableStyle.xml )
*
* Enables the shape's style data and ignores Processing's current styles.
* Styles include attributes such as colors, stroke weight, and stroke
* joints.
*
* ( end auto-generated )
*
* @webref pshape:method
* @usage web_application
* @brief Enables the shape's style data and ignores the Processing styles
* @see PShape#disableStyle()
*/
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).
* @return
*/
public float getWidth() {
//checkBounds();
return width;
}
/**
* Get the height of the drawing area (not necessarily the shape boundary).
* @return
*/
public float getHeight() {
//checkBounds();
return height;
}
/**
* Get the depth of the shape area (not necessarily the shape boundary). Only makes sense for 3D PShape subclasses,
* such as PShape3D.
* @return
*/
public float getDepth() {
//checkBounds();
return depth;
}
/*
// TODO unapproved
protected PVector getTop() {
return getTop(null);
}
protected PVector getTop(PVector top) {
if (top == null) {
top = new PVector();
}
return top;
}
protected PVector getBottom() {
return getBottom(null);
}
protected PVector getBottom(PVector bottom) {
if (bottom == null) {
bottom = new PVector();
}
return bottom;
}
*/
/**
* Return true if this shape is 2D. Defaults to true.
* @return
*/
public boolean is2D() {
return !is3D;
}
/**
* Return true if this shape is 3D. Defaults to false.
* @return
*/
public boolean is3D() {
return is3D;
}
/**
*
* @param val
*/
public void set3D(boolean val) {
is3D = val;
}
// /**
// * Return true if this shape requires rendering through OpenGL. Defaults to false.
// */
// // TODO unapproved
// public boolean isGL() {
// return false;
// }
///////////////////////////////////////////////////////////
//
// Drawing methods
/**
*
* @param mode
*/
public void textureMode(int mode) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "textureMode()");
return;
}
textureMode = mode;
}
/**
*
* @param tex
*/
public void texture(PImage tex) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "texture()");
return;
}
image = tex;
}
/**
*
*/
public void noTexture() {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "noTexture()");
return;
}
image = null;
}
// TODO unapproved
/**
*
* @param solid
*/
protected void solid(boolean solid) {
}
/**
* @webref shape:vertex
* @brief Starts a new contour
* @see PShape#endContour()
*/
public void beginContour() {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "beginContour()");
return;
}
if (family == GROUP) {
PGraphics.showWarning("Cannot begin contour in GROUP shapes");
return;
}
if (openContour) {
PGraphics.showWarning("Already called beginContour().");
return;
}
openContour = true;
beginContourImpl();
}
/**
*
*/
protected void beginContourImpl() {
if (vertexCodes == null) {
vertexCodes = new int[10];
} else if (vertexCodes.length == vertexCodeCount) {
vertexCodes = PApplet.expand(vertexCodes);
}
vertexCodes[vertexCodeCount++] = BREAK;
}
/**
* @webref shape:vertex
* @brief Ends a contour
* @see PShape#beginContour()
*/
public void endContour() {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "endContour()");
return;
}
if (family == GROUP) {
PGraphics.showWarning("Cannot end contour in GROUP shapes");
return;
}
if (!openContour) {
PGraphics.showWarning("Need to call beginContour() first.");
return;
}
endContourImpl();
openContour = false;
}
/**
*
*/
protected void endContourImpl() {
}
/**
*
* @param x
* @param y
*/
public void vertex(float x, float y) {
if (vertices == null) {
vertices = new float[10][2];
} else if (vertices.length == vertexCount) {
vertices = (float[][]) PApplet.expand(vertices);
}
vertices[vertexCount++] = new float[] { x, y };
if (vertexCodes == null) {
vertexCodes = new int[10];
} else if (vertexCodes.length == vertexCodeCount) {
vertexCodes = PApplet.expand(vertexCodes);
}
vertexCodes[vertexCodeCount++] = VERTEX;
if (x > width) {
width = x;
}
if (y > height) {
height = y;
}
}
/**
*
* @param x
* @param y
* @param u
* @param v
*/
public void vertex(float x, float y, float u, float v) {
}
/**
*
* @param x
* @param y
* @param z
*/
public void vertex(float x, float y, float z) {
vertex(x, y); // maybe? maybe not?
}
/**
*
* @param x
* @param y
* @param z
* @param u
* @param v
*/
public void vertex(float x, float y, float z, float u, float v) {
}
/**
*
* @param nx
* @param ny
* @param nz
*/
public void normal(float nx, float ny, float nz) {
}
/**
*
* @param name
* @param x
* @param y
* @param z
*/
public void attribPosition(String name, float x, float y, float z) {
}
/**
*
* @param name
* @param nx
* @param ny
* @param nz
*/
public void attribNormal(String name, float nx, float ny, float nz) {
}
/**
*
* @param name
* @param color
*/
public void attribColor(String name, int color) {
}
/**
*
* @param name
* @param values
*/
public void attrib(String name, float... values) {
}
/**
*
* @param name
* @param values
*/
public void attrib(String name, int... values) {
}
/**
*
* @param name
* @param values
*/
public void attrib(String name, boolean... values) {
}
/**
* @webref pshape:method
* @brief Starts the creation of a new PShape
* @see PApplet#endShape()
*/
public void beginShape() {
beginShape(POLYGON);
}
/**
*
* @param kind
*/
public void beginShape(int kind) {
this.kind = kind;
openShape = true;
}
/**
* @webref pshape:method
* @brief Finishes the creation of a new PShape
* @see PApplet#beginShape()
*/
public void endShape() {
endShape(OPEN);
}
/**
*
* @param mode
*/
public void endShape(int mode) {
if (family == GROUP) {
PGraphics.showWarning("Cannot end GROUP shape");
return;
}
if (!openShape) {
PGraphics.showWarning("Need to call beginShape() first");
return;
}
close = (mode==CLOSE);
// this is the state of the shape
openShape = false;
}
//////////////////////////////////////////////////////////////
// STROKE CAP/JOIN/WEIGHT
/**
*
* @param weight
*/
public void strokeWeight(float weight) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "strokeWeight()");
return;
}
strokeWeight = weight;
}
/**
*
* @param join
*/
public void strokeJoin(int join) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "strokeJoin()");
return;
}
strokeJoin = join;
}
/**
*
* @param cap
*/
public void strokeCap(int cap) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "strokeCap()");
return;
}
strokeCap = cap;
}
//////////////////////////////////////////////////////////////
// FILL COLOR
/**
*
*/
public void noFill() {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "noFill()");
return;
}
fill = false;
fillColor = 0x0;
if (!setAmbient) {
ambientColor = fillColor;
}
}
/**
*
* @param rgb
*/
public void fill(int rgb) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "fill()");
return;
}
fill = true;
colorCalc(rgb);
fillColor = calcColor;
if (!setAmbient) {
ambientColor = fillColor;
}
}
/**
*
* @param rgb
* @param alpha
*/
public void fill(int rgb, float alpha) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "fill()");
return;
}
fill = true;
colorCalc(rgb, alpha);
fillColor = calcColor;
if (!setAmbient) {
ambientColor = fillColor;
}
}
/**
*
* @param gray
*/
public void fill(float gray) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "fill()");
return;
}
fill = true;
colorCalc(gray);
fillColor = calcColor;
if (!setAmbient) {
ambientColor = fillColor;
}
}
/**
*
* @param gray
* @param alpha
*/
public void fill(float gray, float alpha) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "fill()");
return;
}
fill = true;
colorCalc(gray, alpha);
fillColor = calcColor;
if (!setAmbient) {
ambient(fillColor);
setAmbient = false;
}
if (!setAmbient) {
ambientColor = fillColor;
}
}
/**
*
* @param x
* @param y
* @param z
*/
public void fill(float x, float y, float z) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "fill()");
return;
}
fill = true;
colorCalc(x, y, z);
fillColor = calcColor;
if (!setAmbient) {
ambientColor = fillColor;
}
}
/**
*
* @param x
* @param y
* @param z
* @param a
*/
public void fill(float x, float y, float z, float a) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "fill()");
return;
}
fill = true;
colorCalc(x, y, z, a);
fillColor = calcColor;
if (!setAmbient) {
ambientColor = fillColor;
}
}
//////////////////////////////////////////////////////////////
// STROKE COLOR
/**
*
*/
public void noStroke() {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "noStroke()");
return;
}
stroke = false;
}
/**
*
* @param rgb
*/
public void stroke(int rgb) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "stroke()");
return;
}
stroke = true;
colorCalc(rgb);
strokeColor = calcColor;
}
/**
*
* @param rgb
* @param alpha
*/
public void stroke(int rgb, float alpha) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "stroke()");
return;
}
stroke = true;
colorCalc(rgb, alpha);
strokeColor = calcColor;
}
/**
*
* @param gray
*/
public void stroke(float gray) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "stroke()");
return;
}
stroke = true;
colorCalc(gray);
strokeColor = calcColor;
}
/**
*
* @param gray
* @param alpha
*/
public void stroke(float gray, float alpha) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "stroke()");
return;
}
stroke = true;
colorCalc(gray, alpha);
strokeColor = calcColor;
}
/**
*
* @param x
* @param y
* @param z
*/
public void stroke(float x, float y, float z) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "stroke()");
return;
}
stroke = true;
colorCalc(x, y, z);
strokeColor = calcColor;
}
/**
*
* @param x
* @param y
* @param z
* @param alpha
*/
public void stroke(float x, float y, float z, float alpha) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "stroke()");
return;
}
stroke = true;
colorCalc(x, y, z, alpha);
strokeColor = calcColor;
}
//////////////////////////////////////////////////////////////
// TINT COLOR
/**
*
*/
public void noTint() {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "noTint()");
return;
}
tint = false;
}
/**
*
* @param rgb
*/
public void tint(int rgb) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "tint()");
return;
}
tint = true;
colorCalc(rgb);
tintColor = calcColor;
}
/**
*
* @param rgb
* @param alpha
*/
public void tint(int rgb, float alpha) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "tint()");
return;
}
tint = true;
colorCalc(rgb, alpha);
tintColor = calcColor;
}
/**
*
* @param gray
*/
public void tint(float gray) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "tint()");
return;
}
tint = true;
colorCalc(gray);
tintColor = calcColor;
}
/**
*
* @param gray
* @param alpha
*/
public void tint(float gray, float alpha) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "tint()");
return;
}
tint = true;
colorCalc(gray, alpha);
tintColor = calcColor;
}
/**
*
* @param x
* @param y
* @param z
*/
public void tint(float x, float y, float z) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "tint()");
return;
}
tint = true;
colorCalc(x, y, z);
tintColor = calcColor;
}
/**
*
* @param x
* @param y
* @param z
* @param alpha
*/
public void tint(float x, float y, float z, float alpha) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "tint()");
return;
}
tint = true;
colorCalc(x, y, z, alpha);
tintColor = calcColor;
}
//////////////////////////////////////////////////////////////
// Ambient set/update
/**
*
* @param rgb
*/
public void ambient(int rgb) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "ambient()");
return;
}
setAmbient = true;
colorCalc(rgb);
ambientColor = calcColor;
}
/**
*
* @param gray
*/
public void ambient(float gray) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "ambient()");
return;
}
setAmbient = true;
colorCalc(gray);
ambientColor = calcColor;
}
/**
*
* @param x
* @param y
* @param z
*/
public void ambient(float x, float y, float z) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "ambient()");
return;
}
setAmbient = true;
colorCalc(x, y, z);
ambientColor = calcColor;
}
//////////////////////////////////////////////////////////////
// Specular set/update
/**
*
* @param rgb
*/
public void specular(int rgb) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "specular()");
return;
}
colorCalc(rgb);
specularColor = calcColor;
}
/**
*
* @param gray
*/
public void specular(float gray) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "specular()");
return;
}
colorCalc(gray);
specularColor = calcColor;
}
/**
*
* @param x
* @param y
* @param z
*/
public void specular(float x, float y, float z) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "specular()");
return;
}
colorCalc(x, y, z);
specularColor = calcColor;
}
//////////////////////////////////////////////////////////////
// Emissive set/update
/**
*
* @param rgb
*/
public void emissive(int rgb) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "emissive()");
return;
}
colorCalc(rgb);
emissiveColor = calcColor;
}
/**
*
* @param gray
*/
public void emissive(float gray) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "emissive()");
return;
}
colorCalc(gray);
emissiveColor = calcColor;
}
/**
*
* @param x
* @param y
* @param z
*/
public void emissive(float x, float y, float z) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "emissive()");
return;
}
colorCalc(x, y, z);
emissiveColor = calcColor;
}
//////////////////////////////////////////////////////////////
// Shininess set/update
/**
*
* @param shine
*/
public void shininess(float shine) {
if (!openShape) {
PGraphics.showWarning(OUTSIDE_BEGIN_END_ERROR, "shininess()");
return;
}
shininess = shine;
}
///////////////////////////////////////////////////////////
//
// Bezier curves
/**
*
* @param detail
*/
public void bezierDetail(int detail) {
}
/**
*
* @param x2
* @param y2
* @param x3
* @param y3
* @param x4
* @param y4
*/
public void bezierVertex(float x2, float y2,
float x3, float y3,
float x4, float y4) {
if (vertices == null) {
vertices = new float[10][];
} else if (vertexCount + 2 >= vertices.length) {
vertices = (float[][]) PApplet.expand(vertices);
}
vertices[vertexCount++] = new float[] { x2, y2 };
vertices[vertexCount++] = new float[] { x3, y3 };
vertices[vertexCount++] = new float[] { x4, y4 };
// vertexCodes must be allocated because a vertex() call is required
if (vertexCodes.length == vertexCodeCount) {
vertexCodes = PApplet.expand(vertexCodes);
}
vertexCodes[vertexCodeCount++] = BEZIER_VERTEX;
if (x4 > width) {
width = x4;
}
if (y4 > height) {
height = y4;
}
}
/**
*
* @param x2
* @param y2
* @param z2
* @param x3
* @param y3
* @param z3
* @param x4
* @param y4
* @param z4
*/
public void bezierVertex(float x2, float y2, float z2,
float x3, float y3, float z3,
float x4, float y4, float z4) {
}
/**
*
* @param cx
* @param cy
* @param x3
* @param y3
*/
public void quadraticVertex(float cx, float cy,
float x3, float y3) {
if (vertices == null) {
vertices = new float[10][];
} else if (vertexCount + 1 >= vertices.length) {
vertices = (float[][]) PApplet.expand(vertices);
}
vertices[vertexCount++] = new float[] { cx, cy };
vertices[vertexCount++] = new float[] { x3, y3 };
// vertexCodes must be allocated because a vertex() call is required
if (vertexCodes.length == vertexCodeCount) {
vertexCodes = PApplet.expand(vertexCodes);
}
vertexCodes[vertexCodeCount++] = QUADRATIC_VERTEX;
if (x3 > width) {
width = x3;
}
if (y3 > height) {
height = y3;
}
}
/**
*
* @param cx
* @param cy
* @param cz
* @param x3
* @param y3
* @param z3
*/
public void quadraticVertex(float cx, float cy, float cz,
float x3, float y3, float z3) {
}
///////////////////////////////////////////////////////////
//
// Catmull-Rom curves
/**
*
* @param detail
*/
public void curveDetail(int detail) {
}
/**
*
* @param tightness
*/
public void curveTightness(float tightness) {
}
/**
*
* @param x
* @param y
*/
public void curveVertex(float x, float y) {
}
/**
*
* @param x
* @param y
* @param z
*/
public void curveVertex(float x, float y, float z) {
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
/*
boolean strokeSaved;
int strokeColorSaved;
float strokeWeightSaved;
int strokeCapSaved;
int strokeJoinSaved;
boolean fillSaved;
int fillColorSaved;
int rectModeSaved;
int ellipseModeSaved;
int shapeModeSaved;
*/
/**
*
* @param g
*/
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);
}
}
/**
*
* @param 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();
}
}
/**
*
* @param g
*/
protected 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();
}
}
////////////////////////////////////////////////////////////////////////
//
// Shape copy
// TODO unapproved
/**
*
* @param parent
* @param src
* @return
*/
static protected PShape createShape(PApplet parent, PShape src) {
PShape dest = null;
if (src.family == GROUP) {
dest = parent.createShape(GROUP);
PShape.copyGroup(parent, src, dest);
} else if (src.family == PRIMITIVE) {
dest = parent.createShape(src.kind, src.params);
PShape.copyPrimitive(src, dest);
} else if (src.family == GEOMETRY) {
dest = parent.createShape(src.kind);
PShape.copyGeometry(src, dest);
} else if (src.family == PATH) {
dest = parent.createShape(PATH);
PShape.copyPath(src, dest);
}
dest.setName(src.name);
return dest;
}
// TODO unapproved
/**
*
* @param parent
* @param src
* @param dest
*/
static protected void copyGroup(PApplet parent, PShape src, PShape dest) {
copyMatrix(src, dest);
copyStyles(src, dest);
copyImage(src, dest);
for (int i = 0; i < src.childCount; i++) {
PShape c = PShape.createShape(parent, src.children[i]);
dest.addChild(c);
}
}
// TODO unapproved
/**
*
* @param src
* @param dest
*/
static protected void copyPrimitive(PShape src, PShape dest) {
copyMatrix(src, dest);
copyStyles(src, dest);
copyImage(src, dest);
}
// TODO unapproved
/**
*
* @param src
* @param dest
*/
static protected void copyGeometry(PShape src, PShape dest) {
dest.beginShape(src.getKind());
copyMatrix(src, dest);
copyStyles(src, dest);
copyImage(src, dest);
if (src.style) {
for (int i = 0; i < src.vertexCount; i++) {
float[] vert = src.vertices[i];
dest.fill((int)(vert[PGraphics.A] * 255) << 24 |
(int)(vert[PGraphics.R] * 255) << 16 |
(int)(vert[PGraphics.G] * 255) << 8 |
(int)(vert[PGraphics.B] * 255));
// Do we need to copy these as well?
// dest.ambient(vert[PGraphics.AR] * 255, vert[PGraphics.AG] * 255, vert[PGraphics.AB] * 255);
// dest.specular(vert[PGraphics.SPR] * 255, vert[PGraphics.SPG] * 255, vert[PGraphics.SPB] * 255);
// dest.emissive(vert[PGraphics.ER] * 255, vert[PGraphics.EG] * 255, vert[PGraphics.EB] * 255);
// dest.shininess(vert[PGraphics.SHINE]);
if (0 < PApplet.dist(vert[PGraphics.NX],
vert[PGraphics.NY],
vert[PGraphics.NZ], 0, 0, 0)) {
dest.normal(vert[PGraphics.NX],
vert[PGraphics.NY],
vert[PGraphics.NZ]);
}
dest.vertex(vert[X], vert[Y], vert[Z],
vert[PGraphics.U],
vert[PGraphics.V]);
}
} else {
for (int i = 0; i < src.vertexCount; i++) {
float[] vert = src.vertices[i];
if (vert[Z] == 0) {
dest.vertex(vert[X], vert[Y]);
} else {
dest.vertex(vert[X], vert[Y], vert[Z]);
}
}
}
dest.endShape();
}
// TODO unapproved
/**
*
* @param src
* @param dest
*/
static protected void copyPath(PShape src, PShape dest) {
copyMatrix(src, dest);
copyStyles(src, dest);
copyImage(src, dest);
dest.close = src.close;
dest.setPath(src.vertexCount, src.vertices, src.vertexCodeCount, src.vertexCodes);
}
// TODO unapproved
/**
*
* @param src
* @param dest
*/
static protected void copyMatrix(PShape src, PShape dest) {
if (src.matrix != null) {
dest.applyMatrix(src.matrix);
}
}
// TODO unapproved
/**
*
* @param src
* @param dest
*/
static protected void copyStyles(PShape src, PShape dest) {
dest.ellipseMode = src.ellipseMode;
dest.rectMode = src.rectMode;
if (src.stroke) {
dest.stroke = true;
dest.strokeColor = src.strokeColor;
dest.strokeWeight = src.strokeWeight;
dest.strokeCap = src.strokeCap;
dest.strokeJoin = src.strokeJoin;
} else {
dest.stroke = false;
}
if (src.fill) {
dest.fill = true;
dest.fillColor = src.fillColor;
} else {
dest.fill = false;
}
}
// TODO unapproved
/**
*
* @param src
* @param dest
*/
static protected void copyImage(PShape src, PShape dest) {
if (src.image != null) {
dest.texture(src.image);
}
}
////////////////////////////////////////////////////////////////////////
/**
* Called by the following (the shape() command adds the g)
* PShape s = loadShape("blah.svg");
* shape(s);
* @param g
*/
public void draw(PGraphics g) {
if (visible) {
pre(g);
drawImpl(g);
post(g);
}
}
/**
* Draws the SVG document.
* @param g
*/
protected void drawImpl(PGraphics g) {
if (family == GROUP) {
drawGroup(g);
} else if (family == PRIMITIVE) {
drawPrimitive(g);
} else if (family == GEOMETRY) {
// Not same as path: `kind` matters.
// drawPath(g);
drawGeometry(g);
} else if (family == PATH) {
drawPath(g);
}
}
/**
*
* @param g
*/
protected void drawGroup(PGraphics g) {
for (int i = 0; i < childCount; i++) {
children[i].draw(g);
}
}
/**
*
* @param 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) {
int oldMode = g.imageMode;
g.imageMode(CORNER);
g.image(image, params[0], params[1], params[2], params[3]);
g.imageMode(oldMode);
} else {
int oldMode = g.rectMode;
g.rectMode(rectMode);
if (params.length == 4) {
g.rect(params[0], params[1],
params[2], params[3]);
} else if (params.length == 5) {
g.rect(params[0], params[1],
params[2], params[3],
params[4]);
} else if (params.length == 8) {
g.rect(params[0], params[1],
params[2], params[3],
params[4], params[5],
params[6], params[7]);
}
g.rectMode(oldMode);
}
} else if (kind == ELLIPSE) {
int oldMode = g.ellipseMode;
g.ellipseMode(ellipseMode);
g.ellipse(params[0], params[1],
params[2], params[3]);
g.ellipseMode(oldMode);
} else if (kind == ARC) {
int oldMode = g.ellipseMode;
g.ellipseMode(ellipseMode);
if (params.length == 6) {
g.arc(params[0], params[1],
params[2], params[3],
params[4], params[5]);
} else if (params.length == 7) {
g.arc(params[0], params[1],
params[2], params[3],
params[4], params[5],
(int) params[6]);
}
g.ellipseMode(oldMode);
} 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]);
}
}
/**
*
* @param g
*/
protected void drawGeometry(PGraphics g) {
// get cache object using 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[Z] == 0) {
g.vertex(vert[X], vert[Y]);
} else {
g.vertex(vert[X], vert[Y], vert[Z]);
}
}
}
g.endShape(close ? CLOSE : OPEN);
}
/*
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();
}
*/
/**
*
* @param g
*/
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;
boolean insideContour = false;
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 QUADRATIC_VERTEX:
g.quadraticVertex(vertices[index+0][X], vertices[index+0][Y],
vertices[index+1][X], vertices[index+1][Y]);
index += 2;
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++;
break;
case BREAK:
if (insideContour) {
g.endContour();
}
g.beginContour();
insideContour = true;
}
}
} 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 QUADRATIC_VERTEX:
g.quadraticVertex(vertices[index+0][X], vertices[index+0][Y], vertices[index+0][Z],
vertices[index+1][X], vertices[index+1][Y], vertices[index+0][Z]);
index += 2;
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++;
break;
case BREAK:
if (insideContour) {
g.endContour();
}
g.beginContour();
insideContour = true;
}
}
}
}
if (insideContour) {
g.endContour();
}
g.endShape(close ? CLOSE : OPEN);
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
/**
*
* @return
*/
public PShape getParent() {
return parent;
}
/**
* @return
* @webref
* @brief Returns the number of children
*/
public int getChildCount() {
return childCount;
}
/** Resize the children[] array to be in line with childCount */
protected void crop() {
// https://github.com/processing/processing/issues/3347
if (children.length != childCount) {
children = (PShape[]) PApplet.subset(children, 0, childCount);
}
}
/**
*
* @return
*/
public PShape[] getChildren() {
crop();
return children;
}
/**
* ( begin auto-generated from PShape_getChild.xml )
*
* Extracts a child shape from a parent shape. Specify the name of the
* shape with the target parameter. The shape is returned as a
* PShape object, or null is returned if there is an error.
*
* ( end auto-generated )
* @return
* @webref pshape:method
* @usage web_application
* @brief Returns a child element of a shape as a PShape object
* @param index the layer position of the shape to get
* @see PShape#addChild(PShape)
*/
public PShape getChild(int index) {
crop();
return children[index];
}
/**
* @param target the name of the shape to get
* @return
*/
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 eldest grandparent, so that children can be found anywhere.
* @param target
* @return
*/
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
/**
* @webref pshape:method
* @brief Adds a new child
* @param who any variable of type PShape
* @see PShape#getChild(int)
*/
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);
}
}
// adds child who exactly at position idx in the array of children.
/**
* @param who
* @param idx the layer position in which to insert the new child
*/
public void addChild(PShape who, int idx) {
if (idx < childCount) {
if (childCount == children.length) {
children = (PShape[]) PApplet.expand(children);
}
// Copy [idx, childCount - 1] to [idx + 1, childCount]
for (int i = childCount - 1; i >= idx; i--) {
children[i + 1] = children[i];
}
childCount++;
children[idx] = who;
who.parent = this;
if (who.getName() != null) {
addName(who.getName(), who);
}
}
}
/**
* Remove the child shape with index idx.
* @param idx
*/
public void removeChild(int idx) {
if (idx < childCount) {
PShape child = children[idx];
// Copy [idx + 1, childCount - 1] to [idx, childCount - 2]
for (int i = idx; i < childCount - 1; i++) {
children[i] = children[i + 1];
}
childCount--;
if (child.getName() != null && nameTable != null) {
nameTable.remove(child.getName());
}
}
}
/**
* Add a shape to the name lookup table.
* @param nom
* @param shape
*/
public void addName(String nom, PShape shape) {
if (parent != null) {
parent.addName(nom, shape);
} else {
if (nameTable == null) {
nameTable = new HashMap();
}
nameTable.put(nom, shape);
}
}
/**
* Returns the index of child who.
* @param who
* @return
*/
public int getChildIndex(PShape who) {
for (int i = 0; i < childCount; i++) {
if (children[i] == who) {
return i;
}
}
return -1;
}
/**
*
* @return
*/
public PShape getTessellation() {
return null;
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
/** The shape type, one of GROUP, PRIMITIVE, PATH, or GEOMETRY.
* @return */
public int getFamily() {
return family;
}
/**
*
* @return
*/
public int getKind() {
return kind;
}
/**
*
* @return
*/
public float[] getParams() {
return getParams(null);
}
/**
*
* @param target
* @return
*/
public float[] getParams(float[] target) {
if (target == null || target.length != params.length) {
target = new float[params.length];
}
PApplet.arrayCopy(params, target);
return target;
}
/**
*
* @param index
* @return
*/
public float getParam(int index) {
return params[index];
}
/**
*
* @param source
*/
protected void setParams(float[] source) {
if (params == null) {
params = new float[source.length];
}
if (source.length != params.length) {
PGraphics.showWarning("Wrong number of parameters");
return;
}
PApplet.arrayCopy(source, params);
}
/**
*
* @param vcount
* @param verts
*/
public void setPath(int vcount, float[][] verts) {
setPath(vcount, verts, 0, null);
}
/**
*
* @param vcount
* @param verts
* @param ccount
* @param codes
*/
protected void setPath(int vcount, float[][] verts, int ccount, int[] codes) {
if (verts == null || verts.length < vcount) return;
if (0 < ccount && (codes == null || codes.length < ccount)) return;
int ndim = verts[0].length;
vertexCount = vcount;
vertices = new float[vertexCount][ndim];
for (int i = 0; i < vertexCount; i++) {
PApplet.arrayCopy(verts[i], vertices[i]);
}
vertexCodeCount = ccount;
if (0 < vertexCodeCount) {
vertexCodes = new int[vertexCodeCount];
PApplet.arrayCopy(codes, vertexCodes, vertexCodeCount);
}
}
/**
* @return
* @webref pshape:method
* @brief Returns the total number of vertices as an int
* @see PShape#getVertex(int)
* @see PShape#setVertex(int, float, float)
*/
public int getVertexCount() {
if (family == GROUP || family == PRIMITIVE) {
PGraphics.showWarning(NO_VERTICES_ERROR);
}
return vertexCount;
}
/**
* @return
* @webref pshape:method
* @brief Returns the vertex at the index position
* @param index the location of the vertex
* @see PShape#setVertex(int, float, float)
* @see PShape#getVertexCount()
*/
public PVector getVertex(int index) {
return getVertex(index, null);
}
/**
* @param index
* @param vec PVector to assign the data to
* @return
*/
public PVector getVertex(int index, PVector vec) {
if (vec == null) {
vec = new PVector();
}
float[] vert = vertices[index];
vec.x = vert[X];
vec.y = vert[Y];
if (vert.length > 2) {
vec.z = vert[Z];
} else {
vec.z = 0; // in case this isn't a new vector
}
return vec;
}
/**
*
* @param index
* @return
*/
public float getVertexX(int index) {
return vertices[index][X];
}
/**
*
* @param index
* @return
*/
public float getVertexY(int index) {
return vertices[index][Y];
}
/**
*
* @param index
* @return
*/
public float getVertexZ(int index) {
return vertices[index][Z];
}
/**
* @webref pshape:method
* @brief Sets the vertex at the index position
* @param index the location of the vertex
* @param x the x value for the vertex
* @param y the y value for the vertex
* @see PShape#getVertex(int)
* @see PShape#getVertexCount()
*/
public void setVertex(int index, float x, float y) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setVertex()");
return;
}
vertices[index][X] = x;
vertices[index][Y] = y;
}
/**
* @param index
* @param x
* @param z the z value for the vertex
* @param y
*/
public void setVertex(int index, float x, float y, float z) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setVertex()");
return;
}
vertices[index][X] = x;
vertices[index][Y] = y;
vertices[index][Z] = z;
}
/**
* @param index
* @param vec the PVector to define the x, y, z coordinates
*/
public void setVertex(int index, PVector vec) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setVertex()");
return;
}
vertices[index][X] = vec.x;
vertices[index][Y] = vec.y;
if (vertices[index].length > 2) {
vertices[index][Z] = vec.z;
} else if (vec.z != 0 && vec.z == vec.z) {
throw new IllegalArgumentException("Cannot set a z-coordinate on a 2D shape");
}
}
/**
*
* @param index
* @return
*/
public PVector getNormal(int index) {
return getNormal(index, null);
}
/**
*
* @param index
* @param vec
* @return
*/
public PVector getNormal(int index, PVector vec) {
if (vec == null) {
vec = new PVector();
}
vec.x = vertices[index][PGraphics.NX];
vec.y = vertices[index][PGraphics.NY];
vec.z = vertices[index][PGraphics.NZ];
return vec;
}
/**
*
* @param index
* @return
*/
public float getNormalX(int index) {
return vertices[index][PGraphics.NX];
}
/**
*
* @param index
* @return
*/
public float getNormalY(int index) {
return vertices[index][PGraphics.NY];
}
/**
*
* @param index
* @return
*/
public float getNormalZ(int index) {
return vertices[index][PGraphics.NZ];
}
/**
*
* @param index
* @param nx
* @param ny
* @param nz
*/
public void setNormal(int index, float nx, float ny, float nz) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setNormal()");
return;
}
vertices[index][PGraphics.NX] = nx;
vertices[index][PGraphics.NY] = ny;
vertices[index][PGraphics.NZ] = nz;
}
/**
*
* @param name
* @param index
* @param values
*/
public void setAttrib(String name, int index, float... values) {
}
/**
*
* @param name
* @param index
* @param values
*/
public void setAttrib(String name, int index, int... values) {
}
/**
*
* @param name
* @param index
* @param values
*/
public void setAttrib(String name, int index, boolean... values) {
}
/**
*
* @param index
* @return
*/
public float getTextureU(int index) {
return vertices[index][PGraphics.U];
}
/**
*
* @param index
* @return
*/
public float getTextureV(int index) {
return vertices[index][PGraphics.V];
}
/**
*
* @param index
* @param u
* @param v
*/
public void setTextureUV(int index, float u, float v) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setTextureUV()");
return;
}
// make sure we allocated the vertices array and that vertex exists
if (vertices == null ||
index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setTextureUV()");
return;
}
vertices[index][PGraphics.U] = u;
vertices[index][PGraphics.V] = v;
}
/**
*
* @param mode
*/
public void setTextureMode(int mode) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setTextureMode()");
return;
}
textureMode = mode;
}
/**
*
* @param tex
*/
public void setTexture(PImage tex) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setTexture()");
return;
}
image = tex;
}
/**
*
* @param index
* @return
*/
public int getFill(int index) {
// make sure we allocated the vertices array and that vertex exists
if (vertices == null ||
index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getFill()");
return fillColor;
}
if (image == null) {
int a = (int) (vertices[index][PGraphics.A] * 255);
int r = (int) (vertices[index][PGraphics.R] * 255);
int g = (int) (vertices[index][PGraphics.G] * 255);
int b = (int) (vertices[index][PGraphics.B] * 255);
return (a << 24) | (r << 16) | (g << 8) | b;
} else {
return 0;
}
}
/**
* @param fill
* @nowebref
*/
public void setFill(boolean fill) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setFill()");
return;
}
this.fill = fill;
}
/**
* ( begin auto-generated from PShape_setFill.xml )
*
* The setFill() method defines the fill color of a PShape.
* This method is used after shapes are created or when a shape is defined explicitly
* (e.g. createShape(RECT, 20, 20, 80, 80)) as shown in the above example.
* When a shape is created with beginShape() and endShape(), its
* attributes may be changed with fill() and stroke() within
* beginShape() and endShape(). However, after the shape is
* created, only the setFill() method can define a new fill value for
* the PShape.
*
* ( end auto-generated )
*
* @webref
* @param fill
* @brief Set the fill value
*/
public void setFill(int fill) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setFill()");
return;
}
this.fillColor = fill;
if (vertices != null && perVertexStyles) {
for (int i = 0; i < vertexCount; i++) {
setFill(i, fill);
}
}
}
/**
* @param index
* @param fill
* @nowebref
*/
public void setFill(int index, int fill) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setFill()");
return;
}
if (!perVertexStyles) {
PGraphics.showWarning(PER_VERTEX_UNSUPPORTED, "setFill()");
return;
}
// make sure we allocated the vertices array and that vertex exists
if (vertices == null || index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getFill()");
return;
}
if (image == null) {
vertices[index][PGraphics.A] = ((fill >> 24) & 0xFF) / 255.0f;
vertices[index][PGraphics.R] = ((fill >> 16) & 0xFF) / 255.0f;
vertices[index][PGraphics.G] = ((fill >> 8) & 0xFF) / 255.0f;
vertices[index][PGraphics.B] = ((fill >> 0) & 0xFF) / 255.0f;
}
}
/**
*
* @param index
* @return
*/
public int getTint(int index) {
// make sure we allocated the vertices array and that vertex exists
if (vertices == null || index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getTint()");
return this.tintColor;
}
if (image != null) {
int a = (int) (vertices[index][PGraphics.A] * 255);
int r = (int) (vertices[index][PGraphics.R] * 255);
int g = (int) (vertices[index][PGraphics.G] * 255);
int b = (int) (vertices[index][PGraphics.B] * 255);
return (a << 24) | (r << 16) | (g << 8) | b;
} else {
return 0;
}
}
/**
*
* @param tint
*/
public void setTint(boolean tint) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setTint()");
return;
}
this.tint = tint;
}
/**
*
* @param fill
*/
public void setTint(int fill) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setTint()");
return;
}
tintColor = fill;
if (vertices != null) {
for (int i = 0; i < vertices.length; i++) {
setFill(i, fill);
}
}
}
/**
*
* @param index
* @param tint
*/
public void setTint(int index, int tint) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setTint()");
return;
}
// make sure we allocated the vertices array and that vertex exists
if (vertices == null ||
index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setTint()");
return;
}
if (image != null) {
vertices[index][PGraphics.A] = ((tint >> 24) & 0xFF) / 255.0f;
vertices[index][PGraphics.R] = ((tint >> 16) & 0xFF) / 255.0f;
vertices[index][PGraphics.G] = ((tint >> 8) & 0xFF) / 255.0f;
vertices[index][PGraphics.B] = ((tint >> 0) & 0xFF) / 255.0f;
}
}
/**
*
* @param index
* @return
*/
public int getStroke(int index) {
// make sure we allocated the vertices array and that vertex exists
if (vertices == null ||
index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getStroke()");
return strokeColor;
}
int a = (int) (vertices[index][PGraphics.SA] * 255);
int r = (int) (vertices[index][PGraphics.SR] * 255);
int g = (int) (vertices[index][PGraphics.SG] * 255);
int b = (int) (vertices[index][PGraphics.SB] * 255);
return (a << 24) | (r << 16) | (g << 8) | b;
}
/**
* @param stroke
* @nowebref
*/
public void setStroke(boolean stroke) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setStroke()");
return;
}
this.stroke = stroke;
}
/**
* ( begin auto-generated from PShape_setStroke.xml )
*
* The setStroke() method defines the outline color of a PShape.
* This method is used after shapes are created or when a shape is defined
* explicitly (e.g. createShape(RECT, 20, 20, 80, 80)) as shown in
* the above example. When a shape is created with beginShape() and
* endShape(), its attributes may be changed with fill() and
* stroke() within beginShape() and endShape().
* However, after the shape is created, only the setStroke() method
* can define a new stroke value for the PShape.
*
* ( end auto-generated )
*
* @webref
* @param stroke
* @brief Set the stroke value
*/
public void setStroke(int stroke) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setStroke()");
return;
}
strokeColor = stroke;
if (vertices != null && perVertexStyles) {
for (int i = 0; i < vertices.length; i++) {
setStroke(i, stroke);
}
}
}
/**
* @param index
* @param stroke
* @nowebref
*/
public void setStroke(int index, int stroke) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setStroke()");
return;
}
if (!perVertexStyles) {
PGraphics.showWarning(PER_VERTEX_UNSUPPORTED, "setStroke()");
return;
}
// make sure we allocated the vertices array and that vertex exists
if (vertices == null || index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setStroke()");
return;
}
vertices[index][PGraphics.SA] = ((stroke >> 24) & 0xFF) / 255.0f;
vertices[index][PGraphics.SR] = ((stroke >> 16) & 0xFF) / 255.0f;
vertices[index][PGraphics.SG] = ((stroke >> 8) & 0xFF) / 255.0f;
vertices[index][PGraphics.SB] = ((stroke >> 0) & 0xFF) / 255.0f;
}
/**
*
* @param index
* @return
*/
public float getStrokeWeight(int index) {
// make sure we allocated the vertices array and that vertex exists
if (vertices == null || index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getStrokeWeight()");
return strokeWeight;
}
return vertices[index][PGraphics.SW];
}
/**
*
* @param weight
*/
public void setStrokeWeight(float weight) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setStrokeWeight()");
return;
}
strokeWeight = weight;
if (vertices != null && perVertexStyles) {
for (int i = 0; i < vertexCount; i++) {
setStrokeWeight(i, weight);
}
}
}
/**
*
* @param index
* @param weight
*/
public void setStrokeWeight(int index, float weight) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setStrokeWeight()");
return;
}
if (!perVertexStyles) {
PGraphics.showWarning(PER_VERTEX_UNSUPPORTED, "setStrokeWeight()");
return;
}
// make sure we allocated the vertices array and that vertex exists
if (vertices == null || index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setStrokeWeight()");
return;
}
vertices[index][PGraphics.SW] = weight;
}
/**
*
* @param join
*/
public void setStrokeJoin(int join) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setStrokeJoin()");
return;
}
strokeJoin = join;
}
/**
*
* @param cap
*/
public void setStrokeCap(int cap) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setStrokeCap()");
return;
}
strokeCap = cap;
}
/**
*
* @param index
* @return
*/
public int getAmbient(int index) {
// make sure we allocated the vertices array and that vertex exists
if (vertices == null || index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getAmbient()");
return ambientColor;
}
int r = (int) (vertices[index][PGraphics.AR] * 255);
int g = (int) (vertices[index][PGraphics.AG] * 255);
int b = (int) (vertices[index][PGraphics.AB] * 255);
return 0xff000000 | (r << 16) | (g << 8) | b;
}
/**
*
* @param ambient
*/
public void setAmbient(int ambient) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setAmbient()");
return;
}
ambientColor = ambient;
if (vertices != null) {
for (int i = 0; i < vertices.length; i++) {
setAmbient(i, ambient);
}
}
}
/**
*
* @param index
* @param ambient
*/
public void setAmbient(int index, int ambient) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setAmbient()");
return;
}
// make sure we allocated the vertices array and that vertex exists
if (vertices == null || index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setAmbient()");
return;
}
vertices[index][PGraphics.AR] = ((ambient >> 16) & 0xFF) / 255.0f;
vertices[index][PGraphics.AG] = ((ambient >> 8) & 0xFF) / 255.0f;
vertices[index][PGraphics.AB] = ((ambient >> 0) & 0xFF) / 255.0f;
}
/**
*
* @param index
* @return
*/
public int getSpecular(int index) {
// make sure we allocated the vertices array and that vertex exists
if (vertices == null || index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getSpecular()");
return specularColor;
}
int r = (int) (vertices[index][PGraphics.SPR] * 255);
int g = (int) (vertices[index][PGraphics.SPG] * 255);
int b = (int) (vertices[index][PGraphics.SPB] * 255);
return 0xff000000 | (r << 16) | (g << 8) | b;
}
/**
*
* @param specular
*/
public void setSpecular(int specular) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setSpecular()");
return;
}
specularColor = specular;
if (vertices != null) {
for (int i = 0; i < vertices.length; i++) {
setSpecular(i, specular);
}
}
}
/**
*
* @param index
* @param specular
*/
public void setSpecular(int index, int specular) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setSpecular()");
return;
}
// make sure we allocated the vertices array and that vertex exists
if (vertices == null || index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setSpecular()");
return;
}
vertices[index][PGraphics.SPR] = ((specular >> 16) & 0xFF) / 255.0f;
vertices[index][PGraphics.SPG] = ((specular >> 8) & 0xFF) / 255.0f;
vertices[index][PGraphics.SPB] = ((specular >> 0) & 0xFF) / 255.0f;
}
/**
*
* @param index
* @return
*/
public int getEmissive(int index) {
// make sure we allocated the vertices array and that vertex exists
if (vertices == null || index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getEmissive()");
return emissiveColor;
}
int r = (int) (vertices[index][PGraphics.ER] * 255);
int g = (int) (vertices[index][PGraphics.EG] * 255);
int b = (int) (vertices[index][PGraphics.EB] * 255);
return 0xff000000 | (r << 16) | (g << 8) | b;
}
/**
*
* @param emissive
*/
public void setEmissive(int emissive) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setEmissive()");
return;
}
emissiveColor = emissive;
if (vertices != null) {
for (int i = 0; i < vertices.length; i++) {
setEmissive(i, emissive);
}
}
}
/**
*
* @param index
* @param emissive
*/
public void setEmissive(int index, int emissive) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setEmissive()");
return;
}
// make sure we allocated the vertices array and that vertex exists
if (vertices == null ||
index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setEmissive()");
return;
}
vertices[index][PGraphics.ER] = ((emissive >> 16) & 0xFF) / 255.0f;
vertices[index][PGraphics.EG] = ((emissive >> 8) & 0xFF) / 255.0f;
vertices[index][PGraphics.EB] = ((emissive >> 0) & 0xFF) / 255.0f;
}
/**
*
* @param index
* @return
*/
public float getShininess(int index) {
// make sure we allocated the vertices array and that vertex exists
if (vertices == null ||
index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "getShininess()");
return shininess;
}
return vertices[index][PGraphics.SHINE];
}
/**
*
* @param shine
*/
public void setShininess(float shine) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setShininess()");
return;
}
shininess = shine;
if (vertices != null) {
for (int i = 0; i < vertices.length; i++) {
setShininess(i, shine);
}
}
}
/**
*
* @param index
* @param shine
*/
public void setShininess(int index, float shine) {
if (openShape) {
PGraphics.showWarning(INSIDE_BEGIN_END_ERROR, "setShininess()");
return;
}
// make sure we allocated the vertices array and that vertex exists
if (vertices == null ||
index >= vertices.length) {
PGraphics.showWarning(NO_SUCH_VERTEX_ERROR + " (" + index + ")", "setShininess()");
return;
}
vertices[index][PGraphics.SHINE] = shine;
}
/**
*
* @return
*/
public int[] getVertexCodes() {
if (vertexCodes == null) {
return null;
}
if (vertexCodes.length != vertexCodeCount) {
vertexCodes = PApplet.subset(vertexCodes, 0, vertexCodeCount);
}
return vertexCodes;
}
/**
*
* @return
*/
public int getVertexCodeCount() {
return vertexCodeCount;
}
/**
* One of VERTEX, BEZIER_VERTEX, CURVE_VERTEX, or BREAK.
* @param index
* @return
*/
public int getVertexCode(int index) {
return vertexCodes[index];
}
/**
*
* @return
*/
public boolean isClosed() {
return close;
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
/**
*
* @param x
* @param y
* @return
*/
public boolean contains(float x, float y) {
if (family == PATH) {
boolean c = false;
for (int i = 0, j = vertexCount-1; i < vertexCount; j = i++) {
if (((vertices[i][Y] > y) != (vertices[j][Y] > y)) &&
(x <
(vertices[j][X]-vertices[i][X]) *
(y-vertices[i][Y]) /
(vertices[j][1]-vertices[i][Y]) +
vertices[i][X])) {
c = !c;
}
}
return c;
} else {
throw new IllegalArgumentException("The contains() method is only implemented for paths.");
}
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// 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
/**
* ( begin auto-generated from PShape_translate.xml )
*
* Specifies an amount to displace the shape. The x parameter
* specifies left/right translation, the y parameter specifies
* up/down translation, and the z parameter specifies translations
* toward/away from the screen. Subsequent calls to the method accumulates
* the effect. For example, calling translate(50, 0) and then
* translate(20, 0) is the same as translate(70, 0). This
* transformation is applied directly to the shape, it's not refreshed each
* time draw() is run.
*
* Using this method with the z parameter requires using the P3D
* parameter in combination with size.
*
* ( end auto-generated )
* @webref pshape:method
* @usage web_application
* @brief Displaces the shape
* @param x left/right translation
* @param y up/down translation
* @see PShape#rotate(float)
* @see PShape#scale(float)
* @see PShape#resetMatrix()
*/
public void translate(float x, float y) {
checkMatrix(2);
matrix.translate(x, y);
}
/**
* @param x
* @param z forward/back translation
* @param y
*/
public void translate(float x, float y, float z) {
checkMatrix(3);
matrix.translate(x, y, z);
}
/**
* ( begin auto-generated from PShape_rotateX.xml )
*
* Rotates a shape around the x-axis the amount specified by the
* angle parameter. Angles should be specified in radians (values
* from 0 to TWO_PI) or converted to radians with the radians() method.
*
* Shapes are always rotated around the upper-left corner of their bounding
* box. Positive numbers rotate objects in a clockwise direction.
* Subsequent calls to the method accumulates the effect. For example,
* calling rotateX(HALF_PI) and then rotateX(HALF_PI) is the
* same as rotateX(PI). This transformation is applied directly to
* the shape, it's not refreshed each time draw() is run.
*
* This method requires a 3D renderer. You need to use P3D as a third
* parameter for the size() function as shown in the example above.
*
* ( end auto-generated )
* @webref pshape:method
* @usage web_application
* @brief Rotates the shape around the x-axis
* @param angle angle of rotation specified in radians
* @see PShape#rotate(float)
* @see PShape#rotateY(float)
* @see PShape#rotateZ(float)
* @see PShape#scale(float)
* @see PShape#translate(float, float)
* @see PShape#resetMatrix()
*/
public void rotateX(float angle) {
rotate(angle, 1, 0, 0);
}
/**
* ( begin auto-generated from PShape_rotateY.xml )
*
* Rotates a shape around the y-axis the amount specified by the
* angle parameter. Angles should be specified in radians (values
* from 0 to TWO_PI) or converted to radians with the radians() method.
*
* Shapes are always rotated around the upper-left corner of their bounding
* box. Positive numbers rotate objects in a clockwise direction.
* Subsequent calls to the method accumulates the effect. For example,
* calling rotateY(HALF_PI) and then rotateY(HALF_PI) is the
* same as rotateY(PI). This transformation is applied directly to
* the shape, it's not refreshed each time draw() is run.
*
* This method requires a 3D renderer. You need to use P3D as a third
* parameter for the size() function as shown in the example above.
*
* ( end auto-generated )
*
* @webref pshape:method
* @usage web_application
* @brief Rotates the shape around the y-axis
* @param angle angle of rotation specified in radians
* @see PShape#rotate(float)
* @see PShape#rotateX(float)
* @see PShape#rotateZ(float)
* @see PShape#scale(float)
* @see PShape#translate(float, float)
* @see PShape#resetMatrix()
*/
public void rotateY(float angle) {
rotate(angle, 0, 1, 0);
}
/**
* ( begin auto-generated from PShape_rotateZ.xml )
*
* Rotates a shape around the z-axis the amount specified by the
* angle parameter. Angles should be specified in radians (values
* from 0 to TWO_PI) or converted to radians with the radians() method.
*
* Shapes are always rotated around the upper-left corner of their bounding
* box. Positive numbers rotate objects in a clockwise direction.
* Subsequent calls to the method accumulates the effect. For example,
* calling rotateZ(HALF_PI) and then rotateZ(HALF_PI) is the
* same as rotateZ(PI). This transformation is applied directly to
* the shape, it's not refreshed each time draw() is run.
*
* This method requires a 3D renderer. You need to use P3D as a third
* parameter for the size() function as shown in the example above.
*
* ( end auto-generated )
* @webref pshape:method
* @usage web_application
* @brief Rotates the shape around the z-axis
* @param angle angle of rotation specified in radians
* @see PShape#rotate(float)
* @see PShape#rotateX(float)
* @see PShape#rotateY(float)
* @see PShape#scale(float)
* @see PShape#translate(float, float)
* @see PShape#resetMatrix()
*/
public void rotateZ(float angle) {
rotate(angle, 0, 0, 1);
}
/**
* ( begin auto-generated from PShape_rotate.xml )
*
* Rotates a shape the amount specified by the angle parameter.
* Angles should be specified in radians (values from 0 to TWO_PI) or
* converted to radians with the radians() method.
*
* Shapes are always rotated around the upper-left corner of their bounding
* box. Positive numbers rotate objects in a clockwise direction.
* Transformations apply to everything that happens after and subsequent
* calls to the method accumulates the effect. For example, calling
* rotate(HALF_PI) and then rotate(HALF_PI) is the same as
* rotate(PI). This transformation is applied directly to the shape,
* it's not refreshed each time draw() is run.
*
* ( end auto-generated )
* @webref pshape:method
* @usage web_application
* @brief Rotates the shape
* @param angle angle of rotation specified in radians
* @see PShape#rotateX(float)
* @see PShape#rotateY(float)
* @see PShape#rotateZ(float)
* @see PShape#scale(float)
* @see PShape#translate(float, float)
* @see PShape#resetMatrix()
*/
public void rotate(float angle) {
checkMatrix(2); // at least 2...
matrix.rotate(angle);
}
/**
* @param angle
* @param v0
* @param v1
* @param v2
* @nowebref
*/
public void rotate(float angle, float v0, float v1, float v2) {
checkMatrix(3);
float norm2 = v0 * v0 + v1 * v1 + v2 * v2;
if (Math.abs(norm2 - 1) > EPSILON) {
// The rotation vector is not normalized.
float norm = PApplet.sqrt(norm2);
v0 /= norm;
v1 /= norm;
v2 /= norm;
}
matrix.rotate(angle, v0, v1, v2);
}
//
/**
* ( begin auto-generated from PShape_scale.xml )
*
* Increases or decreases the size of a shape by expanding and contracting
* vertices. Shapes always scale from the relative origin of their bounding
* box. Scale values are specified as decimal percentages. For example, the
* method call scale(2.0) increases the dimension of a shape by
* 200%. Subsequent calls to the method multiply the effect. For example,
* calling scale(2.0) and then scale(1.5) is the same as
* scale(3.0). This transformation is applied directly to the shape,
* it's not refreshed each time draw() is run.
*
* Using this method with the z parameter requires using the P3D
* parameter in combination with size.
*
* ( end auto-generated )
* @webref pshape:method
* @usage web_application
* @brief Increases and decreases the size of a shape
* @param s percentate to scale the object
* @see PShape#rotate(float)
* @see PShape#translate(float, float)
* @see PShape#resetMatrix()
*/
public void scale(float s) {
checkMatrix(2); // at least 2...
matrix.scale(s);
}
/**
*
* @param x
* @param y
*/
public void scale(float x, float y) {
checkMatrix(2);
matrix.scale(x, y);
}
/**
* @param x percentage to scale the object in the x-axis
* @param y percentage to scale the object in the y-axis
* @param z percentage to scale the object in the z-axis
*/
public void scale(float x, float y, float z) {
checkMatrix(3);
matrix.scale(x, y, z);
}
//
/**
* ( begin auto-generated from PShape_resetMatrix.xml )
*
* Replaces the current matrix of a shape with the identity matrix. The
* equivalent function in OpenGL is glLoadIdentity().
*
* ( end auto-generated )
* @webref pshape:method
* @brief Replaces the current matrix of a shape with the identity matrix
* @usage web_application
* @see PShape#rotate(float)
* @see PShape#scale(float)
* @see PShape#translate(float, float)
*/
public void resetMatrix() {
checkMatrix(2);
matrix.reset();
}
/**
*
* @param source
*/
public void applyMatrix(PMatrix source) {
if (source instanceof PMatrix2D) {
applyMatrix((PMatrix2D) source);
} else if (source instanceof PMatrix3D) {
applyMatrix((PMatrix3D) source);
}
}
/**
*
* @param 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);
}
/**
*
* @param n00
* @param n01
* @param n02
* @param n10
* @param n11
* @param n12
*/
public void applyMatrix(float n00, float n01, float n02,
float n10, float n11, float n12) {
checkMatrix(2);
matrix.apply(n00, n01, n02,
n10, n11, n12);
}
/**
*
* @param source
*/
public void applyMatrix(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);
}
/**
*
* @param n00
* @param n01
* @param n02
* @param n03
* @param n10
* @param n11
* @param n12
* @param n13
* @param n20
* @param n21
* @param n22
* @param n23
* @param n30
* @param n31
* @param n32
* @param n33
*/
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 at least the specified number of dimensions.
* @param 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.
* @param mode
*/
// public void pivot(float x, float y) {
// px = x;
// py = y;
// }
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
public void colorMode(int mode) {
colorMode(mode, colorModeX, colorModeY, colorModeZ, colorModeA);
}
/**
* @param mode
* @param max range for all color elements
*/
public void colorMode(int mode, float max) {
colorMode(mode, max, max, max, max);
}
/**
* @param mode
* @param maxX range for the red or hue depending on the current color mode
* @param maxY range for the green or saturation depending on the current color mode
* @param maxZ range for the blue or brightness depending on the current color mode
*/
public void colorMode(int mode, float maxX, float maxY, float maxZ) {
colorMode(mode, maxX, maxY, maxZ, colorModeA);
}
/**
* @param mode
* @param maxA range for the alpha
* @param maxY
* @param maxX
* @param maxZ
*/
public void colorMode(int mode,
float maxX, float maxY, float maxZ, float maxA) {
colorMode = mode;
colorModeX = maxX; // still needs to be set for hsb
colorModeY = maxY;
colorModeZ = maxZ;
colorModeA = maxA;
// if color max values are all 1, then no need to scale
colorModeScale =
((maxA != 1) || (maxX != maxY) || (maxY != maxZ) || (maxZ != maxA));
// if color is rgb/0..255 this will make it easier for the
// red() green() etc functions
colorModeDefault = (colorMode == RGB) &&
(colorModeA == 255) && (colorModeX == 255) &&
(colorModeY == 255) && (colorModeZ == 255);
}
/**
*
* @param rgb
*/
protected void colorCalc(int rgb) {
if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
colorCalc((float) rgb);
} else {
colorCalcARGB(rgb, colorModeA);
}
}
/**
*
* @param rgb
* @param alpha
*/
protected void colorCalc(int rgb, float alpha) {
if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
colorCalc((float) rgb, alpha);
} else {
colorCalcARGB(rgb, alpha);
}
}
/**
*
* @param gray
*/
protected void colorCalc(float gray) {
colorCalc(gray, colorModeA);
}
/**
*
* @param gray
* @param alpha
*/
protected void colorCalc(float gray, float alpha) {
if (gray > colorModeX) gray = colorModeX;
if (alpha > colorModeA) alpha = colorModeA;
if (gray < 0) gray = 0;
if (alpha < 0) alpha = 0;
calcR = colorModeScale ? (gray / colorModeX) : gray;
calcG = calcR;
calcB = calcR;
calcA = colorModeScale ? (alpha / colorModeA) : alpha;
calcRi = (int)(calcR*255); calcGi = (int)(calcG*255);
calcBi = (int)(calcB*255); calcAi = (int)(calcA*255);
calcColor = (calcAi << 24) | (calcRi << 16) | (calcGi << 8) | calcBi;
calcAlpha = (calcAi != 255);
}
/**
*
* @param x
* @param y
* @param z
*/
protected void colorCalc(float x, float y, float z) {
colorCalc(x, y, z, colorModeA);
}
/**
*
* @param x
* @param y
* @param z
* @param a
*/
protected void colorCalc(float x, float y, float z, float a) {
if (x > colorModeX) x = colorModeX;
if (y > colorModeY) y = colorModeY;
if (z > colorModeZ) z = colorModeZ;
if (a > colorModeA) a = colorModeA;
if (x < 0) x = 0;
if (y < 0) y = 0;
if (z < 0) z = 0;
if (a < 0) a = 0;
switch (colorMode) {
case RGB:
if (colorModeScale) {
calcR = x / colorModeX;
calcG = y / colorModeY;
calcB = z / colorModeZ;
calcA = a / colorModeA;
} else {
calcR = x; calcG = y; calcB = z; calcA = a;
}
break;
case HSB:
x /= colorModeX; // h
y /= colorModeY; // s
z /= colorModeZ; // b
calcA = colorModeScale ? (a/colorModeA) : a;
if (y == 0) { // saturation == 0
calcR = calcG = calcB = z;
} else {
float which = (x - (int)x) * 6.0f;
float f = which - (int)which;
float p = z * (1.0f - y);
float q = z * (1.0f - y * f);
float t = z * (1.0f - (y * (1.0f - f)));
switch ((int)which) {
case 0: calcR = z; calcG = t; calcB = p; break;
case 1: calcR = q; calcG = z; calcB = p; break;
case 2: calcR = p; calcG = z; calcB = t; break;
case 3: calcR = p; calcG = q; calcB = z; break;
case 4: calcR = t; calcG = p; calcB = z; break;
case 5: calcR = z; calcG = p; calcB = q; break;
}
}
break;
}
calcRi = (int)(255*calcR); calcGi = (int)(255*calcG);
calcBi = (int)(255*calcB); calcAi = (int)(255*calcA);
calcColor = (calcAi << 24) | (calcRi << 16) | (calcGi << 8) | calcBi;
calcAlpha = (calcAi != 255);
}
/**
*
* @param argb
* @param alpha
*/
protected void colorCalcARGB(int argb, float alpha) {
if (alpha == colorModeA) {
calcAi = (argb >> 24) & 0xff;
calcColor = argb;
} else {
calcAi = (int) (((argb >> 24) & 0xff) * (alpha / colorModeA));
calcColor = (calcAi << 24) | (argb & 0xFFFFFF);
}
calcRi = (argb >> 16) & 0xff;
calcGi = (argb >> 8) & 0xff;
calcBi = argb & 0xff;
calcA = calcAi / 255.0f;
calcR = calcRi / 255.0f;
calcG = calcGi / 255.0f;
calcB = calcBi / 255.0f;
calcAlpha = (calcAi != 255);
}
}