/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* Part of the Processing project - http://processing.org Copyright (c) 2004-09 Ben Fry and Casey Reas Copyright (c) 2001-04 Massachusetts Institute of Technology This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package processing.core; import java.awt.*; import java.util.HashMap; /** * Main graphics and rendering context, as well as the base API implementation for processing "core". * Use this class if you need to draw into an off-screen graphics buffer. * A PGraphics object can be constructed with the createGraphics() function. * The beginDraw() and endDraw() methods (see above example) are necessary to set up the buffer and to finalize it. * The fields and methods for this class are extensive; * for a complete list visit the developer's reference: http://dev.processing.org/reference/core/ * =advanced * Main graphics and rendering context, as well as the base API implementation. * *
* This is called when a sketch is shut down and this renderer was * specified using the size() command, or inside endRecord() and * endRaw(), in order to shut things off. */ public void dispose() { // ignore } ////////////////////////////////////////////////////////////// // FRAME /** * Some renderers have requirements re: when they are ready to draw. */ public boolean canDraw() { // ignore return true; } /** * Sets the default properties for a PGraphics object. It should be called before anything is drawn into the object. * =advanced *
* When creating your own PGraphics, you should call this before * drawing anything. * * @webref * @brief Sets up the rendering context */ public void beginDraw() { // ignore } /** * Finalizes the rendering of a PGraphics object so that it can be shown on screen. * =advanced * * When creating your own PGraphics, you should call this when * you're finished drawing. * * @webref * @brief Finalizes the renderering context */ public void endDraw() { // ignore } public void flush() { // no-op, mostly for P3D to write sorted stuff } protected void checkSettings() { if (!settingsInited) defaultSettings(); } /** * Set engine's default values. This has to be called by PApplet, * somewhere inside setup() or draw() because it talks to the * graphics buffer, meaning that for subclasses like OpenGL, there * needs to be a valid graphics context to mess with otherwise * you'll get some good crashing action. * * This is currently called by checkSettings(), during beginDraw(). */ protected void defaultSettings() { // ignore // System.out.println("PGraphics.defaultSettings() " + width + " " + height); noSmooth(); // 0149 colorMode(RGB, 255); fill(255); stroke(0); // as of 0178, no longer relying on local versions of the variables // being set, because subclasses may need to take extra action. strokeWeight(DEFAULT_STROKE_WEIGHT); strokeJoin(DEFAULT_STROKE_JOIN); strokeCap(DEFAULT_STROKE_CAP); // init shape stuff shape = 0; // init matrices (must do before lights) //matrixStackDepth = 0; rectMode(CORNER); ellipseMode(DIAMETER); // no current font textFont = null; textSize = 12; textLeading = 14; textAlign = LEFT; textMode = MODEL; // if this fella is associated with an applet, then clear its background. // if it's been created by someone else through createGraphics, // they have to call background() themselves, otherwise everything gets // a gray background (when just a transparent surface or an empty pdf // is what's desired). // this background() call is for the Java 2D and OpenGL renderers. if (primarySurface) { //System.out.println("main drawing surface bg " + getClass().getName()); background(backgroundColor); } settingsInited = true; // defaultSettings() overlaps reapplySettings(), don't do both //reapplySettings = false; } /** * Re-apply current settings. Some methods, such as textFont(), require that * their methods be called (rather than simply setting the textFont variable) * because they affect the graphics context, or they require parameters from * the context (e.g. getting native fonts for text). * * This will only be called from an allocate(), which is only called from * size(), which is safely called from inside beginDraw(). And it cannot be * called before defaultSettings(), so we should be safe. */ protected void reapplySettings() { // System.out.println("attempting reapplySettings()"); if (!settingsInited) return; // if this is the initial setup, no need to reapply // System.out.println(" doing reapplySettings"); // new Exception().printStackTrace(System.out); colorMode(colorMode, colorModeX, colorModeY, colorModeZ); if (fill) { // PApplet.println(" fill " + PApplet.hex(fillColor)); fill(fillColor); } else { noFill(); } if (stroke) { stroke(strokeColor); // The if() statements should be handled inside the functions, // otherwise an actual reset/revert won't work properly. //if (strokeWeight != DEFAULT_STROKE_WEIGHT) { strokeWeight(strokeWeight); //} // if (strokeCap != DEFAULT_STROKE_CAP) { strokeCap(strokeCap); // } // if (strokeJoin != DEFAULT_STROKE_JOIN) { strokeJoin(strokeJoin); // } } else { noStroke(); } if (tint) { tint(tintColor); } else { noTint(); } if (smooth) { smooth(); } else { // Don't bother setting this, cuz it'll anger P3D. noSmooth(); } if (textFont != null) { // System.out.println(" textFont in reapply is " + textFont); // textFont() resets the leading, so save it in case it's changed float saveLeading = textLeading; textFont(textFont, textSize); textLeading(saveLeading); } textMode(textMode); textAlign(textAlign, textAlignY); background(backgroundColor); //reapplySettings = false; } ////////////////////////////////////////////////////////////// // HINTS /** * Set various hints and hacks for the renderer. This is used to handle obscure rendering features that cannot be implemented in a consistent manner across renderers. Many options will often graduate to standard features instead of hints over time. ** Differences between beginShape() and line() and point() methods. *
* beginShape() is intended to be more flexible at the expense of being * a little more complicated to use. it handles more complicated shapes * that can consist of many connected lines (so you get joins) or lines * mixed with curves. *
* The line() and point() command are for the far more common cases * (particularly for our audience) that simply need to draw a line * or a point on the screen. *
* From the code side of things, line() may or may not call beginShape() * to do the drawing. In the beta code, they do, but in the alpha code, * they did not. they might be implemented one way or the other depending * on tradeoffs of runtime efficiency vs. implementation efficiency &mdash * meaning the speed that things run at vs. the speed it takes me to write * the code and maintain it. for beta, the latter is most important so * that's how things are implemented. */ public void beginShape(int kind) { shape = kind; } /** * Sets whether the upcoming vertex is part of an edge. * Equivalent to glEdgeFlag(), for people familiar with OpenGL. */ public void edge(boolean edge) { this.edge = edge; } /** * Sets the current normal vector. Only applies with 3D rendering * and inside a beginShape/endShape block. *
* This is for drawing three dimensional shapes and surfaces, * allowing you to specify a vector perpendicular to the surface * of the shape, which determines how lighting affects it. * * For the most part, PGraphics3D will attempt to automatically * assign normals to shapes, but since that's imperfect, * this is a better option when you want more control. * * For people familiar with OpenGL, this function is basically * identical to glNormal3f(). */ public void normal(float nx, float ny, float nz) { normalX = nx; normalY = ny; normalZ = nz; // if drawing a shape and the normal hasn't been set yet, // then we need to set the normals for each vertex so far if (shape != 0) { if (normalMode == NORMAL_MODE_AUTO) { // either they set the normals, or they don't [0149] // for (int i = vertex_start; i < vertexCount; i++) { // vertices[i][NX] = normalX; // vertices[i][NY] = normalY; // vertices[i][NZ] = normalZ; // } // One normal per begin/end shape normalMode = NORMAL_MODE_SHAPE; } else if (normalMode == NORMAL_MODE_SHAPE) { // a separate normal for each vertex normalMode = NORMAL_MODE_VERTEX; } } } /** * Set texture mode to either to use coordinates based on the IMAGE * (more intuitive for new users) or NORMALIZED (better for advanced chaps) */ public void textureMode(int mode) { this.textureMode = mode; } /** * Set texture image for current shape. * Needs to be called between @see beginShape and @see endShape * * @param image reference to a PImage object */ public void texture(PImage image) { textureImage = image; } protected void vertexCheck() { if (vertexCount == vertices.length) { float temp[][] = new float[vertexCount << 1][VERTEX_FIELD_COUNT]; System.arraycopy(vertices, 0, temp, 0, vertexCount); vertices = temp; } } public void vertex(float x, float y) { vertexCheck(); float[] vertex = vertices[vertexCount]; curveVertexCount = 0; vertex[X] = x; vertex[Y] = y; vertex[EDGE] = edge ? 1 : 0; // if (fill) { // vertex[R] = fillR; // vertex[G] = fillG; // vertex[B] = fillB; // vertex[A] = fillA; // } if (fill || textureImage != null) { if (textureImage == null) { vertex[R] = fillR; vertex[G] = fillG; vertex[B] = fillB; vertex[A] = fillA; } else { if (tint) { vertex[R] = tintR; vertex[G] = tintG; vertex[B] = tintB; vertex[A] = tintA; } else { vertex[R] = 1; vertex[G] = 1; vertex[B] = 1; vertex[A] = 1; } } } if (stroke) { vertex[SR] = strokeR; vertex[SG] = strokeG; vertex[SB] = strokeB; vertex[SA] = strokeA; vertex[SW] = strokeWeight; } if (textureImage != null) { vertex[U] = textureU; vertex[V] = textureV; } vertexCount++; } public void vertex(float x, float y, float z) { vertexCheck(); float[] vertex = vertices[vertexCount]; // only do this if we're using an irregular (POLYGON) shape that // will go through the triangulator. otherwise it'll do thinks like // disappear in mathematically odd ways // http://dev.processing.org/bugs/show_bug.cgi?id=444 if (shape == POLYGON) { if (vertexCount > 0) { float pvertex[] = vertices[vertexCount-1]; if ((Math.abs(pvertex[X] - x) < EPSILON) && (Math.abs(pvertex[Y] - y) < EPSILON) && (Math.abs(pvertex[Z] - z) < EPSILON)) { // this vertex is identical, don't add it, // because it will anger the triangulator return; } } } // User called vertex(), so that invalidates anything queued up for curve // vertices. If this is internally called by curveVertexSegment, // then curveVertexCount will be saved and restored. curveVertexCount = 0; vertex[X] = x; vertex[Y] = y; vertex[Z] = z; vertex[EDGE] = edge ? 1 : 0; if (fill || textureImage != null) { if (textureImage == null) { vertex[R] = fillR; vertex[G] = fillG; vertex[B] = fillB; vertex[A] = fillA; } else { if (tint) { vertex[R] = tintR; vertex[G] = tintG; vertex[B] = tintB; vertex[A] = tintA; } else { vertex[R] = 1; vertex[G] = 1; vertex[B] = 1; vertex[A] = 1; } } vertex[AR] = ambientR; vertex[AG] = ambientG; vertex[AB] = ambientB; vertex[SPR] = specularR; vertex[SPG] = specularG; vertex[SPB] = specularB; //vertex[SPA] = specularA; vertex[SHINE] = shininess; vertex[ER] = emissiveR; vertex[EG] = emissiveG; vertex[EB] = emissiveB; } if (stroke) { vertex[SR] = strokeR; vertex[SG] = strokeG; vertex[SB] = strokeB; vertex[SA] = strokeA; vertex[SW] = strokeWeight; } if (textureImage != null) { vertex[U] = textureU; vertex[V] = textureV; } vertex[NX] = normalX; vertex[NY] = normalY; vertex[NZ] = normalZ; vertex[BEEN_LIT] = 0; vertexCount++; } /** * Used by renderer subclasses or PShape to efficiently pass in already * formatted vertex information. * @param v vertex parameters, as a float array of length VERTEX_FIELD_COUNT */ public void vertex(float[] v) { vertexCheck(); curveVertexCount = 0; float[] vertex = vertices[vertexCount]; System.arraycopy(v, 0, vertex, 0, VERTEX_FIELD_COUNT); vertexCount++; } public void vertex(float x, float y, float u, float v) { vertexTexture(u, v); vertex(x, y); } public void vertex(float x, float y, float z, float u, float v) { vertexTexture(u, v); vertex(x, y, z); } /** * Internal method to copy all style information for the given vertex. * Can be overridden by subclasses to handle only properties pertinent to * that renderer. (e.g. no need to copy the emissive color in P2D) */ // protected void vertexStyle() { // } /** * Set (U, V) coords for the next vertex in the current shape. * This is ugly as its own function, and will (almost?) always be * coincident with a call to vertex. As of beta, this was moved to * the protected method you see here, and called from an optional * param of and overloaded vertex(). * * The parameters depend on the current textureMode. When using * textureMode(IMAGE), the coordinates will be relative to the size * of the image texture, when used with textureMode(NORMAL), * they'll be in the range 0..1. * * Used by both PGraphics2D (for images) and PGraphics3D. */ protected void vertexTexture(float u, float v) { if (textureImage == null) { throw new RuntimeException("You must first call texture() before " + "using u and v coordinates with vertex()"); } if (textureMode == IMAGE) { u /= (float) textureImage.width; v /= (float) textureImage.height; } textureU = u; textureV = v; if (textureU < 0) textureU = 0; else if (textureU > 1) textureU = 1; if (textureV < 0) textureV = 0; else if (textureV > 1) textureV = 1; } /** This feature is in testing, do not use or rely upon its implementation */ public void breakShape() { showWarning("This renderer cannot currently handle concave shapes, " + "or shapes with holes."); } public void endShape() { endShape(OPEN); } public void endShape(int mode) { } ////////////////////////////////////////////////////////////// // CURVE/BEZIER VERTEX HANDLING protected void bezierVertexCheck() { if (shape == 0 || shape != POLYGON) { throw new RuntimeException("beginShape() or beginShape(POLYGON) " + "must be used before bezierVertex()"); } if (vertexCount == 0) { throw new RuntimeException("vertex() must be used at least once" + "before bezierVertex()"); } } public void bezierVertex(float x2, float y2, float x3, float y3, float x4, float y4) { bezierInitCheck(); bezierVertexCheck(); PMatrix3D draw = bezierDrawMatrix; float[] prev = vertices[vertexCount-1]; float x1 = prev[X]; float y1 = prev[Y]; float xplot1 = draw.m10*x1 + draw.m11*x2 + draw.m12*x3 + draw.m13*x4; float xplot2 = draw.m20*x1 + draw.m21*x2 + draw.m22*x3 + draw.m23*x4; float xplot3 = draw.m30*x1 + draw.m31*x2 + draw.m32*x3 + draw.m33*x4; float yplot1 = draw.m10*y1 + draw.m11*y2 + draw.m12*y3 + draw.m13*y4; float yplot2 = draw.m20*y1 + draw.m21*y2 + draw.m22*y3 + draw.m23*y4; float yplot3 = draw.m30*y1 + draw.m31*y2 + draw.m32*y3 + draw.m33*y4; for (int j = 0; j < bezierDetail; j++) { x1 += xplot1; xplot1 += xplot2; xplot2 += xplot3; y1 += yplot1; yplot1 += yplot2; yplot2 += yplot3; vertex(x1, y1); } } public void bezierVertex(float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4) { bezierInitCheck(); bezierVertexCheck(); PMatrix3D draw = bezierDrawMatrix; float[] prev = vertices[vertexCount-1]; float x1 = prev[X]; float y1 = prev[Y]; float z1 = prev[Z]; float xplot1 = draw.m10*x1 + draw.m11*x2 + draw.m12*x3 + draw.m13*x4; float xplot2 = draw.m20*x1 + draw.m21*x2 + draw.m22*x3 + draw.m23*x4; float xplot3 = draw.m30*x1 + draw.m31*x2 + draw.m32*x3 + draw.m33*x4; float yplot1 = draw.m10*y1 + draw.m11*y2 + draw.m12*y3 + draw.m13*y4; float yplot2 = draw.m20*y1 + draw.m21*y2 + draw.m22*y3 + draw.m23*y4; float yplot3 = draw.m30*y1 + draw.m31*y2 + draw.m32*y3 + draw.m33*y4; float zplot1 = draw.m10*z1 + draw.m11*z2 + draw.m12*z3 + draw.m13*z4; float zplot2 = draw.m20*z1 + draw.m21*z2 + draw.m22*z3 + draw.m23*z4; float zplot3 = draw.m30*z1 + draw.m31*z2 + draw.m32*z3 + draw.m33*z4; for (int j = 0; j < bezierDetail; j++) { x1 += xplot1; xplot1 += xplot2; xplot2 += xplot3; y1 += yplot1; yplot1 += yplot2; yplot2 += yplot3; z1 += zplot1; zplot1 += zplot2; zplot2 += zplot3; vertex(x1, y1, z1); } } /** * Perform initialization specific to curveVertex(), and handle standard * error modes. Can be overridden by subclasses that need the flexibility. */ protected void curveVertexCheck() { if (shape != POLYGON) { throw new RuntimeException("You must use beginShape() or " + "beginShape(POLYGON) before curveVertex()"); } // to improve code init time, allocate on first use. if (curveVertices == null) { curveVertices = new float[128][3]; } if (curveVertexCount == curveVertices.length) { // Can't use PApplet.expand() cuz it doesn't do the copy properly float[][] temp = new float[curveVertexCount << 1][3]; System.arraycopy(curveVertices, 0, temp, 0, curveVertexCount); curveVertices = temp; } curveInitCheck(); } public void curveVertex(float x, float y) { curveVertexCheck(); float[] vertex = curveVertices[curveVertexCount]; vertex[X] = x; vertex[Y] = y; curveVertexCount++; // draw a segment if there are enough points if (curveVertexCount > 3) { curveVertexSegment(curveVertices[curveVertexCount-4][X], curveVertices[curveVertexCount-4][Y], curveVertices[curveVertexCount-3][X], curveVertices[curveVertexCount-3][Y], curveVertices[curveVertexCount-2][X], curveVertices[curveVertexCount-2][Y], curveVertices[curveVertexCount-1][X], curveVertices[curveVertexCount-1][Y]); } } public void curveVertex(float x, float y, float z) { curveVertexCheck(); float[] vertex = curveVertices[curveVertexCount]; vertex[X] = x; vertex[Y] = y; vertex[Z] = z; curveVertexCount++; // draw a segment if there are enough points if (curveVertexCount > 3) { curveVertexSegment(curveVertices[curveVertexCount-4][X], curveVertices[curveVertexCount-4][Y], curveVertices[curveVertexCount-4][Z], curveVertices[curveVertexCount-3][X], curveVertices[curveVertexCount-3][Y], curveVertices[curveVertexCount-3][Z], curveVertices[curveVertexCount-2][X], curveVertices[curveVertexCount-2][Y], curveVertices[curveVertexCount-2][Z], curveVertices[curveVertexCount-1][X], curveVertices[curveVertexCount-1][Y], curveVertices[curveVertexCount-1][Z]); } } /** * Handle emitting a specific segment of Catmull-Rom curve. This can be * overridden by subclasses that need more efficient rendering options. */ protected void curveVertexSegment(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { float x0 = x2; float y0 = y2; PMatrix3D draw = curveDrawMatrix; float xplot1 = draw.m10*x1 + draw.m11*x2 + draw.m12*x3 + draw.m13*x4; float xplot2 = draw.m20*x1 + draw.m21*x2 + draw.m22*x3 + draw.m23*x4; float xplot3 = draw.m30*x1 + draw.m31*x2 + draw.m32*x3 + draw.m33*x4; float yplot1 = draw.m10*y1 + draw.m11*y2 + draw.m12*y3 + draw.m13*y4; float yplot2 = draw.m20*y1 + draw.m21*y2 + draw.m22*y3 + draw.m23*y4; float yplot3 = draw.m30*y1 + draw.m31*y2 + draw.m32*y3 + draw.m33*y4; // vertex() will reset splineVertexCount, so save it int savedCount = curveVertexCount; vertex(x0, y0); for (int j = 0; j < curveDetail; j++) { x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3; y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3; vertex(x0, y0); } curveVertexCount = savedCount; } /** * Handle emitting a specific segment of Catmull-Rom curve. This can be * overridden by subclasses that need more efficient rendering options. */ protected void curveVertexSegment(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4) { float x0 = x2; float y0 = y2; float z0 = z2; PMatrix3D draw = curveDrawMatrix; float xplot1 = draw.m10*x1 + draw.m11*x2 + draw.m12*x3 + draw.m13*x4; float xplot2 = draw.m20*x1 + draw.m21*x2 + draw.m22*x3 + draw.m23*x4; float xplot3 = draw.m30*x1 + draw.m31*x2 + draw.m32*x3 + draw.m33*x4; float yplot1 = draw.m10*y1 + draw.m11*y2 + draw.m12*y3 + draw.m13*y4; float yplot2 = draw.m20*y1 + draw.m21*y2 + draw.m22*y3 + draw.m23*y4; float yplot3 = draw.m30*y1 + draw.m31*y2 + draw.m32*y3 + draw.m33*y4; // vertex() will reset splineVertexCount, so save it int savedCount = curveVertexCount; float zplot1 = draw.m10*z1 + draw.m11*z2 + draw.m12*z3 + draw.m13*z4; float zplot2 = draw.m20*z1 + draw.m21*z2 + draw.m22*z3 + draw.m23*z4; float zplot3 = draw.m30*z1 + draw.m31*z2 + draw.m32*z3 + draw.m33*z4; vertex(x0, y0, z0); for (int j = 0; j < curveDetail; j++) { x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3; y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3; z0 += zplot1; zplot1 += zplot2; zplot2 += zplot3; vertex(x0, y0, z0); } curveVertexCount = savedCount; } ////////////////////////////////////////////////////////////// // SIMPLE SHAPES WITH ANALOGUES IN beginShape() public void point(float x, float y) { beginShape(POINTS); vertex(x, y); endShape(); } /** * Draws a point, a coordinate in space at the dimension of one pixel. * The first parameter is the horizontal value for the point, the second * value is the vertical value for the point, and the optional third value * is the depth value. Drawing this shape in 3D using the z * parameter requires the P3D or OPENGL parameter in combination with * size as shown in the above example. ** Implementation notes: *
* cache all the points of the sphere in a static array * top and bottom are just a bunch of triangles that land * in the center point *
* sphere is a series of concentric circles who radii vary * along the shape, based on, er.. cos or something *
* [toxi 031031] new sphere code. removed all multiplies with * radius, as scale() will take care of that anyway * * [toxi 031223] updated sphere code (removed modulos) * and introduced sphereAt(x,y,z,r) * to avoid additional translate()'s on the user/sketch side * * [davbol 080801] now using separate sphereDetailU/V ** * @webref shape:3d_primitives * @param r the radius of the sphere */ public void sphere(float r) { if ((sphereDetailU < 3) || (sphereDetailV < 2)) { sphereDetail(30); } pushMatrix(); scale(r); edge(false); // 1st ring from south pole beginShape(TRIANGLE_STRIP); for (int i = 0; i < sphereDetailU; i++) { normal(0, -1, 0); vertex(0, -1, 0); normal(sphereX[i], sphereY[i], sphereZ[i]); vertex(sphereX[i], sphereY[i], sphereZ[i]); } //normal(0, -1, 0); vertex(0, -1, 0); normal(sphereX[0], sphereY[0], sphereZ[0]); vertex(sphereX[0], sphereY[0], sphereZ[0]); endShape(); int v1,v11,v2; // middle rings int voff = 0; for (int i = 2; i < sphereDetailV; i++) { v1 = v11 = voff; voff += sphereDetailU; v2 = voff; beginShape(TRIANGLE_STRIP); for (int j = 0; j < sphereDetailU; j++) { normal(sphereX[v1], sphereY[v1], sphereZ[v1]); vertex(sphereX[v1], sphereY[v1], sphereZ[v1++]); normal(sphereX[v2], sphereY[v2], sphereZ[v2]); vertex(sphereX[v2], sphereY[v2], sphereZ[v2++]); } // close each ring v1 = v11; v2 = voff; normal(sphereX[v1], sphereY[v1], sphereZ[v1]); vertex(sphereX[v1], sphereY[v1], sphereZ[v1]); normal(sphereX[v2], sphereY[v2], sphereZ[v2]); vertex(sphereX[v2], sphereY[v2], sphereZ[v2]); endShape(); } // add the northern cap beginShape(TRIANGLE_STRIP); for (int i = 0; i < sphereDetailU; i++) { v2 = voff + i; normal(sphereX[v2], sphereY[v2], sphereZ[v2]); vertex(sphereX[v2], sphereY[v2], sphereZ[v2]); normal(0, 1, 0); vertex(0, 1, 0); } normal(sphereX[voff], sphereY[voff], sphereZ[voff]); vertex(sphereX[voff], sphereY[voff], sphereZ[voff]); normal(0, 1, 0); vertex(0, 1, 0); endShape(); edge(true); popMatrix(); } ////////////////////////////////////////////////////////////// // BEZIER /** * Evaluates the Bezier at point t for points a, b, c, d. The parameter t varies between 0 and 1, a and d are points on the curve, and b and c are the control points. This can be done once with the x coordinates and a second time with the y coordinates to get the location of a bezier curve at t. */ /** * Evalutes quadratic bezier at point t for points a, b, c, d. * The parameter t varies between 0 and 1. The a and d parameters are the * on-curve points, b and c are the control points. To make a two-dimensional * curve, call this function once with the x coordinates and a second time * with the y coordinates to get the location of a bezier curve at t. * * =advanced * For instance, to convert the following example:
* stroke(255, 102, 0);
* line(85, 20, 10, 10);
* line(90, 90, 15, 80);
* stroke(0, 0, 0);
* bezier(85, 20, 10, 10, 90, 90, 15, 80);
*
* // draw it in gray, using 10 steps instead of the default 20
* // this is a slower way to do it, but useful if you need
* // to do things with the coordinates at each step
* stroke(128);
* beginShape(LINE_STRIP);
* for (int i = 0; i <= 10; i++) {
* float t = i / 10.0f;
* float x = bezierPoint(85, 10, 90, 15, t);
* float y = bezierPoint(20, 10, 90, 80, t);
* vertex(x, y);
* }
* endShape();
*
* @webref shape:curves
* @param a coordinate of first point on the curve
* @param b coordinate of first control point
* @param c coordinate of second control point
* @param d coordinate of second point on the curve
* @param t value between 0 and 1
*
* @see PGraphics#bezier(float, float, float, float, float, float, float, float, float, float, float, float)
* @see PGraphics#bezierVertex(float, float, float, float, float, float)
* @see PGraphics#curvePoint(float, float, float, float, float)
*/
public float bezierPoint(float a, float b, float c, float d, float t) {
float t1 = 1.0f - t;
return a*t1*t1*t1 + 3*b*t*t1*t1 + 3*c*t*t*t1 + d*t*t*t;
}
/**
* Calculates the tangent of a point on a Bezier curve. There is a good
* definition of "tangent" at Wikipedia: http://en.wikipedia.org/wiki/Tangent
*
* =advanced
* Code submitted by Dave Bollinger (davol) for release 0136.
*
* @webref shape:curves
* @param a coordinate of first point on the curve
* @param b coordinate of first control point
* @param c coordinate of second control point
* @param d coordinate of second point on the curve
* @param t value between 0 and 1
*
* @see PGraphics#bezier(float, float, float, float, float, float, float, float, float, float, float, float)
* @see PGraphics#bezierVertex(float, float, float, float, float, float)
* @see PGraphics#curvePoint(float, float, float, float, float)
*/
public float bezierTangent(float a, float b, float c, float d, float t) {
return (3*t*t * (-a+3*b-3*c+d) +
6*t * (a-2*b+c) +
3 * (-a+b));
}
protected void bezierInitCheck() {
if (!bezierInited) {
bezierInit();
}
}
protected void bezierInit() {
// overkill to be broken out, but better parity with the curve stuff below
bezierDetail(bezierDetail);
bezierInited = true;
}
/**
* Sets the resolution at which Beziers display. The default value is 20. This function is only useful when using the P3D or OPENGL renderer as the default (JAVA2D) renderer does not use this information.
*
* @webref shape:curves
* @param detail resolution of the curves
*
* @see PApplet#curve(float, float, float, float, float, float, float, float, float, float, float, float)
* @see PApplet#curveVertex(float, float)
* @see PApplet#curveTightness(float)
*/
public void bezierDetail(int detail) {
bezierDetail = detail;
if (bezierDrawMatrix == null) {
bezierDrawMatrix = new PMatrix3D();
}
// setup matrix for forward differencing to speed up drawing
splineForward(detail, bezierDrawMatrix);
// multiply the basis and forward diff matrices together
// saves much time since this needn't be done for each curve
//mult_spline_matrix(bezierForwardMatrix, bezier_basis, bezierDrawMatrix, 4);
//bezierDrawMatrix.set(bezierForwardMatrix);
bezierDrawMatrix.apply(bezierBasisMatrix);
}
/**
* Draws a Bezier curve on the screen. These curves are defined by a series
* of anchor and control points. The first two parameters specify the first
* anchor point and the last two parameters specify the other anchor point.
* The middle parameters specify the control points which define the shape
* of the curve. Bezier curves were developed by French engineer Pierre
* Bezier. Using the 3D version of requires rendering with P3D or OPENGL
* (see the Environment reference for more information).
*
* =advanced
* Draw a cubic bezier curve. The first and last points are
* the on-curve points. The middle two are the 'control' points,
* or 'handles' in an application like Illustrator.
* * Identical to typing: *
beginShape(); * vertex(x1, y1); * bezierVertex(x2, y2, x3, y3, x4, y4); * endShape(); ** In Postscript-speak, this would be: *
moveto(x1, y1); * curveto(x2, y2, x3, y3, x4, y4);* If you were to try and continue that curve like so: *
curveto(x5, y5, x6, y6, x7, y7);* This would be done in processing by adding these statements: *
bezierVertex(x5, y5, x6, y6, x7, y7) ** To draw a quadratic (instead of cubic) curve, * use the control point twice by doubling it: *
bezier(x1, y1, cx, cy, cx, cy, x2, y2);* * @webref shape:curves * @param x1 coordinates for the first anchor point * @param y1 coordinates for the first anchor point * @param z1 coordinates for the first anchor point * @param x2 coordinates for the first control point * @param y2 coordinates for the first control point * @param z2 coordinates for the first control point * @param x3 coordinates for the second control point * @param y3 coordinates for the second control point * @param z3 coordinates for the second control point * @param x4 coordinates for the second anchor point * @param y4 coordinates for the second anchor point * @param z4 coordinates for the second anchor point * * @see PGraphics#bezierVertex(float, float, float, float, float, float) * @see PGraphics#curve(float, float, float, float, float, float, float, float, float, float, float, float) */ public void bezier(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { beginShape(); vertex(x1, y1); bezierVertex(x2, y2, x3, y3, x4, y4); endShape(); } public void bezier(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4) { beginShape(); vertex(x1, y1, z1); bezierVertex(x2, y2, z2, x3, y3, z3, x4, y4, z4); endShape(); } ////////////////////////////////////////////////////////////// // CATMULL-ROM CURVE /** * Evalutes the Catmull-Rom curve at point t for points a, b, c, d. The * parameter t varies between 0 and 1, a and d are points on the curve, * and b and c are the control points. This can be done once with the x * coordinates and a second time with the y coordinates to get the * location of a curve at t. * * @webref shape:curves * @param a coordinate of first point on the curve * @param b coordinate of second point on the curve * @param c coordinate of third point on the curve * @param d coordinate of fourth point on the curve * @param t value between 0 and 1 * * @see PGraphics#curve(float, float, float, float, float, float, float, float, float, float, float, float) * @see PGraphics#curveVertex(float, float) * @see PGraphics#bezierPoint(float, float, float, float, float) */ public float curvePoint(float a, float b, float c, float d, float t) { curveInitCheck(); float tt = t * t; float ttt = t * tt; PMatrix3D cb = curveBasisMatrix; // not optimized (and probably need not be) return (a * (ttt*cb.m00 + tt*cb.m10 + t*cb.m20 + cb.m30) + b * (ttt*cb.m01 + tt*cb.m11 + t*cb.m21 + cb.m31) + c * (ttt*cb.m02 + tt*cb.m12 + t*cb.m22 + cb.m32) + d * (ttt*cb.m03 + tt*cb.m13 + t*cb.m23 + cb.m33)); } /** * Calculates the tangent of a point on a Catmull-Rom curve. There is a good definition of "tangent" at Wikipedia: http://en.wikipedia.org/wiki/Tangent. * * =advanced * Code thanks to Dave Bollinger (Bug #715) * * @webref shape:curves * @param a coordinate of first point on the curve * @param b coordinate of first control point * @param c coordinate of second control point * @param d coordinate of second point on the curve * @param t value between 0 and 1 * * @see PGraphics#curve(float, float, float, float, float, float, float, float, float, float, float, float) * @see PGraphics#curveVertex(float, float) * @see PGraphics#curvePoint(float, float, float, float, float) * @see PGraphics#bezierTangent(float, float, float, float, float) */ public float curveTangent(float a, float b, float c, float d, float t) { curveInitCheck(); float tt3 = t * t * 3; float t2 = t * 2; PMatrix3D cb = curveBasisMatrix; // not optimized (and probably need not be) return (a * (tt3*cb.m00 + t2*cb.m10 + cb.m20) + b * (tt3*cb.m01 + t2*cb.m11 + cb.m21) + c * (tt3*cb.m02 + t2*cb.m12 + cb.m22) + d * (tt3*cb.m03 + t2*cb.m13 + cb.m23) ); } /** * Sets the resolution at which curves display. The default value is 20. * This function is only useful when using the P3D or OPENGL renderer as * the default (JAVA2D) renderer does not use this information. * * @webref shape:curves * @param detail resolution of the curves * * @see PGraphics#curve(float, float, float, float, float, float, float, float, float, float, float, float) * @see PGraphics#curveVertex(float, float) * @see PGraphics#curveTightness(float) */ public void curveDetail(int detail) { curveDetail = detail; curveInit(); } /** * Modifies the quality of forms created with curve() and *curveVertex(). The parameter squishy determines how the * curve fits to the vertex points. The value 0.0 is the default value for * squishy (this value defines the curves to be Catmull-Rom splines) * and the value 1.0 connects all the points with straight lines. * Values within the range -5.0 and 5.0 will deform the curves but * will leave them recognizable and as values increase in magnitude, * they will continue to deform. * * @webref shape:curves * @param tightness amount of deformation from the original vertices * * @see PGraphics#curve(float, float, float, float, float, float, float, float, float, float, float, float) * @see PGraphics#curveVertex(float, float) * */ public void curveTightness(float tightness) { curveTightness = tightness; curveInit(); } protected void curveInitCheck() { if (!curveInited) { curveInit(); } } /** * Set the number of segments to use when drawing a Catmull-Rom * curve, and setting the s parameter, which defines how tightly * the curve fits to each vertex. Catmull-Rom curves are actually * a subset of this curve type where the s is set to zero. *
* (This function is not optimized, since it's not expected to * be called all that often. there are many juicy and obvious * opimizations in here, but it's probably better to keep the * code more readable) */ protected void curveInit() { // allocate only if/when used to save startup time if (curveDrawMatrix == null) { curveBasisMatrix = new PMatrix3D(); curveDrawMatrix = new PMatrix3D(); curveInited = true; } float s = curveTightness; curveBasisMatrix.set((s-1)/2f, (s+3)/2f, (-3-s)/2f, (1-s)/2f, (1-s), (-5-s)/2f, (s+2), (s-1)/2f, (s-1)/2f, 0, (1-s)/2f, 0, 0, 1, 0, 0); //setup_spline_forward(segments, curveForwardMatrix); splineForward(curveDetail, curveDrawMatrix); if (bezierBasisInverse == null) { bezierBasisInverse = bezierBasisMatrix.get(); bezierBasisInverse.invert(); curveToBezierMatrix = new PMatrix3D(); } // TODO only needed for PGraphicsJava2D? if so, move it there // actually, it's generally useful for other renderers, so keep it // or hide the implementation elsewhere. curveToBezierMatrix.set(curveBasisMatrix); curveToBezierMatrix.preApply(bezierBasisInverse); // multiply the basis and forward diff matrices together // saves much time since this needn't be done for each curve curveDrawMatrix.apply(curveBasisMatrix); } /** * Draws a curved line on the screen. The first and second parameters * specify the beginning control point and the last two parameters specify * the ending control point. The middle parameters specify the start and * stop of the curve. Longer curves can be created by putting a series of * curve() functions together or using curveVertex(). * An additional function called curveTightness() provides control * for the visual quality of the curve. The curve() function is an * implementation of Catmull-Rom splines. Using the 3D version of requires * rendering with P3D or OPENGL (see the Environment reference for more * information). * * =advanced * As of revision 0070, this function no longer doubles the first * and last points. The curves are a bit more boring, but it's more * mathematically correct, and properly mirrored in curvePoint(). *
* Identical to typing out:
* beginShape(); * curveVertex(x1, y1); * curveVertex(x2, y2); * curveVertex(x3, y3); * curveVertex(x4, y4); * endShape(); ** * @webref shape:curves * @param x1 coordinates for the beginning control point * @param y1 coordinates for the beginning control point * @param z1 coordinates for the beginning control point * @param x2 coordinates for the first point * @param y2 coordinates for the first point * @param z2 coordinates for the first point * @param x3 coordinates for the second point * @param y3 coordinates for the second point * @param z3 coordinates for the second point * @param x4 coordinates for the ending control point * @param y4 coordinates for the ending control point * @param z4 coordinates for the ending control point * * @see PGraphics#curveVertex(float, float) * @see PGraphics#curveTightness(float) * @see PGraphics#bezier(float, float, float, float, float, float, float, float, float, float, float, float) */ public void curve(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { beginShape(); curveVertex(x1, y1); curveVertex(x2, y2); curveVertex(x3, y3); curveVertex(x4, y4); endShape(); } public void curve(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4) { beginShape(); curveVertex(x1, y1, z1); curveVertex(x2, y2, z2); curveVertex(x3, y3, z3); curveVertex(x4, y4, z4); endShape(); } ////////////////////////////////////////////////////////////// // SPLINE UTILITY FUNCTIONS (used by both Bezier and Catmull-Rom) /** * Setup forward-differencing matrix to be used for speedy * curve rendering. It's based on using a specific number * of curve segments and just doing incremental adds for each * vertex of the segment, rather than running the mathematically * expensive cubic equation. * @param segments number of curve segments to use when drawing * @param matrix target object for the new matrix */ protected void splineForward(int segments, PMatrix3D matrix) { float f = 1.0f / segments; float ff = f * f; float fff = ff * f; matrix.set(0, 0, 0, 1, fff, ff, f, 0, 6*fff, 2*ff, 0, 0, 6*fff, 0, 0, 0); } ////////////////////////////////////////////////////////////// // SMOOTHING /** * If true in PImage, use bilinear interpolation for copy() * operations. When inherited by PGraphics, also controls shapes. */ public void smooth() { smooth = true; } /** * Disable smoothing. See smooth(). */ public void noSmooth() { smooth = false; } ////////////////////////////////////////////////////////////// // IMAGE /** * Modifies the location from which images draw. The default mode is * imageMode(CORNER), which specifies the location to be the * upper-left corner and uses the fourth and fifth parameters of * image() to set the image's width and height. The syntax * imageMode(CORNERS) uses the second and third parameters of * image() to set the location of one corner of the image and * uses the fourth and fifth parameters to set the opposite corner. * Use imageMode(CENTER) to draw images centered at the given * x and y position. *
* Given an (x, y, z) coordinate, returns the x position of where * that point would be placed on screen, once affected by translate(), * scale(), or any other transformations. */ public float screenX(float x, float y, float z) { showMissingWarning("screenX"); return 0; } /** * Maps a three dimensional point to its placement on-screen. *
* Given an (x, y, z) coordinate, returns the y position of where * that point would be placed on screen, once affected by translate(), * scale(), or any other transformations. */ public float screenY(float x, float y, float z) { showMissingWarning("screenY"); return 0; } /** * Maps a three dimensional point to its placement on-screen. *
* Given an (x, y, z) coordinate, returns its z value. * This value can be used to determine if an (x, y, z) coordinate * is in front or in back of another (x, y, z) coordinate. * The units are based on how the zbuffer is set up, and don't * relate to anything "real". They're only useful for in * comparison to another value obtained from screenZ(), * or directly out of the zbuffer[]. */ public float screenZ(float x, float y, float z) { showMissingWarning("screenZ"); return 0; } /** * Returns the model space x value for an x, y, z coordinate. *
* This will give you a coordinate after it has been transformed
* by translate(), rotate(), and camera(), but not yet transformed
* by the projection matrix. For instance, his can be useful for
* figuring out how points in 3D space relate to the edge
* coordinates of a shape.
*/
public float modelX(float x, float y, float z) {
showMissingWarning("modelX");
return 0;
}
/**
* Returns the model space y value for an x, y, z coordinate.
*/
public float modelY(float x, float y, float z) {
showMissingWarning("modelY");
return 0;
}
/**
* Returns the model space z value for an x, y, z coordinate.
*/
public float modelZ(float x, float y, float z) {
showMissingWarning("modelZ");
return 0;
}
//////////////////////////////////////////////////////////////
// STYLE
public void pushStyle() {
if (styleStackDepth == styleStack.length) {
styleStack = (PStyle[]) PApplet.expand(styleStack);
}
if (styleStack[styleStackDepth] == null) {
styleStack[styleStackDepth] = new PStyle();
}
PStyle s = styleStack[styleStackDepth++];
getStyle(s);
}
public void popStyle() {
if (styleStackDepth == 0) {
throw new RuntimeException("Too many popStyle() without enough pushStyle()");
}
styleStackDepth--;
style(styleStack[styleStackDepth]);
}
public void style(PStyle s) {
// if (s.smooth) {
// smooth();
// } else {
// noSmooth();
// }
imageMode(s.imageMode);
rectMode(s.rectMode);
ellipseMode(s.ellipseMode);
shapeMode(s.shapeMode);
if (s.tint) {
tint(s.tintColor);
} else {
noTint();
}
if (s.fill) {
fill(s.fillColor);
} else {
noFill();
}
if (s.stroke) {
stroke(s.strokeColor);
} else {
noStroke();
}
strokeWeight(s.strokeWeight);
strokeCap(s.strokeCap);
strokeJoin(s.strokeJoin);
// Set the colorMode() for the material properties.
// TODO this is really inefficient, need to just have a material() method,
// but this has the least impact to the API.
colorMode(RGB, 1);
ambient(s.ambientR, s.ambientG, s.ambientB);
emissive(s.emissiveR, s.emissiveG, s.emissiveB);
specular(s.specularR, s.specularG, s.specularB);
shininess(s.shininess);
/*
s.ambientR = ambientR;
s.ambientG = ambientG;
s.ambientB = ambientB;
s.specularR = specularR;
s.specularG = specularG;
s.specularB = specularB;
s.emissiveR = emissiveR;
s.emissiveG = emissiveG;
s.emissiveB = emissiveB;
s.shininess = shininess;
*/
// material(s.ambientR, s.ambientG, s.ambientB,
// s.emissiveR, s.emissiveG, s.emissiveB,
// s.specularR, s.specularG, s.specularB,
// s.shininess);
// Set this after the material properties.
colorMode(s.colorMode,
s.colorModeX, s.colorModeY, s.colorModeZ, s.colorModeA);
// This is a bit asymmetric, since there's no way to do "noFont()",
// and a null textFont will produce an error (since usually that means that
// the font couldn't load properly). So in some cases, the font won't be
// 'cleared' to null, even though that's technically correct.
if (s.textFont != null) {
textFont(s.textFont, s.textSize);
textLeading(s.textLeading);
}
// These don't require a font to be set.
textAlign(s.textAlign, s.textAlignY);
textMode(s.textMode);
}
public PStyle getStyle() { // ignore
return getStyle(null);
}
public PStyle getStyle(PStyle s) { // ignore
if (s == null) {
s = new PStyle();
}
s.imageMode = imageMode;
s.rectMode = rectMode;
s.ellipseMode = ellipseMode;
s.shapeMode = shapeMode;
s.colorMode = colorMode;
s.colorModeX = colorModeX;
s.colorModeY = colorModeY;
s.colorModeZ = colorModeZ;
s.colorModeA = colorModeA;
s.tint = tint;
s.tintColor = tintColor;
s.fill = fill;
s.fillColor = fillColor;
s.stroke = stroke;
s.strokeColor = strokeColor;
s.strokeWeight = strokeWeight;
s.strokeCap = strokeCap;
s.strokeJoin = strokeJoin;
s.ambientR = ambientR;
s.ambientG = ambientG;
s.ambientB = ambientB;
s.specularR = specularR;
s.specularG = specularG;
s.specularB = specularB;
s.emissiveR = emissiveR;
s.emissiveG = emissiveG;
s.emissiveB = emissiveB;
s.shininess = shininess;
s.textFont = textFont;
s.textAlign = textAlign;
s.textAlignY = textAlignY;
s.textMode = textMode;
s.textSize = textSize;
s.textLeading = textLeading;
return s;
}
//////////////////////////////////////////////////////////////
// STROKE CAP/JOIN/WEIGHT
public void strokeWeight(float weight) {
strokeWeight = weight;
}
public void strokeJoin(int join) {
strokeJoin = join;
}
public void strokeCap(int cap) {
strokeCap = cap;
}
//////////////////////////////////////////////////////////////
// STROKE COLOR
/**
* Disables drawing the stroke (outline). If both noStroke() and
* noFill() are called, no shapes will be drawn to the screen.
*
* @webref color:setting
*
* @see PGraphics#stroke(float, float, float, float)
*/
public void noStroke() {
stroke = false;
}
/**
* Set the tint to either a grayscale or ARGB value.
* See notes attached to the fill() function.
* @param rgb color value in hexadecimal notation
* (i.e. #FFCC00 or 0xFFFFCC00) or any value of the color datatype
*/
public void stroke(int rgb) {
colorCalc(rgb);
strokeFromCalc();
}
public void stroke(int rgb, float alpha) {
colorCalc(rgb, alpha);
strokeFromCalc();
}
/**
*
* @param gray specifies a value between white and black
*/
public void stroke(float gray) {
colorCalc(gray);
strokeFromCalc();
}
public void stroke(float gray, float alpha) {
colorCalc(gray, alpha);
strokeFromCalc();
}
public void stroke(float x, float y, float z) {
colorCalc(x, y, z);
strokeFromCalc();
}
/**
* Sets the color used to draw lines and borders around shapes. This color
* is either specified in terms of the RGB or HSB color depending on the
* current colorMode() (the default color space is RGB, with each
* value in the range from 0 to 255).
*
When using hexadecimal notation to specify a color, use "#" or
* "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA). The # syntax uses six
* digits to specify a color (the way colors are specified in HTML and CSS).
* When using the hexadecimal notation starting with "0x", the hexadecimal
* value must be specified with eight characters; the first two characters
* define the alpha component and the remainder the red, green, and blue
* components.
*
The value for the parameter "gray" must be less than or equal
* to the current maximum value as specified by colorMode().
* The default maximum value is 255.
*
* @webref color:setting
* @param alpha opacity of the stroke
* @param x red or hue value (depending on the current color mode)
* @param y green or saturation value (depending on the current color mode)
* @param z blue or brightness value (depending on the current color mode)
*/
public void stroke(float x, float y, float z, float a) {
colorCalc(x, y, z, a);
strokeFromCalc();
}
protected void strokeFromCalc() {
stroke = true;
strokeR = calcR;
strokeG = calcG;
strokeB = calcB;
strokeA = calcA;
strokeRi = calcRi;
strokeGi = calcGi;
strokeBi = calcBi;
strokeAi = calcAi;
strokeColor = calcColor;
strokeAlpha = calcAlpha;
}
//////////////////////////////////////////////////////////////
// TINT COLOR
/**
* Removes the current fill value for displaying images and reverts to displaying images with their original hues.
*
* @webref image:loading_displaying
* @see processing.core.PGraphics#tint(float, float, float, float)
* @see processing.core.PGraphics#image(PImage, float, float, float, float)
*/
public void noTint() {
tint = false;
}
/**
* Set the tint to either a grayscale or ARGB value.
*/
public void tint(int rgb) {
colorCalc(rgb);
tintFromCalc();
}
/**
* @param rgb color value in hexadecimal notation
* (i.e. #FFCC00 or 0xFFFFCC00) or any value of the color datatype
* @param alpha opacity of the image
*/
public void tint(int rgb, float alpha) {
colorCalc(rgb, alpha);
tintFromCalc();
}
/**
* @param gray any valid number
*/
public void tint(float gray) {
colorCalc(gray);
tintFromCalc();
}
public void tint(float gray, float alpha) {
colorCalc(gray, alpha);
tintFromCalc();
}
public void tint(float x, float y, float z) {
colorCalc(x, y, z);
tintFromCalc();
}
/**
* Sets the fill value for displaying images. Images can be tinted to
* specified colors or made transparent by setting the alpha.
*
To make an image transparent, but not change it's color,
* use white as the tint color and specify an alpha value. For instance,
* tint(255, 128) will make an image 50% transparent (unless
* colorMode() has been used).
*
*
When using hexadecimal notation to specify a color, use "#" or
* "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA). The # syntax uses six
* digits to specify a color (the way colors are specified in HTML and CSS).
* When using the hexadecimal notation starting with "0x", the hexadecimal
* value must be specified with eight characters; the first two characters
* define the alpha component and the remainder the red, green, and blue
* components.
*
The value for the parameter "gray" must be less than or equal
* to the current maximum value as specified by colorMode().
* The default maximum value is 255.
*
The tint() method is also used to control the coloring of
* textures in 3D.
*
* @webref image:loading_displaying
* @param x red or hue value
* @param y green or saturation value
* @param z blue or brightness value
*
* @see processing.core.PGraphics#noTint()
* @see processing.core.PGraphics#image(PImage, float, float, float, float)
*/
public void tint(float x, float y, float z, float a) {
colorCalc(x, y, z, a);
tintFromCalc();
}
protected void tintFromCalc() {
tint = true;
tintR = calcR;
tintG = calcG;
tintB = calcB;
tintA = calcA;
tintRi = calcRi;
tintGi = calcGi;
tintBi = calcBi;
tintAi = calcAi;
tintColor = calcColor;
tintAlpha = calcAlpha;
}
//////////////////////////////////////////////////////////////
// FILL COLOR
/**
* Disables filling geometry. If both noStroke() and noFill()
* are called, no shapes will be drawn to the screen.
*
* @webref color:setting
*
* @see PGraphics#fill(float, float, float, float)
*
*/
public void noFill() {
fill = false;
}
/**
* Set the fill to either a grayscale value or an ARGB int.
* @param rgb color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00) or any value of the color datatype
*/
public void fill(int rgb) {
colorCalc(rgb);
fillFromCalc();
}
public void fill(int rgb, float alpha) {
colorCalc(rgb, alpha);
fillFromCalc();
}
/**
* @param gray number specifying value between white and black
*/
public void fill(float gray) {
colorCalc(gray);
fillFromCalc();
}
public void fill(float gray, float alpha) {
colorCalc(gray, alpha);
fillFromCalc();
}
public void fill(float x, float y, float z) {
colorCalc(x, y, z);
fillFromCalc();
}
/**
* Sets the color used to fill shapes. For example, if you run fill(204, 102, 0), all subsequent shapes will be filled with orange. This color is either specified in terms of the RGB or HSB color depending on the current colorMode() (the default color space is RGB, with each value in the range from 0 to 255).
*
When using hexadecimal notation to specify a color, use "#" or "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA). The # syntax uses six digits to specify a color (the way colors are specified in HTML and CSS). When using the hexadecimal notation starting with "0x", the hexadecimal value must be specified with eight characters; the first two characters define the alpha component and the remainder the red, green, and blue components.
*
The value for the parameter "gray" must be less than or equal to the current maximum value as specified by colorMode(). The default maximum value is 255.
*
To change the color of an image (or a texture), use tint().
*
* @webref color:setting
* @param x red or hue value
* @param y green or saturation value
* @param z blue or brightness value
* @param alpha opacity of the fill
*
* @see PGraphics#noFill()
* @see PGraphics#stroke(float)
* @see PGraphics#tint(float)
* @see PGraphics#background(float, float, float, float)
* @see PGraphics#colorMode(int, float, float, float, float)
*/
public void fill(float x, float y, float z, float a) {
colorCalc(x, y, z, a);
fillFromCalc();
}
protected void fillFromCalc() {
fill = true;
fillR = calcR;
fillG = calcG;
fillB = calcB;
fillA = calcA;
fillRi = calcRi;
fillGi = calcGi;
fillBi = calcBi;
fillAi = calcAi;
fillColor = calcColor;
fillAlpha = calcAlpha;
}
//////////////////////////////////////////////////////////////
// MATERIAL PROPERTIES
public void ambient(int rgb) {
// if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
// ambient((float) rgb);
//
// } else {
// colorCalcARGB(rgb, colorModeA);
// ambientFromCalc();
// }
colorCalc(rgb);
ambientFromCalc();
}
public void ambient(float gray) {
colorCalc(gray);
ambientFromCalc();
}
public void ambient(float x, float y, float z) {
colorCalc(x, y, z);
ambientFromCalc();
}
protected void ambientFromCalc() {
ambientR = calcR;
ambientG = calcG;
ambientB = calcB;
}
public void specular(int rgb) {
// if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
// specular((float) rgb);
//
// } else {
// colorCalcARGB(rgb, colorModeA);
// specularFromCalc();
// }
colorCalc(rgb);
specularFromCalc();
}
public void specular(float gray) {
colorCalc(gray);
specularFromCalc();
}
public void specular(float x, float y, float z) {
colorCalc(x, y, z);
specularFromCalc();
}
protected void specularFromCalc() {
specularR = calcR;
specularG = calcG;
specularB = calcB;
}
public void shininess(float shine) {
shininess = shine;
}
public void emissive(int rgb) {
// if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
// emissive((float) rgb);
//
// } else {
// colorCalcARGB(rgb, colorModeA);
// emissiveFromCalc();
// }
colorCalc(rgb);
emissiveFromCalc();
}
public void emissive(float gray) {
colorCalc(gray);
emissiveFromCalc();
}
public void emissive(float x, float y, float z) {
colorCalc(x, y, z);
emissiveFromCalc();
}
protected void emissiveFromCalc() {
emissiveR = calcR;
emissiveG = calcG;
emissiveB = calcB;
}
//////////////////////////////////////////////////////////////
// LIGHTS
// The details of lighting are very implementation-specific, so this base
// class does not handle any details of settings lights. It does however
// display warning messages that the functions are not available.
public void lights() {
showMethodWarning("lights");
}
public void noLights() {
showMethodWarning("noLights");
}
public void ambientLight(float red, float green, float blue) {
showMethodWarning("ambientLight");
}
public void ambientLight(float red, float green, float blue,
float x, float y, float z) {
showMethodWarning("ambientLight");
}
public void directionalLight(float red, float green, float blue,
float nx, float ny, float nz) {
showMethodWarning("directionalLight");
}
public void pointLight(float red, float green, float blue,
float x, float y, float z) {
showMethodWarning("pointLight");
}
public void spotLight(float red, float green, float blue,
float x, float y, float z,
float nx, float ny, float nz,
float angle, float concentration) {
showMethodWarning("spotLight");
}
public void lightFalloff(float constant, float linear, float quadratic) {
showMethodWarning("lightFalloff");
}
public void lightSpecular(float x, float y, float z) {
showMethodWarning("lightSpecular");
}
//////////////////////////////////////////////////////////////
// BACKGROUND
/**
* Set the background to a gray or ARGB color.
*
* For the main drawing surface, the alpha value will be ignored. However, * alpha can be used on PGraphics objects from createGraphics(). This is * the only way to set all the pixels partially transparent, for instance. *
* Note that background() should be called before any transformations occur,
* because some implementations may require the current transformation matrix
* to be identity before drawing.
*
* @param rgb color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
or any value of the color datatype
*/
public void background(int rgb) {
// if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
// background((float) rgb);
//
// } else {
// if (format == RGB) {
// rgb |= 0xff000000; // ignore alpha for main drawing surface
// }
// colorCalcARGB(rgb, colorModeA);
// backgroundFromCalc();
// backgroundImpl();
// }
colorCalc(rgb);
backgroundFromCalc();
}
/**
* See notes about alpha in background(x, y, z, a).
*/
public void background(int rgb, float alpha) {
// if (format == RGB) {
// background(rgb); // ignore alpha for main drawing surface
//
// } else {
// if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
// background((float) rgb, alpha);
//
// } else {
// colorCalcARGB(rgb, alpha);
// backgroundFromCalc();
// backgroundImpl();
// }
// }
colorCalc(rgb, alpha);
backgroundFromCalc();
}
/**
* Set the background to a grayscale value, based on the
* current colorMode.
*/
public void background(float gray) {
colorCalc(gray);
backgroundFromCalc();
// backgroundImpl();
}
/**
* See notes about alpha in background(x, y, z, a).
* @param gray specifies a value between white and black
* @param alpha opacity of the background
*/
public void background(float gray, float alpha) {
if (format == RGB) {
background(gray); // ignore alpha for main drawing surface
} else {
colorCalc(gray, alpha);
backgroundFromCalc();
// backgroundImpl();
}
}
/**
* Set the background to an r, g, b or h, s, b value,
* based on the current colorMode.
*/
public void background(float x, float y, float z) {
colorCalc(x, y, z);
backgroundFromCalc();
// backgroundImpl();
}
/**
* The background() function sets the color used for the background of the Processing window. The default background is light gray. In the draw() function, the background color is used to clear the display window at the beginning of each frame.
*
An image can also be used as the background for a sketch, however its width and height must be the same size as the sketch window. To resize an image 'b' to the size of the sketch window, use b.resize(width, height).
*
Images used as background will ignore the current tint() setting.
*
It is not possible to use transparency (alpha) in background colors with the main drawing surface, however they will work properly with createGraphics.
*
* =advanced
*
Clear the background with a color that includes an alpha value. This can * only be used with objects created by createGraphics(), because the main * drawing surface cannot be set transparent.
*It might be tempting to use this function to partially clear the screen * on each frame, however that's not how this function works. When calling * background(), the pixels will be replaced with pixels that have that level * of transparency. To do a semi-transparent overlay, use fill() with alpha * and draw a rectangle.
* * @webref color:setting * @param x red or hue value (depending on the current color mode) * @param y green or saturation value (depending on the current color mode) * @param z blue or brightness value (depending on the current color mode) * * @see PGraphics#stroke(float) * @see PGraphics#fill(float) * @see PGraphics#tint(float) * @see PGraphics#colorMode(int) */ public void background(float x, float y, float z, float a) { // if (format == RGB) { // background(x, y, z); // don't allow people to set alpha // // } else { // colorCalc(x, y, z, a); // backgroundFromCalc(); // backgroundImpl(); // } colorCalc(x, y, z, a); backgroundFromCalc(); } protected void backgroundFromCalc() { backgroundR = calcR; backgroundG = calcG; backgroundB = calcB; backgroundA = (format == RGB) ? colorModeA : calcA; backgroundRi = calcRi; backgroundGi = calcGi; backgroundBi = calcBi; backgroundAi = (format == RGB) ? 255 : calcAi; backgroundAlpha = (format == RGB) ? false : calcAlpha; backgroundColor = calcColor; backgroundImpl(); } /** * Takes an RGB or ARGB image and sets it as the background. * The width and height of the image must be the same size as the sketch. * Use image.resize(width, height) to make short work of such a task. ** Note that even if the image is set as RGB, the high 8 bits of each pixel * should be set opaque (0xFF000000), because the image data will be copied * directly to the screen, and non-opaque background images may have strange * behavior. Using image.filter(OPAQUE) will handle this easily. *
* When using 3D, this will also clear the zbuffer (if it exists). */ public void background(PImage image) { if ((image.width != width) || (image.height != height)) { throw new RuntimeException(ERROR_BACKGROUND_IMAGE_SIZE); } if ((image.format != RGB) && (image.format != ARGB)) { throw new RuntimeException(ERROR_BACKGROUND_IMAGE_FORMAT); } backgroundColor = 0; // just zero it out for images backgroundImpl(image); } /** * Actually set the background image. This is separated from the error * handling and other semantic goofiness that is shared across renderers. */ protected void backgroundImpl(PImage image) { // blit image to the screen set(0, 0, image); } /** * Actual implementation of clearing the background, now that the * internal variables for background color have been set. Called by the * backgroundFromCalc() method, which is what all the other background() * methods call once the work is done. */ protected void backgroundImpl() { pushStyle(); pushMatrix(); resetMatrix(); fill(backgroundColor); rect(0, 0, width, height); popMatrix(); popStyle(); } /** * Callback to handle clearing the background when begin/endRaw is in use. * Handled as separate function for OpenGL (or other) subclasses that * override backgroundImpl() but still needs this to work properly. */ // protected void backgroundRawImpl() { // if (raw != null) { // raw.colorMode(RGB, 1); // raw.noStroke(); // raw.fill(backgroundR, backgroundG, backgroundB); // raw.beginShape(TRIANGLES); // // raw.vertex(0, 0); // raw.vertex(width, 0); // raw.vertex(0, height); // // raw.vertex(width, 0); // raw.vertex(width, height); // raw.vertex(0, height); // // raw.endShape(); // } // } ////////////////////////////////////////////////////////////// // COLOR MODE /** * @param mode Either RGB or HSB, corresponding to Red/Green/Blue and Hue/Saturation/Brightness * @param max range for all color elements */ public void colorMode(int mode) { colorMode(mode, colorModeX, colorModeY, colorModeZ, colorModeA); } public void colorMode(int mode, float max) { colorMode(mode, max, max, max, max); } /** * Set the colorMode and the maximum values for (r, g, b) * or (h, s, b). *
* Note that this doesn't set the maximum for the alpha value, * which might be confusing if for instance you switched to *
colorMode(HSB, 360, 100, 100);* because the alpha values were still between 0 and 255. */ public void colorMode(int mode, float maxX, float maxY, float maxZ) { colorMode(mode, maxX, maxY, maxZ, colorModeA); } /** * Changes the way Processing interprets color data. By default, the parameters for fill(), stroke(), background(), and color() are defined by values between 0 and 255 using the RGB color model. The colorMode() function is used to change the numerical range used for specifying colors and to switch color systems. For example, calling colorMode(RGB, 1.0) will specify that values are specified between 0 and 1. The limits for defining colors are altered by setting the parameters range1, range2, range3, and range 4. * * @webref color:setting * @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 * @param maxA range for the alpha * * @see PGraphics#background(float) * @see PGraphics#fill(float) * @see PGraphics#stroke(float) */ 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); } ////////////////////////////////////////////////////////////// // COLOR CALCULATIONS // Given input values for coloring, these functions will fill the calcXxxx // variables with values that have been properly filtered through the // current colorMode settings. // Renderers that need to subclass any drawing properties such as fill or // stroke will usally want to override methods like fillFromCalc (or the // same for stroke, ambient, etc.) That way the color calcuations are // covered by this based PGraphics class, leaving only a single function // to override/implement in the subclass. /** * Set the fill to either a grayscale value or an ARGB int. *
* The problem with this code is that it has to detect between these two * situations automatically. This is done by checking to see if the high bits * (the alpha for 0xAA000000) is set, and if not, whether the color value * that follows is less than colorModeX (first param passed to colorMode). *
* This auto-detect would break in the following situation: *
size(256, 256);
* for (int i = 0; i < 256; i++) {
* color c = color(0, 0, 0, i);
* stroke(c);
* line(i, 0, i, 256);
* }
* ...on the first time through the loop, where (i == 0), since the color
* itself is zero (black) then it would appear indistinguishable from code
* that reads "fill(0)". The solution is to use the four parameter versions
* of stroke or fill to more directly specify the desired result.
*/
protected void colorCalc(int rgb) {
if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
colorCalc((float) rgb);
} else {
colorCalcARGB(rgb, colorModeA);
}
}
protected void colorCalc(int rgb, float alpha) {
if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
colorCalc((float) rgb, alpha);
} else {
colorCalcARGB(rgb, alpha);
}
}
protected void colorCalc(float gray) {
colorCalc(gray, colorModeA);
}
protected void colorCalc(float gray, float alpha) {
if (gray > colorModeX) gray = colorModeX;
if (alpha > colorModeA) alpha = colorModeA;
if (gray < 0) gray = 0;
if (alpha < 0) alpha = 0;
calcR = 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);
}
protected void colorCalc(float x, float y, float z) {
colorCalc(x, y, z, colorModeA);
}
protected void colorCalc(float x, float y, float z, float a) {
if (x > colorModeX) x = colorModeX;
if (y > colorModeY) y = colorModeY;
if (z > colorModeZ) z = colorModeZ;
if (a > colorModeA) a = colorModeA;
if (x < 0) x = 0;
if (y < 0) y = 0;
if (z < 0) z = 0;
if (a < 0) a = 0;
switch (colorMode) {
case RGB:
if (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);
}
/**
* Unpacks AARRGGBB color for direct use with colorCalc.
* * Handled here with its own function since this is indepenent * of the color mode. *
* Strangely the old version of this code ignored the alpha * value. not sure if that was a bug or what. *
* Note, no need for a bounds check since it's a 32 bit number.
*/
protected void colorCalcARGB(int argb, float alpha) {
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 = (float)calcAi / 255.0f;
calcR = (float)calcRi / 255.0f;
calcG = (float)calcGi / 255.0f;
calcB = (float)calcBi / 255.0f;
calcAlpha = (calcAi != 255);
}
//////////////////////////////////////////////////////////////
// COLOR DATATYPE STUFFING
// The 'color' primitive type in Processing syntax is in fact a 32-bit int.
// These functions handle stuffing color values into a 32-bit cage based
// on the current colorMode settings.
// These functions are really slow (because they take the current colorMode
// into account), but they're easy to use. Advanced users can write their
// own bit shifting operations to setup 'color' data types.
public final int color(int gray) { // ignore
if (((gray & 0xff000000) == 0) && (gray <= colorModeX)) {
if (colorModeDefault) {
// bounds checking to make sure the numbers aren't to high or low
if (gray > 255) gray = 255; else if (gray < 0) gray = 0;
return 0xff000000 | (gray << 16) | (gray << 8) | gray;
} else {
colorCalc(gray);
}
} else {
colorCalcARGB(gray, colorModeA);
}
return calcColor;
}
public final int color(float gray) { // ignore
colorCalc(gray);
return calcColor;
}
/**
* @param gray can be packed ARGB or a gray in this case
*/
public final int color(int gray, int alpha) { // ignore
if (colorModeDefault) {
// bounds checking to make sure the numbers aren't to high or low
if (gray > 255) gray = 255; else if (gray < 0) gray = 0;
if (alpha > 255) alpha = 255; else if (alpha < 0) alpha = 0;
return ((alpha & 0xff) << 24) | (gray << 16) | (gray << 8) | gray;
}
colorCalc(gray, alpha);
return calcColor;
}
/**
* @param rgb can be packed ARGB or a gray in this case
*/
public final int color(int rgb, float alpha) { // ignore
if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
colorCalc(rgb, alpha);
} else {
colorCalcARGB(rgb, alpha);
}
return calcColor;
}
public final int color(float gray, float alpha) { // ignore
colorCalc(gray, alpha);
return calcColor;
}
public final int color(int x, int y, int z) { // ignore
if (colorModeDefault) {
// bounds checking to make sure the numbers aren't to high or low
if (x > 255) x = 255; else if (x < 0) x = 0;
if (y > 255) y = 255; else if (y < 0) y = 0;
if (z > 255) z = 255; else if (z < 0) z = 0;
return 0xff000000 | (x << 16) | (y << 8) | z;
}
colorCalc(x, y, z);
return calcColor;
}
public final int color(float x, float y, float z) { // ignore
colorCalc(x, y, z);
return calcColor;
}
public final int color(int x, int y, int z, int a) { // ignore
if (colorModeDefault) {
// bounds checking to make sure the numbers aren't to high or low
if (a > 255) a = 255; else if (a < 0) a = 0;
if (x > 255) x = 255; else if (x < 0) x = 0;
if (y > 255) y = 255; else if (y < 0) y = 0;
if (z > 255) z = 255; else if (z < 0) z = 0;
return (a << 24) | (x << 16) | (y << 8) | z;
}
colorCalc(x, y, z, a);
return calcColor;
}
public final int color(float x, float y, float z, float a) { // ignore
colorCalc(x, y, z, a);
return calcColor;
}
//////////////////////////////////////////////////////////////
// COLOR DATATYPE EXTRACTION
// Vee have veys of making the colors talk.
/**
* Extracts the alpha value from a color.
*
* @webref color:creating_reading
* @param what any value of the color datatype
*/
public final float alpha(int what) {
float c = (what >> 24) & 0xff;
if (colorModeA == 255) return c;
return (c / 255.0f) * colorModeA;
}
/**
* Extracts the red value from a color, scaled to match current colorMode(). This value is always returned as a float so be careful not to assign it to an int value.
The red() function is easy to use and undestand, but is slower than another technique. To achieve the same results when working in colorMode(RGB, 255), but with greater speed, use the >> (right shift) operator with a bit mask. For example, the following two lines of code are equivalent:
float r1 = red(myColor);* * @webref color:creating_reading * @param what any value of the color datatype * * @see PGraphics#green(int) * @see PGraphics#blue(int) * @see PGraphics#hue(int) * @see PGraphics#saturation(int) * @see PGraphics#brightness(int) * @ref rightshift */ public final float red(int what) { float c = (what >> 16) & 0xff; if (colorModeDefault) return c; return (c / 255.0f) * colorModeX; } /** * Extracts the green value from a color, scaled to match current colorMode(). This value is always returned as a float so be careful not to assign it to an int value.
float r2 = myColor >> 16 & 0xFF;
float r1 = green(myColor);* * @webref color:creating_reading * @param what any value of the color datatype * * @see PGraphics#red(int) * @see PGraphics#blue(int) * @see PGraphics#hue(int) * @see PGraphics#saturation(int) * @see PGraphics#brightness(int) * @ref rightshift */ public final float green(int what) { float c = (what >> 8) & 0xff; if (colorModeDefault) return c; return (c / 255.0f) * colorModeY; } /** * Extracts the blue value from a color, scaled to match current colorMode(). This value is always returned as a float so be careful not to assign it to an int value.
float r2 = myColor >> 8 & 0xFF;
float r1 = blue(myColor);* * @webref color:creating_reading * @param what any value of the color datatype * * @see PGraphics#red(int) * @see PGraphics#green(int) * @see PGraphics#hue(int) * @see PGraphics#saturation(int) * @see PGraphics#brightness(int) */ public final float blue(int what) { float c = (what) & 0xff; if (colorModeDefault) return c; return (c / 255.0f) * colorModeZ; } /** * Extracts the hue value from a color. * * @webref color:creating_reading * @param what any value of the color datatype * * @see PGraphics#red(int) * @see PGraphics#green(int) * @see PGraphics#blue(int) * @see PGraphics#saturation(int) * @see PGraphics#brightness(int) */ public final float hue(int what) { if (what != cacheHsbKey) { Color.RGBtoHSB((what >> 16) & 0xff, (what >> 8) & 0xff, what & 0xff, cacheHsbValue); cacheHsbKey = what; } return cacheHsbValue[0] * colorModeX; } /** * Extracts the saturation value from a color. * * @webref color:creating_reading * @param what any value of the color datatype * * @see PGraphics#red(int) * @see PGraphics#green(int) * @see PGraphics#blue(int) * @see PGraphics#hue(int) * @see PGraphics#brightness(int) */ public final float saturation(int what) { if (what != cacheHsbKey) { Color.RGBtoHSB((what >> 16) & 0xff, (what >> 8) & 0xff, what & 0xff, cacheHsbValue); cacheHsbKey = what; } return cacheHsbValue[1] * colorModeY; } /** * Extracts the brightness value from a color. * * * @webref color:creating_reading * @param what any value of the color datatype * * @see PGraphics#red(int) * @see PGraphics#green(int) * @see PGraphics#blue(int) * @see PGraphics#hue(int) * @see PGraphics#saturation(int) */ public final float brightness(int what) { if (what != cacheHsbKey) { Color.RGBtoHSB((what >> 16) & 0xff, (what >> 8) & 0xff, what & 0xff, cacheHsbValue); cacheHsbKey = what; } return cacheHsbValue[2] * colorModeZ; } ////////////////////////////////////////////////////////////// // COLOR DATATYPE INTERPOLATION // Against our better judgement. /** * Calculates a color or colors between two color at a specific increment. The amt parameter is the amount to interpolate between the two values where 0.0 equal to the first point, 0.1 is very near the first point, 0.5 is half-way in between, etc. * * @webref color:creating_reading * @param c1 interpolate from this color * @param c2 interpolate to this color * @param amt between 0.0 and 1.0 * * @see PGraphics#blendColor(int, int, int) * @see PGraphics#color(float, float, float, float) */ public int lerpColor(int c1, int c2, float amt) { return lerpColor(c1, c2, amt, colorMode); } static float[] lerpColorHSB1; static float[] lerpColorHSB2; /** * Interpolate between two colors. Like lerp(), but for the * individual color components of a color supplied as an int value. */ static public int lerpColor(int c1, int c2, float amt, int mode) { if (mode == RGB) { float a1 = ((c1 >> 24) & 0xff); float r1 = (c1 >> 16) & 0xff; float g1 = (c1 >> 8) & 0xff; float b1 = c1 & 0xff; float a2 = (c2 >> 24) & 0xff; float r2 = (c2 >> 16) & 0xff; float g2 = (c2 >> 8) & 0xff; float b2 = c2 & 0xff; return (((int) (a1 + (a2-a1)*amt) << 24) | ((int) (r1 + (r2-r1)*amt) << 16) | ((int) (g1 + (g2-g1)*amt) << 8) | ((int) (b1 + (b2-b1)*amt))); } else if (mode == HSB) { if (lerpColorHSB1 == null) { lerpColorHSB1 = new float[3]; lerpColorHSB2 = new float[3]; } float a1 = (c1 >> 24) & 0xff; float a2 = (c2 >> 24) & 0xff; int alfa = ((int) (a1 + (a2-a1)*amt)) << 24; Color.RGBtoHSB((c1 >> 16) & 0xff, (c1 >> 8) & 0xff, c1 & 0xff, lerpColorHSB1); Color.RGBtoHSB((c2 >> 16) & 0xff, (c2 >> 8) & 0xff, c2 & 0xff, lerpColorHSB2); /* If mode is HSB, this will take the shortest path around the * color wheel to find the new color. For instance, red to blue * will go red violet blue (backwards in hue space) rather than * cycling through ROYGBIV. */ // Disabling rollover (wasn't working anyway) for 0126. // Otherwise it makes full spectrum scale impossible for // those who might want it...in spite of how despicable // a full spectrum scale might be. // roll around when 0.9 to 0.1 // more than 0.5 away means that it should roll in the other direction /* float h1 = lerpColorHSB1[0]; float h2 = lerpColorHSB2[0]; if (Math.abs(h1 - h2) > 0.5f) { if (h1 > h2) { // i.e. h1 is 0.7, h2 is 0.1 h2 += 1; } else { // i.e. h1 is 0.1, h2 is 0.7 h1 += 1; } } float ho = (PApplet.lerp(lerpColorHSB1[0], lerpColorHSB2[0], amt)) % 1.0f; */ float ho = PApplet.lerp(lerpColorHSB1[0], lerpColorHSB2[0], amt); float so = PApplet.lerp(lerpColorHSB1[1], lerpColorHSB2[1], amt); float bo = PApplet.lerp(lerpColorHSB1[2], lerpColorHSB2[2], amt); return alfa | (Color.HSBtoRGB(ho, so, bo) & 0xFFFFFF); } return 0; } ////////////////////////////////////////////////////////////// // BEGINRAW/ENDRAW /** * Record individual lines and triangles by echoing them to another renderer. */ public void beginRaw(PGraphics rawGraphics) { // ignore this.raw = rawGraphics; rawGraphics.beginDraw(); } public void endRaw() { // ignore if (raw != null) { // for 3D, need to flush any geometry that's been stored for sorting // (particularly if the ENABLE_DEPTH_SORT hint is set) flush(); // just like beginDraw, this will have to be called because // endDraw() will be happening outside of draw() raw.endDraw(); raw.dispose(); raw = null; } } ////////////////////////////////////////////////////////////// // WARNINGS and EXCEPTIONS static protected HashMap
float r2 = myColor & 0xFF;