mirror of
https://github.com/processing/processing4.git
synced 2026-02-02 13:21:07 +01:00
3562 lines
92 KiB
Java
3562 lines
92 KiB
Java
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
|
|
|
/*
|
|
PGraphics - main graphics and rendering context
|
|
Part of the Processing project - http://processing.org
|
|
|
|
Copyright (c) 2004- Ben Fry and Casey Reas
|
|
Copyright (c) 2001-04 Massachusetts Institute of Technology
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General
|
|
Public License along with this library; if not, write to the
|
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
|
Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
package processing.core;
|
|
|
|
import java.applet.*;
|
|
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import java.awt.image.*;
|
|
import java.io.*;
|
|
|
|
|
|
public class PGraphics extends PImage implements PConstants {
|
|
|
|
/// width minus one (useful for many calculations)
|
|
public int width1;
|
|
|
|
/// height minus one (useful for many calculations)
|
|
public int height1;
|
|
|
|
/// width * height (useful for many calculations)
|
|
public int pixelCount;
|
|
|
|
// ........................................................
|
|
|
|
// specifics for java memoryimagesource
|
|
DirectColorModel cm;
|
|
MemoryImageSource mis;
|
|
public Image image;
|
|
|
|
// ........................................................
|
|
|
|
// needs to happen before background() is called
|
|
// and resize.. so it's gotta be outside
|
|
protected boolean hints[] = new boolean[HINT_COUNT];
|
|
|
|
// ........................................................
|
|
|
|
// underscored_names are used for private functions or variables
|
|
|
|
/** The current colorMode */
|
|
public int colorMode;
|
|
|
|
/** Max value for red (or hue) set by colorMode */
|
|
public float colorModeX;
|
|
|
|
/** Max value for green (or saturation) set by colorMode */
|
|
public float colorModeY;
|
|
|
|
/** Max value for blue (or value) set by colorMode */
|
|
public float colorModeZ;
|
|
|
|
/** Max value for alpha set by colorMode */
|
|
public float colorModeA;
|
|
|
|
/** True if colors are not in the range 0..1 */
|
|
boolean colorScale;
|
|
|
|
/** True if colorMode(RGB, 255) */
|
|
boolean colorRgb255;
|
|
|
|
/** True if tint() is enabled, read-only */
|
|
public boolean tint;
|
|
|
|
/** Tint that was last set, read-only */
|
|
public int tintColor;
|
|
|
|
/** True if the tint has an alpha value */
|
|
boolean tintAlpha;
|
|
|
|
public float tintR, tintG, tintB, tintA;
|
|
public int tintRi, tintGi, tintBi, tintAi;
|
|
|
|
// fill color
|
|
public boolean fill;
|
|
public int fillColor;
|
|
public boolean fillAlpha;
|
|
public float fillR, fillG, fillB, fillA;
|
|
public int fillRi, fillGi, fillBi, fillAi;
|
|
|
|
// stroke color
|
|
public boolean stroke;
|
|
boolean strokeAlpha;
|
|
public float strokeR, strokeG, strokeB, strokeA;
|
|
public int strokeRi, strokeGi, strokeBi, strokeAi;
|
|
public int strokeColor;
|
|
|
|
//public boolean background;
|
|
/** Last background color that was set */
|
|
public int backgroundColor;
|
|
public float backgroundR, backgroundG, backgroundB;
|
|
public int backgroundRi, backgroundGi, backgroundBi;
|
|
|
|
// internal color for setting/calculating
|
|
float calcR, calcG, calcB, calcA;
|
|
int calcRi, calcGi, calcBi, calcAi;
|
|
int calcColor;
|
|
boolean calcAlpha;
|
|
|
|
/** The last rgb value converted to HSB */
|
|
int cacheHsbKey;
|
|
/** Result of the last conversion to HSB */
|
|
float cacheHsbValue[] = new float[3]; // inits to zero
|
|
|
|
/** True if depth() is enabled, read-only */
|
|
//public boolean depth;
|
|
|
|
/**
|
|
* Internal values for enabling/disabling 2D or 0D optimizations.
|
|
* These are normally turned on, but will be shut off for OpenGL.
|
|
* Also, users may want to disable them if they're causing trouble.
|
|
*/
|
|
//public boolean optimize0 = true;
|
|
//public boolean optimize2 = true;
|
|
|
|
/** Set by strokeWeight(), read-only */
|
|
public float strokeWeight;
|
|
|
|
/** Set by strokeJoin(), read-only */
|
|
public int strokeJoin;
|
|
|
|
/** Set by strokeCap(), read-only */
|
|
public int strokeCap;
|
|
|
|
// ........................................................
|
|
|
|
/**
|
|
* Model transformation of the form m[row][column],
|
|
* which is a "column vector" (as opposed to "row vector") matrix.
|
|
*/
|
|
public float m00, m01, m02;
|
|
public float m10, m11, m12;
|
|
|
|
public int angleMode;
|
|
|
|
static final int MATRIX_STACK_DEPTH = 32;
|
|
float matrixStack[][] = new float[MATRIX_STACK_DEPTH][16];
|
|
int matrixStackDepth;
|
|
|
|
// ........................................................
|
|
|
|
/**
|
|
* Type of shape passed to beginShape(),
|
|
* zero if no shape is currently being drawn.
|
|
*/
|
|
protected int shape;
|
|
//int shape_index;
|
|
|
|
// vertices
|
|
static final int DEFAULT_VERTICES = 512;
|
|
public float vertices[][] = new float[DEFAULT_VERTICES][VERTEX_FIELD_COUNT];
|
|
int vertexCount; // total number of vertices
|
|
|
|
|
|
// ........................................................
|
|
|
|
// spline vertices
|
|
|
|
static final int DEFAULT_SPLINE_VERTICES = 128;
|
|
protected float splineVertices[][];
|
|
protected int splineVertexCount;
|
|
//boolean spline_vertices_flat;
|
|
|
|
|
|
// ........................................................
|
|
|
|
// precalculate sin/cos lookup tables [toxi]
|
|
// circle resolution is determined from the actual used radii
|
|
// passed to ellipse() method. this will automatically take any
|
|
// scale transformations into account too
|
|
|
|
// [toxi 031031]
|
|
// changed table's precision to 0.5 degree steps
|
|
// introduced new vars for more flexible code
|
|
static final float sinLUT[];
|
|
static final float cosLUT[];
|
|
static final float SINCOS_PRECISION = 0.5f;
|
|
static final int SINCOS_LENGTH = (int) (360f / SINCOS_PRECISION);
|
|
static {
|
|
sinLUT = new float[SINCOS_LENGTH];
|
|
cosLUT = new float[SINCOS_LENGTH];
|
|
for (int i = 0; i < SINCOS_LENGTH; i++) {
|
|
sinLUT[i] = (float) Math.sin(i * DEG_TO_RAD * SINCOS_PRECISION);
|
|
cosLUT[i] = (float) Math.cos(i * DEG_TO_RAD * SINCOS_PRECISION);
|
|
}
|
|
}
|
|
|
|
|
|
// ........................................................
|
|
|
|
|
|
public int rectMode;
|
|
public int ellipseMode;
|
|
public int arcMode;
|
|
|
|
//int text_mode;
|
|
//int text_space;
|
|
public PFont textFont;
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// INTERNAL
|
|
|
|
|
|
/**
|
|
* Constructor for the PGraphics object.
|
|
* This prototype only exists because of annoying
|
|
* java compilers, and should not be used.
|
|
*/
|
|
public PGraphics() { }
|
|
|
|
|
|
/**
|
|
* Constructor for the PGraphics object. Use this to ensure that
|
|
* the defaults get set properly. In a subclass, use this(w, h)
|
|
* as the first line of a subclass' constructor to properly set
|
|
* the internal fields and defaults.
|
|
*
|
|
* @param iwidth viewport width
|
|
* @param iheight viewport height
|
|
*/
|
|
public PGraphics(int iwidth, int iheight) {
|
|
resize(iwidth, iheight);
|
|
|
|
// init color/stroke/fill
|
|
//defaults();
|
|
|
|
// clear geometry for loading later
|
|
//circleX = null; // so that bagel knows to init these
|
|
//sphereX = null; // diff from cpp b/c mem in cpp is preallocated
|
|
}
|
|
|
|
|
|
/**
|
|
* Called in repsonse to a resize event, handles setting the
|
|
* new width and height internally, as well as re-allocating
|
|
* the pixel buffer for the new size.
|
|
*
|
|
* Note that this will nuke any cameraMode() settings.
|
|
*/
|
|
public void resize(int iwidth, int iheight) { // ignore
|
|
//System.out.println("resize " + iwidth + " " + iheight);
|
|
|
|
width = iwidth;
|
|
height = iheight;
|
|
width1 = width - 1;
|
|
height1 = height - 1;
|
|
|
|
allocate();
|
|
|
|
// clear the screen with the old background color
|
|
background(backgroundColor);
|
|
}
|
|
|
|
|
|
// broken out because of subclassing
|
|
protected void allocate() {
|
|
pixelCount = width * height;
|
|
pixels = new int[pixelCount];
|
|
|
|
// because of a java 1.1 bug, pixels must be registered as
|
|
// opaque before their first run, the memimgsrc will flicker
|
|
// and run very slowly.
|
|
for (int i = 0; i < pixelCount; i++) pixels[i] = 0xffffffff;
|
|
|
|
cm = new DirectColorModel(32, 0x00ff0000, 0x0000ff00, 0x000000ff);;
|
|
mis = new MemoryImageSource(width, height, pixels, 0, width);
|
|
mis.setFullBufferUpdates(true);
|
|
mis.setAnimated(true);
|
|
image = Toolkit.getDefaultToolkit().createImage(mis);
|
|
}
|
|
|
|
|
|
/**
|
|
* set engine's default values
|
|
*/
|
|
public void defaults() {
|
|
colorMode(RGB, TFF);
|
|
fill(TFF);
|
|
stroke(0);
|
|
strokeWeight(ONE);
|
|
background(204);
|
|
|
|
// init shape stuff
|
|
shape = 0;
|
|
|
|
// init matrices (must do before lights)
|
|
matrixStackDepth = 0;
|
|
|
|
rectMode(CORNER);
|
|
ellipseMode(CENTER);
|
|
arcMode(CENTER);
|
|
angleMode(RADIANS);
|
|
|
|
// no current font
|
|
textFont = null;
|
|
//text_mode = ALIGN_LEFT;
|
|
//text_space = OBJECT_SPACE;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// FRAME
|
|
|
|
|
|
/**
|
|
* Initializes engine before drawing a new frame.
|
|
* Called by PApplet, no need to call this.
|
|
*/
|
|
public void beginFrame() {
|
|
resetMatrix(); // reset model matrix
|
|
|
|
// reset vertices
|
|
vertexCount = 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Indicates a completed frame.
|
|
* Finishes rendering and swaps the buffer to the screen.
|
|
*
|
|
* If z-sorting has been turned on, then the triangles will
|
|
* all be quicksorted here (to make alpha work more properly)
|
|
* and then blit to the screen.
|
|
*/
|
|
public void endFrame() {
|
|
// moving this back here (post-68) because of macosx thread problem
|
|
mis.newPixels(pixels, cm, 0, width);
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// SHAPES
|
|
|
|
/**
|
|
* Start a new shape of type POLYGON
|
|
*/
|
|
public void beginShape() {
|
|
beginShape(POLYGON);
|
|
}
|
|
|
|
|
|
/**
|
|
* Start a new shape.
|
|
*
|
|
* @param kind indicates shape type
|
|
*/
|
|
public void beginShape(int kind) {
|
|
shape = kind;
|
|
|
|
// reset vertex, line and triangle information
|
|
// every shape is rendered at endShape();
|
|
vertexCount = 0;
|
|
|
|
splineVertexCount = 0;
|
|
//spline_vertices_flat = true;
|
|
|
|
//strokeChanged = false;
|
|
//fillChanged = false;
|
|
//normalChanged = false;
|
|
}
|
|
|
|
|
|
public void endShape() {
|
|
int vertex_end = vertexCount;
|
|
|
|
// don't try to draw if there are no vertices
|
|
// (fixes a bug in LINE_LOOP that re-adds a nonexistent vertex)
|
|
if (vertexCount == 0) {
|
|
shape = 0;
|
|
return;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// CREATE LINES
|
|
|
|
int increment = 1;
|
|
int stop = 0;
|
|
int counter = 0;
|
|
|
|
if (stroke) {
|
|
switch (shape) {
|
|
|
|
case POINTS:
|
|
{
|
|
stop = vertex_end;
|
|
for (int i = vertex_start; i < stop; i++) {
|
|
add_path(); // total overkill for points
|
|
add_line(i, i);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LINES:
|
|
case LINE_STRIP:
|
|
case LINE_LOOP:
|
|
{
|
|
// store index of first vertex
|
|
int first = lineCount;
|
|
stop = vertex_end - 1;
|
|
increment = (shape == LINES) ? 2 : 1;
|
|
|
|
// for LINE_STRIP and LINE_LOOP, make this all one path
|
|
if (shape != LINES) add_path();
|
|
|
|
for (int i = vertex_start; i < stop; i+=increment) {
|
|
// for LINES, make a new path for each segment
|
|
if (shape == LINES) add_path();
|
|
add_line(i, i+1);
|
|
}
|
|
|
|
// for LINE_LOOP, close the loop with a final segment
|
|
if (shape == LINE_LOOP) {
|
|
add_line(stop, lines[first][VERTEX1]);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TRIANGLES:
|
|
{
|
|
for (int i = vertex_start; i < vertex_end; i += 3) {
|
|
add_path();
|
|
counter = i - vertex_start;
|
|
add_line(i+0, i+1);
|
|
add_line(i+1, i+2);
|
|
add_line(i+2, i+0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TRIANGLE_STRIP:
|
|
{
|
|
// first draw all vertices as a line strip
|
|
stop = vertex_end-1;
|
|
|
|
add_path();
|
|
for (int i = vertex_start; i < stop; i++) {
|
|
counter = i - vertex_start;
|
|
add_line(i,i+1);
|
|
}
|
|
|
|
// then draw from vertex (n) to (n+2)
|
|
stop = vertex_end-2;
|
|
for (int i = vertex_start; i < stop; i++) {
|
|
add_path();
|
|
add_line(i,i+2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TRIANGLE_FAN:
|
|
{
|
|
// this just draws a series of line segments
|
|
// from the center to each exterior point
|
|
for (int i = vertex_start + 1; i < vertex_end; i++) {
|
|
add_path();
|
|
add_line(vertex_start, i);
|
|
}
|
|
|
|
// then a single line loop around the outside.
|
|
add_path();
|
|
for (int i = vertex_start + 1; i < vertex_end-1; i++) {
|
|
add_line(i, i+1);
|
|
}
|
|
// closing the loop
|
|
add_line(vertex_end-1, vertex_start + 1);
|
|
}
|
|
break;
|
|
|
|
case QUADS:
|
|
{
|
|
for (int i = vertex_start; i < vertex_end; i += 4) {
|
|
add_path();
|
|
counter = i - vertex_start;
|
|
add_line(i+0, i+1);
|
|
add_line(i+1, i+2);
|
|
add_line(i+2, i+3);
|
|
add_line(i+3, i+0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case QUAD_STRIP:
|
|
{
|
|
// first draw all vertices as a line strip
|
|
stop = vertex_end - 1;
|
|
|
|
add_path();
|
|
for (int i = vertex_start; i < stop; i++) {
|
|
counter = i - vertex_start;
|
|
add_line(i, i+1);
|
|
}
|
|
|
|
// then draw from vertex (n) to (n+3)
|
|
stop = vertex_end-2;
|
|
increment = 2;
|
|
|
|
add_path();
|
|
for (int i = vertex_start; i < stop; i += increment) {
|
|
add_line(i, i+3);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case POLYGON:
|
|
case CONCAVE_POLYGON:
|
|
case CONVEX_POLYGON:
|
|
{
|
|
// store index of first vertex
|
|
int first = lineCount;
|
|
stop = vertex_end - 1;
|
|
|
|
add_path();
|
|
for (int i = vertex_start; i < stop; i++) {
|
|
add_line(i, i+1);
|
|
}
|
|
// draw the last line connecting back to the first point in poly
|
|
add_line(stop, lines[first][VERTEX1]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// CREATE TRIANGLES
|
|
|
|
if (fill) {
|
|
switch (shape) {
|
|
case TRIANGLES:
|
|
case TRIANGLE_STRIP:
|
|
{
|
|
stop = vertex_end - 2;
|
|
increment = (shape == TRIANGLES) ? 3 : 1;
|
|
for (int i = vertex_start; i < stop; i += increment) {
|
|
add_triangle(i, i+1, i+2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case QUADS:
|
|
case QUAD_STRIP:
|
|
{
|
|
stop = vertexCount-3;
|
|
increment = (shape == QUADS) ? 4 : 2;
|
|
|
|
for (int i = vertex_start; i < stop; i += increment) {
|
|
// first triangle
|
|
add_triangle(i, i+1, i+2);
|
|
// second triangle
|
|
add_triangle(i, i+2, i+3);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case POLYGON:
|
|
case CONCAVE_POLYGON:
|
|
case CONVEX_POLYGON:
|
|
{
|
|
triangulate_polygon();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// 2D or 3D POINTS FROM MODEL (MX, MY, MZ) TO VIEW SPACE (X, Y, Z)
|
|
|
|
// if no depth in use, then the points can be transformed simpler
|
|
for (int i = vertex_start; i < vertex_end; i++) {
|
|
vertices[i][X] = m00*vertices[i][MX] + m01*vertices[i][MY] + m02;
|
|
vertices[i][Y] = m10*vertices[i][MX] + m11*vertices[i][MY] + m12;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// TRANSFORM / LIGHT / CLIP
|
|
|
|
//light_and_transform();
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// RENDER SHAPES FILLS HERE WHEN NOT DEPTH SORTING
|
|
|
|
// if true, the shapes will be rendered on endFrame
|
|
//if (hints[DEPTH_SORT]) {
|
|
//shape = 0;
|
|
//return;
|
|
//}
|
|
|
|
if (fill) render_triangles();
|
|
if (stroke) render_lines();
|
|
|
|
shape = 0;
|
|
}
|
|
|
|
|
|
public void vertex(float x, float y) {
|
|
if (vertexCount == vertices.length) {
|
|
float temp[][] = new float[vertexCount<<1][VERTEX_FIELD_COUNT];
|
|
System.arraycopy(vertices, 0, temp, 0, vertexCount);
|
|
vertices = temp;
|
|
message(CHATTER, "allocating more vertices " + vertices.length);
|
|
}
|
|
float vertex[] = vertices[vertexCount++];
|
|
|
|
//if (polygon.redundantVertex(x, y, z)) return;
|
|
|
|
// user called vertex(), so that invalidates anything queued
|
|
// up for curve vertices. if this is internally called by
|
|
// spline_segment, then splineVertexCount will be saved and restored.
|
|
splineVertexCount = 0;
|
|
|
|
vertex[MX] = x;
|
|
vertex[MY] = y;
|
|
|
|
if (fill) {
|
|
vertex[R] = fillR;
|
|
vertex[G] = fillG;
|
|
vertex[B] = fillB;
|
|
vertex[A] = fillA;
|
|
}
|
|
|
|
if (stroke) {
|
|
vertex[SR] = strokeR;
|
|
vertex[SG] = strokeG;
|
|
vertex[SB] = strokeB;
|
|
vertex[SA] = strokeA;
|
|
vertex[SW] = strokeWeight;
|
|
}
|
|
}
|
|
|
|
|
|
//public void vertex(float x, float y, float u, float v) {
|
|
//}
|
|
|
|
//public void vertex(float x, float y, float z) {
|
|
//}
|
|
|
|
//public void vertex(float x, float y, float z, float u, float v) {
|
|
//}
|
|
|
|
|
|
protected void spline_vertex(float x, float y, boolean bezier) {
|
|
// allocate space for the spline vertices
|
|
// to improve processing applet load times, don't allocate until actual use
|
|
if (splineVertices == null) {
|
|
splineVertices = new float[DEFAULT_SPLINE_VERTICES][VERTEX_FIELD_COUNT];
|
|
}
|
|
|
|
// if more than 128 points, shift everything back to the beginning
|
|
if (splineVertexCount == DEFAULT_SPLINE_VERTICES) {
|
|
System.arraycopy(splineVertices[DEFAULT_SPLINE_VERTICES - 3], 0,
|
|
splineVertices[0], 0, VERTEX_FIELD_COUNT);
|
|
System.arraycopy(splineVertices[DEFAULT_SPLINE_VERTICES - 2], 0,
|
|
splineVertices[1], 0, VERTEX_FIELD_COUNT);
|
|
splineVertexCount = 3;
|
|
}
|
|
|
|
// 'flat' may be a misnomer here because it's actually just
|
|
// calculating whether z is zero for all the spline points,
|
|
// so that it knows whether to calculate all three params,
|
|
// or just two for x and y.
|
|
//if (splineVertices_flat) {
|
|
//if (z != 0) splineVertices_flat = false;
|
|
//}
|
|
float vertex[] = splineVertices[splineVertexCount];
|
|
|
|
vertex[MX] = x;
|
|
vertex[MY] = y;
|
|
|
|
if (fill) {
|
|
vertex[R] = fillR;
|
|
vertex[G] = fillG;
|
|
vertex[B] = fillB;
|
|
vertex[A] = fillA;
|
|
}
|
|
|
|
if (stroke) {
|
|
vertex[SR] = strokeR;
|
|
vertex[SG] = strokeG;
|
|
vertex[SB] = strokeB;
|
|
vertex[SA] = strokeA;
|
|
vertex[SW] = strokeWeight;
|
|
}
|
|
|
|
splineVertexCount++;
|
|
|
|
// draw a segment if there are enough points
|
|
if (splineVertexCount > 3) {
|
|
if (bezier) {
|
|
if ((splineVertexCount % 4) == 0) {
|
|
if (!bezier_inited) bezier_init();
|
|
spline2_segment(splineVertexCount-4,
|
|
splineVertexCount-4,
|
|
bezier_draw,
|
|
bezier_detail);
|
|
}
|
|
} else { // catmull-rom curve (!bezier)
|
|
if (!curve_inited) curve_init();
|
|
spline2_segment(splineVertexCount-4,
|
|
splineVertexCount-3,
|
|
curve_draw,
|
|
curve_detail);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* See notes with the bezier() function.
|
|
*/
|
|
public void bezierVertex(float x, float y) {
|
|
spline_vertex(x, y, true);
|
|
}
|
|
|
|
/**
|
|
* See notes with the bezier() function.
|
|
*/
|
|
//public void bezierVertex(float x, float y, float z) {
|
|
//}
|
|
|
|
/**
|
|
* See notes with the curve() function.
|
|
*/
|
|
public void curveVertex(float x, float y) {
|
|
spline_vertex(x, y, false);
|
|
}
|
|
|
|
/**
|
|
* See notes with the curve() function.
|
|
*/
|
|
//public void curveVertex(float x, float y, float z) {
|
|
//}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// POINT
|
|
|
|
|
|
public void point(float x, float y) {
|
|
point(x, y, 0);
|
|
}
|
|
|
|
|
|
public void point(float x, float y, float z) {
|
|
// temporary, just for the opengl stuff
|
|
beginShape(POINTS);
|
|
vertex(x, y, z);
|
|
endShape();
|
|
|
|
/*
|
|
if (depth) {
|
|
if (strokeWeight < 2) {
|
|
// just a single dot on the screen with a z value
|
|
// TODO what is lighting calculation for this point?
|
|
point0(screenX(x, y, z),
|
|
screenY(x, y, z),
|
|
screenZ(x, y, z), strokeColor);
|
|
|
|
} else {
|
|
float cx = screenX(x, y, z);
|
|
float cy = screenX(x, y, z);
|
|
float hsw = strokeWeight / 2f;
|
|
|
|
if (strokeCap == ROUND_ENDCAP) {
|
|
// non-smoothed, filled circle
|
|
circle0_rough_fill(cx, cy, z, hsw, strokeColor);
|
|
|
|
} else { // otherwise one of the square endcaps
|
|
//if ((strokeCap == PROJECTED_ENDCAP) ||
|
|
// (strokeCap == SQUARE_ENDCAP)) {
|
|
// technically, if SQUARE_ENDCAP, nothing should be drawn
|
|
// but we'll go easy on the lads
|
|
// non-smoothed (since 3D), filled square
|
|
|
|
int x1 = (int) (cx - hsw + 0.5f);
|
|
int y1 = (int) (cy - hsw + 0.5f);
|
|
int x2 = (int) (cx + hsw + 0.5f);
|
|
int y2 = (int) (cy + hsw + 0.5f);
|
|
|
|
rect0_rough_fill(x1, y1, x2, y2, z, strokeColor);
|
|
}
|
|
}
|
|
|
|
} else { // noDepth
|
|
if (strokeWeight < 2) {
|
|
point0(screenX(x, y), screenY(x, y), 0, strokeColor);
|
|
|
|
} else {
|
|
float hsw = strokeWeight / 2f;
|
|
|
|
if ((strokeCap == PROJECTED_ENDCAP) ||
|
|
(strokeCap == SQUARE_ENDCAP)) {
|
|
rect0_fill(x - hsw, y - hsw, x + hsw, y + hsw, 0, strokeColor);
|
|
|
|
} else if (strokeCap == ROUND_ENDCAP) {
|
|
circle0_fill(x - hsw, y - hsw, 0, hsw, strokeColor);
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
private void point2(float x, float y, int color) {
|
|
}
|
|
|
|
|
|
private void point0(float xx, float yy, float z, int color) {
|
|
point0((int) (xx + 0.5f), (int) (yy + 0.5f), z, color);
|
|
}
|
|
|
|
|
|
private void point0(int x, int y, float z, int color) {
|
|
if ((x < 0) || (x > width1) ||
|
|
(y < 0) || (y > height1)) return;
|
|
|
|
int index = y*width + x;
|
|
if ((color & 0xff000000) == 0xff000000) { // opaque
|
|
pixels[index] = color;
|
|
|
|
} else { // transparent
|
|
// a1 is how much of the orig pixel
|
|
int a2 = (color >> 24) & 0xff;
|
|
int a1 = a2 ^ 0xff;
|
|
|
|
int p2 = strokeColor;
|
|
int p1 = pixels[index];
|
|
|
|
int r = (a1 * ((p1 >> 16) & 0xff) + a2 * ((p2 >> 16) & 0xff)) & 0xff00;
|
|
int g = (a1 * ((p1 >> 8) & 0xff) + a2 * ((p2 >> 8) & 0xff)) & 0xff00;
|
|
int b = (a1 * ( p1 & 0xff) + a2 * ( p2 & 0xff)) >> 8;
|
|
|
|
pixels[index] = 0xff000000 | (r << 8) | g | b;
|
|
}
|
|
if (zbuffer != null) zbuffer[index] = z;
|
|
}
|
|
|
|
|
|
// points are inherently flat, but always tangent
|
|
// to the screen surface. the z is only so that things
|
|
// get scaled properly if the pt is way in back
|
|
/*
|
|
private void thick_point(float x, float y, float z, // note floats
|
|
float r, float g, float b, float a) {
|
|
spolygon.reset(4);
|
|
spolygon.interpARGB = false; // no changes for vertices of a point
|
|
|
|
float strokeWidth2 = strokeWeight/2.0f;
|
|
|
|
float svertex[] = spolygon.vertices[0];
|
|
svertex[X] = x - strokeWidth2;
|
|
svertex[Y] = y - strokeWidth2;
|
|
svertex[Z] = z;
|
|
|
|
svertex[R] = r;
|
|
svertex[G] = g;
|
|
svertex[B] = b;
|
|
svertex[A] = a;
|
|
|
|
svertex = spolygon.vertices[1];
|
|
svertex[X] = x + strokeWidth2;
|
|
svertex[Y] = y - strokeWidth2;
|
|
svertex[Z] = z;
|
|
|
|
svertex = spolygon.vertices[2];
|
|
svertex[X] = x + strokeWidth2;
|
|
svertex[Y] = y + strokeWidth2;
|
|
svertex[Z] = z;
|
|
|
|
svertex = spolygon.vertices[3];
|
|
svertex[X] = x - strokeWidth2;
|
|
svertex[Y] = y + strokeWidth2;
|
|
svertex[Z] = z;
|
|
|
|
spolygon.render();
|
|
}
|
|
*/
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// LINE
|
|
|
|
|
|
public void line(float x1, float y1, float x2, float y2) {
|
|
beginShape(LINES);
|
|
vertex(x1, y1);
|
|
vertex(x2, y2);
|
|
endShape();
|
|
}
|
|
|
|
|
|
public void line(float x1, float y1, float z1,
|
|
float x2, float y2, float z2) {
|
|
beginShape(LINES);
|
|
vertex(x1, y1, z1);
|
|
vertex(x2, y2, z2);
|
|
endShape();
|
|
}
|
|
|
|
|
|
private void thick_flat_line(float ox1, float oy1,
|
|
float r1, float g1, float b1, float a1,
|
|
float ox2, float oy2,
|
|
float r2, float g2, float b2, float a2) {
|
|
spolygon.interpARGB = (r1 != r2) || (g1 != g2) || (b1 != b2) || (a1 != a2);
|
|
spolygon.interpZ = false;
|
|
|
|
float dX = ox2-ox1 + EPSILON;
|
|
float dY = oy2-oy1 + EPSILON;
|
|
float len = sqrt(dX*dX + dY*dY);
|
|
|
|
// TODO strokeWidth should be transformed!
|
|
float rh = strokeWeight / len;
|
|
|
|
float dx0 = rh * dY;
|
|
float dy0 = rh * dX;
|
|
float dx1 = rh * dY;
|
|
float dy1 = rh * dX;
|
|
|
|
spolygon.reset(4);
|
|
|
|
float svertex[] = spolygon.vertices[0];
|
|
svertex[X] = ox1+dx0;
|
|
svertex[Y] = oy1-dy0;
|
|
svertex[R] = r1;
|
|
svertex[G] = g1;
|
|
svertex[B] = b1;
|
|
svertex[A] = a1;
|
|
|
|
svertex = spolygon.vertices[1];
|
|
svertex[X] = ox1-dx0;
|
|
svertex[Y] = oy1+dy0;
|
|
svertex[R] = r1;
|
|
svertex[G] = g1;
|
|
svertex[B] = b1;
|
|
svertex[A] = a1;
|
|
|
|
svertex = spolygon.vertices[2];
|
|
svertex[X] = ox2-dx1;
|
|
svertex[Y] = oy2+dy1;
|
|
svertex[R] = r2;
|
|
svertex[G] = g2;
|
|
svertex[B] = b2;
|
|
svertex[A] = a2;
|
|
|
|
svertex = spolygon.vertices[3];
|
|
svertex[X] = ox2+dx1;
|
|
svertex[Y] = oy2-dy1;
|
|
svertex[R] = r2;
|
|
svertex[G] = g2;
|
|
svertex[B] = b2;
|
|
svertex[A] = a2;
|
|
|
|
spolygon.render();
|
|
}
|
|
|
|
|
|
// the incoming values are transformed,
|
|
// and the colors have been calculated
|
|
private void thick_spatial_line(float x1, float y1, float z1,
|
|
float r1, float g1, float b1,
|
|
float x2, float y2, float z2,
|
|
float r2, float g2, float b2) {
|
|
spolygon.interpARGB = (r1 != r2) || (g1 != g2) || (b1 != b2);
|
|
spolygon.interpZ = true;
|
|
|
|
float ox1 = x1; float oy1 = y1; float oz1 = z1;
|
|
float ox2 = x2; float oy2 = y2; float oz2 = z2;
|
|
|
|
float dX = ox2-ox1 + 0.0001f;
|
|
float dY = oy2-oy1 + 0.0001f;
|
|
float len = sqrt(dX*dX + dY*dY);
|
|
|
|
float rh = strokeWeight / len;
|
|
|
|
float dx0 = rh * dY;
|
|
float dy0 = rh * dX;
|
|
float dx1 = rh * dY;
|
|
float dy1 = rh * dX;
|
|
|
|
spolygon.reset(4);
|
|
|
|
float svertex[] = spolygon.vertices[0];
|
|
svertex[X] = ox1+dx0;
|
|
svertex[Y] = oy1-dy0;
|
|
svertex[Z] = oz1;
|
|
svertex[R] = r1;
|
|
svertex[G] = g1;
|
|
svertex[B] = b1;
|
|
|
|
svertex = spolygon.vertices[1];
|
|
svertex[X] = ox1-dx0;
|
|
svertex[Y] = oy1+dy0;
|
|
svertex[Z] = oz1;
|
|
svertex[R] = r1;
|
|
svertex[G] = g1;
|
|
svertex[B] = b1;
|
|
|
|
svertex = spolygon.vertices[2];
|
|
svertex[X] = ox2-dx1;
|
|
svertex[Y] = oy2+dy1;
|
|
svertex[Z] = oz2;
|
|
svertex[R] = r2;
|
|
svertex[G] = g2;
|
|
svertex[B] = b2;
|
|
|
|
svertex = spolygon.vertices[3];
|
|
svertex[X] = ox2+dx1;
|
|
svertex[Y] = oy2-dy1;
|
|
svertex[Z] = oz2;
|
|
svertex[R] = r2;
|
|
svertex[G] = g2;
|
|
svertex[B] = b2;
|
|
|
|
spolygon.render();
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// TRIANGLE
|
|
|
|
|
|
public void triangle(float x1, float y1, float x2, float y2,
|
|
float x3, float y3) {
|
|
beginShape(TRIANGLES);
|
|
vertex(x1, y1);
|
|
vertex(x2, y2);
|
|
vertex(x3, y3);
|
|
endShape();
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// RECT
|
|
|
|
|
|
public void rectMode(int mode) {
|
|
rectMode = mode;
|
|
}
|
|
|
|
|
|
public void rect(float x1, float y1, float x2, float y2) {
|
|
float hradius, vradius;
|
|
switch (rectMode) {
|
|
case CORNERS:
|
|
break;
|
|
case CORNER:
|
|
x2 += x1; y2 += y1;
|
|
break;
|
|
case CENTER_RADIUS:
|
|
hradius = x2;
|
|
vradius = y2;
|
|
x2 = x1 + hradius;
|
|
y2 = y1 + vradius;
|
|
x1 -= hradius;
|
|
y1 -= vradius;
|
|
break;
|
|
case CENTER:
|
|
hradius = x2 / 2.0f;
|
|
vradius = y2 / 2.0f;
|
|
x2 = x1 + hradius;
|
|
y2 = y1 + vradius;
|
|
x1 -= hradius;
|
|
y1 -= vradius;
|
|
}
|
|
|
|
if (depth || !optimize2) {
|
|
rect3(x1, y1, x2, y2);
|
|
|
|
} else {
|
|
rect2(x1, y1, x2, y2);
|
|
}
|
|
|
|
/*
|
|
if (depth) {
|
|
rect3(x1, y1, x2, y2);
|
|
|
|
} else if ((m00 != 1) || (m11 != 1) ||
|
|
(m01 != 0) || (m10 != 0)) {
|
|
rect2(x1, y1, x2, y2);
|
|
|
|
} else {
|
|
rect0(x1 + m02, y1 + m12, x2 + m02, y2 + m12);
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
/**
|
|
* This is the function overridden by other renderers.
|
|
*/
|
|
protected void rect3(float x1, float y1, float x2, float y2) {
|
|
beginShape(QUADS);
|
|
vertex(x1, y1);
|
|
vertex(x2, y1);
|
|
vertex(x2, y2);
|
|
vertex(x1, y2);
|
|
endShape();
|
|
}
|
|
|
|
|
|
protected void rect2(float x1, float y1, float x2, float y2) {
|
|
if (fill) rect2_fill(x1, y1, x2, y2);
|
|
if (stroke) rect2_stroke(x1, y1, x2, y2);
|
|
}
|
|
|
|
|
|
protected void rect2_fill(float x1, float y1, float x2, float y2) {
|
|
if ((m01 != 0) || (m10 != 0)) {
|
|
// this is actually transformed, transform points and draw a quad
|
|
quad0_fill(screenX(x1, y1), screenY(x1, y1),
|
|
screenX(x2, y1), screenY(x2, y1),
|
|
screenX(x2, y2), screenY(x2, y2),
|
|
screenX(x1, y2), screenY(x1, y2), fillColor);
|
|
|
|
} else {
|
|
if ((m00 == 1) && (m11 == 1)) {
|
|
// no scale, but maybe a translate
|
|
rect0_fill(x1 + m02, y1 + m12, x2 + m02, y2 + m12, 0, fillColor);
|
|
|
|
} else {
|
|
// scaled, maybe translated
|
|
rect0_fill(screenX(x1, y1), screenY(x1, y1),
|
|
screenX(x2, y2), screenY(x2, y2), 0, fillColor);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
protected void rect0_fill(float x1, float y1, float x2, float y2,
|
|
float z, int color) {
|
|
if (smooth) {
|
|
rect0_smooth_fill(x1, y1, x2, y2, z, color);
|
|
|
|
} else {
|
|
rect0_rough_fill((int) (x1+0.5f), (int) (y1+0.5f),
|
|
(int) (x2+0.5f), (int) (y2+0.5f), z, color);
|
|
}
|
|
}
|
|
|
|
|
|
protected void rect0_smooth_fill(float x1, float y1, float x2, float y2,
|
|
float z, int color) {
|
|
quad0_smooth_fill(x1, y1, x2, y1, x2, y2, x1, y2, color);
|
|
}
|
|
|
|
|
|
protected void rect0_rough_fill(int x1, int y1, int x2, int y2,
|
|
float z, int color) {
|
|
// needs to check if smooth
|
|
// or if there's an affine transform on the shape
|
|
// also the points are now floats instead of ints
|
|
|
|
//System.out.println("flat quad");
|
|
if (y2 < y1) {
|
|
int temp = y1; y1 = y2; y2 = temp;
|
|
}
|
|
if (x2 < x1) {
|
|
int temp = x1; x1 = x2; x2 = temp;
|
|
}
|
|
// checking to watch out for boogers
|
|
if ((x1 > width1) || (x2 < 0) ||
|
|
(y1 > height1) || (y2 < 0)) return;
|
|
|
|
int fx1 = (int) x1;
|
|
int fy1 = (int) y1;
|
|
int fx2 = (int) x2;
|
|
int fy2 = (int) y2;
|
|
|
|
// these only affect the fill, not the stroke
|
|
// (otherwise strange boogers at edges b/c frame changes shape)
|
|
if (fx1 < 0) fx1 = 0;
|
|
if (fx2 > width) fx2 = width;
|
|
if (fy1 < 0) fy1 = 0;
|
|
if (fy2 > height) fy2 = height;
|
|
|
|
// [toxi 031223]
|
|
// on avg. 20-25% faster fill routine using System.arraycopy()
|
|
int ww = fx2 - fx1;
|
|
int hh = fy2 - fy1;
|
|
int[] row = new int[ww];
|
|
for (int i = 0; i < ww; i++) row[i] = fillColor;
|
|
int idx = fy1 * width + fx1;
|
|
for (int y = 0; y < hh; y++) {
|
|
System.arraycopy(row, 0, pixels, idx, ww);
|
|
idx += width;
|
|
}
|
|
row = null;
|
|
}
|
|
|
|
|
|
protected void rect2_stroke(float x1, float y1, float x2, float y2) {
|
|
/*
|
|
if (strokeWeight == 1) {
|
|
thin_flat_line(x1, y1, x2, y1);
|
|
thin_flat_line(x2, y1, x2, y2);
|
|
thin_flat_line(x2, y2, x1, y2);
|
|
thin_flat_line(x1, y2, x1, y1);
|
|
|
|
} else {
|
|
thick_flat_line(x1, y1, fillR, fillG, fillB, fillA,
|
|
x2, y1, fillR, fillG, fillB, fillA);
|
|
thick_flat_line(x2, y1, fillR, fillG, fillB, fillA,
|
|
x2, y2, fillR, fillG, fillB, fillA);
|
|
thick_flat_line(x2, y2, fillR, fillG, fillB, fillA,
|
|
x1, y2, fillR, fillG, fillB, fillA);
|
|
thick_flat_line(x1, y2, fillR, fillG, fillB, fillA,
|
|
x1, y1, fillR, fillG, fillB, fillA);
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// QUAD
|
|
|
|
|
|
public void quad(float x1, float y1, float x2, float y2,
|
|
float x3, float y3, float x4, float y4) {
|
|
beginShape(QUADS);
|
|
vertex(x1, y1);
|
|
vertex(x2, y2);
|
|
vertex(x3, y3);
|
|
vertex(x4, y4);
|
|
endShape();
|
|
}
|
|
|
|
|
|
protected void quad0_fill(float x1, float y1, float x2, float y2,
|
|
float x3, float y3, float x4, float y4,
|
|
int color) {
|
|
if (smooth) {
|
|
quad0_smooth_fill(x1, y1, x2, y2, x3, y3, x4, y4, color);
|
|
|
|
} else {
|
|
quad0_rough_fill((int) (x1+0.5f), (int) (y1+0.5f),
|
|
(int) (x2+0.5f), (int) (y2+0.5f),
|
|
(int) (x3+0.5f), (int) (y3+0.5f),
|
|
(int) (x4+0.5f), (int) (y4+0.5f),
|
|
color);
|
|
}
|
|
}
|
|
|
|
|
|
protected void quad0_smooth_fill(float x1, float y1, float x2, float y2,
|
|
float x3, float y3, float x4, float y4,
|
|
int color) {
|
|
}
|
|
|
|
|
|
protected void quad0_rough_fill(float x1, float y1, float x2, float y2,
|
|
float x3, float y3, float x4, float y4,
|
|
int color) {
|
|
}
|
|
|
|
|
|
protected void quad0_stroke(float x1, float y1, float x2, float y2,
|
|
float x3, float y3, float x4, float y4,
|
|
int color) {
|
|
if (smooth) {
|
|
quad0_smooth_stroke(x1, y1, x2, y2, x3, y3, x4, y4, color);
|
|
|
|
} else {
|
|
quad0_rough_stroke((int) (x1+0.5f), (int) (y1+0.5f),
|
|
(int) (x2+0.5f), (int) (y2+0.5f),
|
|
(int) (x3+0.5f), (int) (y3+0.5f),
|
|
(int) (x4+0.5f), (int) (y4+0.5f), color);
|
|
}
|
|
}
|
|
|
|
|
|
protected void quad0_smooth_stroke(float x1, float y1, float x2, float y2,
|
|
float x3, float y3, float x4, float y4,
|
|
int color) {
|
|
}
|
|
|
|
|
|
protected void quad0_rough_stroke(float x1, float y1, float x2, float y2,
|
|
float x3, float y3, float x4, float y4,
|
|
int color) {
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// IMAGE
|
|
|
|
|
|
// NOPE this has to be overridden for opengl to prevent the
|
|
// NOPE upper situation from happening, since the mXX vars
|
|
// NOPE will be bad and glDrawPixels is slower drawing
|
|
// NOPE flat images than drawing an image as a texture..
|
|
public void image(PImage image, float x1, float y1) {
|
|
if (optimize0 && !depth &&
|
|
(m00 == 1) && (m01 == 0) &&
|
|
(m10 == 0) && (m11 == 1)) {
|
|
// if drawing a flat image with no warping,
|
|
// use faster routine to draw direct to the screen
|
|
if ((imageMode == CENTER) ||
|
|
(imageMode == CENTER_RADIUS)) {
|
|
x1 -= image.width /2f;
|
|
y1 -= image.height / 2f;
|
|
}
|
|
image0(image,
|
|
(int) (x1 + m02 + 0.5f),
|
|
(int) (y1 + m12 + 0.5f));
|
|
|
|
} else {
|
|
int savedTextureMode = textureMode;
|
|
textureMode(IMAGE_SPACE);
|
|
|
|
image(image, x1, y1, image.width, image.height,
|
|
0, 0, image.width, image.height);
|
|
|
|
textureMode(savedTextureMode);
|
|
}
|
|
}
|
|
|
|
|
|
public void image(PImage image,
|
|
float x1, float y1, float x2, float y2) {
|
|
int savedTextureMode = textureMode;
|
|
textureMode(IMAGE_SPACE);
|
|
|
|
image(image, x1, y1, x2, y2, 0, 0, image.width, image.height);
|
|
|
|
textureMode(savedTextureMode);
|
|
}
|
|
|
|
|
|
// this probably needs to be broken into affine/non-affine versions
|
|
// since affine w/ smoothing is a fairly easy case to handle and
|
|
// with better quality and speed than using the full texture mapping.
|
|
|
|
/**
|
|
* u, v coordinates are always of the form x1, y1, x2, y2, or the
|
|
* same as imageMode(CORNERS), even if the imageMode is something else.
|
|
*
|
|
* when drawing without depth(), the coordinates for u, v are
|
|
* always done in IMAGE_SPACE, because the textureMode() option
|
|
* is not available in 2D.
|
|
*/
|
|
public void image(PImage image,
|
|
float x1, float y1, float x2, float y2,
|
|
float u1, float v1, float u2, float v2) {
|
|
float hr, vr;
|
|
|
|
switch (imageMode) {
|
|
case CORNERS:
|
|
break;
|
|
case CORNER:
|
|
x2 += x1; y2 += y1;
|
|
break;
|
|
case CENTER:
|
|
x2 /= 2f;
|
|
y2 /= 2f;
|
|
hr = x2;
|
|
vr = y2;
|
|
x2 = x1 + hr;
|
|
y2 = y1 + vr;
|
|
x1 -= hr;
|
|
y1 -= vr;
|
|
break;
|
|
case CENTER_RADIUS:
|
|
hr = x2;
|
|
vr = y2;
|
|
x2 = x1 + hr;
|
|
y2 = y1 + vr;
|
|
x1 -= hr;
|
|
y1 -= vr;
|
|
break;
|
|
}
|
|
|
|
boolean savedStroke = stroke;
|
|
boolean savedFill = fill;
|
|
|
|
stroke = false;
|
|
fill = true;
|
|
|
|
float savedFillR = fillR;
|
|
float savedFillG = fillG;
|
|
float savedFillB = fillB;
|
|
float savedFillA = fillA;
|
|
|
|
if (tint) {
|
|
fillR = tintR;
|
|
fillG = tintG;
|
|
fillB = tintB;
|
|
fillA = tintA;
|
|
|
|
} else {
|
|
fillR = 1;
|
|
fillG = 1;
|
|
fillB = 1;
|
|
fillA = 1;
|
|
}
|
|
|
|
beginShape(QUADS);
|
|
texture(image); // move outside to make java gl happier?
|
|
vertex(x1, y1, u1, v1);
|
|
vertex(x1, y2, u1, v2);
|
|
vertex(x2, y2, u2, v2);
|
|
vertex(x2, y1, u2, v1);
|
|
endShape();
|
|
|
|
stroke = savedStroke;
|
|
fill = savedFill;
|
|
|
|
fillR = savedFillR;
|
|
fillG = savedFillG;
|
|
fillB = savedFillB;
|
|
fillA = savedFillA;
|
|
}
|
|
|
|
|
|
/**
|
|
* Image drawn in flat "screen space", with no scaling or warping.
|
|
* this is so common that a special routine is included for it,
|
|
* because the alternative is much slower.
|
|
*
|
|
* @param image image to be drawn
|
|
* @param sx1 x coordinate of upper-lefthand corner in screen space
|
|
* @param sy1 y coordinate of upper-lefthand corner in screen space
|
|
*/
|
|
protected void image0(PImage image, int sx1, int sy1) {
|
|
int ix1 = 0;
|
|
int iy1 = 0;
|
|
int ix2 = image.width;
|
|
int iy2 = image.height;
|
|
|
|
/*
|
|
if (imageMode == CENTER) {
|
|
sx1 -= image.width / 2;
|
|
sy1 -= image.height / 2;
|
|
}
|
|
*/
|
|
|
|
int sx2 = sx1 + image.width;
|
|
int sy2 = sy1 + image.height;
|
|
|
|
// don't draw if completely offscreen
|
|
// (without this check, ArrayIndexOutOfBoundsException)
|
|
if ((sx1 > width1) || (sx2 < 0) ||
|
|
(sy1 > height1) || (sy2 < 0)) return;
|
|
|
|
if (sx1 < 0) { // off left edge
|
|
ix1 -= sx1;
|
|
sx1 = 0;
|
|
}
|
|
if (sy1 < 0) { // off top edge
|
|
iy1 -= sy1;
|
|
sy1 = 0;
|
|
}
|
|
if (sx2 > width) { // off right edge
|
|
ix2 -= sx2 - width;
|
|
sx2 = width;
|
|
}
|
|
if (sy2 > height) { // off bottom edge
|
|
iy2 -= sy2 - height;
|
|
sy2 = height;
|
|
}
|
|
|
|
int source = iy1 * image.width + ix1;
|
|
int target = sy1 * width;
|
|
|
|
if (image.format == ARGB) {
|
|
for (int y = sy1; y < sy2; y++) {
|
|
int tx = 0;
|
|
|
|
for (int x = sx1; x < sx2; x++) {
|
|
pixels[target + x] =
|
|
_blend(pixels[target + x],
|
|
image.pixels[source + tx],
|
|
image.pixels[source + tx++] >>> 24);
|
|
}
|
|
source += image.width;
|
|
target += width;
|
|
}
|
|
} else if (image.format == ALPHA) {
|
|
for (int y = sy1; y < sy2; y++) {
|
|
int tx = 0;
|
|
|
|
for (int x = sx1; x < sx2; x++) {
|
|
pixels[target + x] =
|
|
_blend(pixels[target + x],
|
|
fillColor,
|
|
image.pixels[source + tx++]);
|
|
}
|
|
source += image.width;
|
|
target += width;
|
|
}
|
|
|
|
} else if (image.format == RGB) {
|
|
target += sx1;
|
|
int tw = sx2 - sx1;
|
|
for (int y = sy1; y < sy2; y++) {
|
|
System.arraycopy(image.pixels, source, pixels, target, tw);
|
|
// should set z coordinate in here
|
|
// or maybe not, since dims=0, meaning no relevant z
|
|
source += image.width;
|
|
target += width;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Used by OpenGL implementations of PGraphics, so that images,
|
|
* or textures, can be loaded into texture memory.
|
|
*/
|
|
public void cache(PImage image) {
|
|
// keep the lower } on a separate line b/c of preproc
|
|
}
|
|
|
|
public void cache(PImage images[]) {
|
|
// keep the lower } on a separate line b/c of preproc
|
|
}
|
|
|
|
protected void cache(PImage image, int index) {
|
|
// keep the lower } on a separate line b/c of preproc
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// ARC
|
|
|
|
|
|
public void arcMode(int mode) {
|
|
arcMode = mode;
|
|
}
|
|
|
|
|
|
/**
|
|
* Identical parameters and placement to ellipse,
|
|
* but draws only an arc of that ellipse.
|
|
*/
|
|
public void arc(float start, float stop,
|
|
float x, float y, float radius) {
|
|
arc(start, stop, x, y, radius, radius);
|
|
}
|
|
|
|
|
|
/**
|
|
* Identical parameters and placement to ellipse,
|
|
* but draws only an arc of that ellipse.
|
|
*/
|
|
public void arc(float start, float stop,
|
|
float x, float y, float hr, float vr) {
|
|
switch (arcMode) {
|
|
case CENTER_RADIUS:
|
|
break;
|
|
case CENTER:
|
|
hr /= 2f; vr /= 2f;
|
|
break;
|
|
case CORNER:
|
|
hr /= 2f; vr /= 2f;
|
|
x += hr; y += vr;
|
|
break;
|
|
case CORNERS:
|
|
hr = (hr - x) / 2f;
|
|
vr = (vr - y) / 2f;
|
|
x += hr;
|
|
y += vr;
|
|
break;
|
|
}
|
|
|
|
if (angleMode == DEGREES) {
|
|
start = start * DEG_TO_RAD;
|
|
stop = stop * DEG_TO_RAD;
|
|
|
|
// before running a while loop like this,
|
|
// make sure it will exit at some point.
|
|
if (Float.isInfinite(start) || Float.isInfinite(stop)) return;
|
|
while (stop < start) stop += TWO_PI;
|
|
}
|
|
|
|
if (depth || !optimize2) {
|
|
arc3(start, stop, x, y, hr, vr);
|
|
|
|
} else {
|
|
arc2(start, stop, x, y, hr, vr);
|
|
}
|
|
}
|
|
|
|
|
|
protected void arc3(float start, float stop,
|
|
float x, float y, float hr, float vr) {
|
|
if (fill) arc3_fill(start, stop, x, y, hr, vr);
|
|
if (stroke) arc3_stroke(start, stop, x, y, hr, vr);
|
|
}
|
|
|
|
|
|
/**
|
|
* Start and stop are in radians, converted by the parent function.
|
|
* Note that the radians can be greater (or less) than TWO_PI.
|
|
* This is so that an arc can be drawn that crosses zero mark,
|
|
* and the user will still collect $200.
|
|
*/
|
|
protected void arc3_fill(float start, float stop,
|
|
float x, float y, float hr, float vr) {
|
|
// shut off stroke for a minute
|
|
boolean savedStroke = stroke;
|
|
stroke = false;
|
|
|
|
int startLUT = (int) (0.5f + (start / TWO_PI) * SINCOS_LENGTH);
|
|
int stopLUT = (int) (0.5f + (stop / TWO_PI) * SINCOS_LENGTH);
|
|
|
|
beginShape(TRIANGLE_FAN);
|
|
vertex(x, y);
|
|
int increment = 1; // what's a good algorithm? stopLUT - startLUT;
|
|
for (int i = startLUT; i < stopLUT; i += increment) {
|
|
int ii = i % SINCOS_LENGTH;
|
|
vertex(x + cosLUT[ii] * hr,
|
|
y + sinLUT[ii] * vr);
|
|
}
|
|
// draw last point explicitly for accuracy
|
|
vertex(x + cosLUT[stopLUT % SINCOS_LENGTH] * hr,
|
|
y + sinLUT[stopLUT % SINCOS_LENGTH] * vr);
|
|
endShape();
|
|
|
|
stroke = savedStroke;
|
|
}
|
|
|
|
|
|
/**
|
|
* Almost identical to the arc3_fill() command, but this one
|
|
* uses a LINE_STRIP and doesn't include the first (center) vertex.
|
|
*/
|
|
protected void arc3_stroke(float start, float stop,
|
|
float x, float y, float hr, float vr) {
|
|
boolean savedFill = fill;
|
|
fill = false;
|
|
|
|
int startLUT = (int) (0.5f + (start / TWO_PI) * SINCOS_LENGTH);
|
|
int stopLUT = (int) (0.5f + (stop / TWO_PI) * SINCOS_LENGTH);
|
|
|
|
beginShape(LINE_STRIP);
|
|
int increment = 1; // what's a good algorithm? stopLUT - startLUT;
|
|
for (int i = startLUT; i < stopLUT; i += increment) {
|
|
int ii = i % SINCOS_LENGTH;
|
|
vertex(x + cosLUT[ii] * hr,
|
|
y + sinLUT[ii] * vr);
|
|
}
|
|
// draw last point explicitly for accuracy
|
|
vertex(x + cosLUT[stopLUT % SINCOS_LENGTH] * hr,
|
|
y + sinLUT[stopLUT % SINCOS_LENGTH] * vr);
|
|
endShape();
|
|
|
|
fill = savedFill;
|
|
}
|
|
|
|
|
|
protected void arc2(float start, float stop,
|
|
float x, float y, float hr, float vr) {
|
|
if (fill) arc2_fill(start, stop, x, y, hr, vr);
|
|
if (stroke) arc2_stroke(start, stop, x, y, hr, vr);
|
|
}
|
|
|
|
|
|
protected void arc2_fill(float start, float stop,
|
|
float x, float y, float hr, float vr) {
|
|
}
|
|
|
|
|
|
protected void arc2_stroke(float start, float stop,
|
|
float x, float y, float hr, float vr) {
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// ELLIPSE
|
|
|
|
|
|
public void ellipseMode(int mode) {
|
|
ellipseMode = mode;
|
|
}
|
|
|
|
|
|
public void ellipse(float x, float y, float hradius, float vradius) {
|
|
switch (ellipseMode) {
|
|
case CENTER_RADIUS:
|
|
break;
|
|
case CENTER:
|
|
hradius /= 2f; vradius /= 2f;
|
|
break;
|
|
case CORNER:
|
|
hradius /= 2f; vradius /= 2f;
|
|
x += hradius; y += vradius;
|
|
break;
|
|
case CORNERS:
|
|
hradius = (hradius - x) / 2f;
|
|
vradius = (vradius - y) / 2f;
|
|
x += hradius;
|
|
y += vradius;
|
|
break;
|
|
}
|
|
|
|
if (depth || !optimize2) {
|
|
ellipse3(x, y, hradius, vradius);
|
|
|
|
} else {
|
|
ellipse2(x, y, hradius, vradius);
|
|
}
|
|
}
|
|
|
|
|
|
public void ellipse3(float x, float y, float hradius, float vradius) {
|
|
if (fill) ellipse3_fill(x, y, hradius, vradius);
|
|
if (stroke) ellipse3_stroke(x, y, hradius, vradius);
|
|
}
|
|
|
|
|
|
protected void ellipse3_fill(float x, float y, float h, float v) {
|
|
}
|
|
|
|
|
|
protected void ellipse3_stroke(float x, float y, float h, float v) {
|
|
}
|
|
|
|
|
|
public void ellipse2(float x, float y, float hradius, float vradius) {
|
|
if (fill) ellipse2_fill(x, y, hradius, vradius);
|
|
if (stroke) ellipse2_stroke(x, y, hradius, vradius);
|
|
}
|
|
|
|
|
|
protected void ellipse2_fill(float x, float y, float h, float v) {
|
|
}
|
|
|
|
|
|
protected void ellipse2_stroke(float x, float y, float h, float v) {
|
|
}
|
|
|
|
|
|
/*
|
|
protected void ellipse_mess(float x, float y,
|
|
float hradius, float vradius) {
|
|
// adapt accuracy to radii used w/ a minimum of 4 segments [toxi]
|
|
// now uses current scale factors to determine "real" transformed radius
|
|
|
|
//System.out.println(m00 + " " + m11);
|
|
//int cAccuracy = (int)(4+Math.sqrt(hradius*abs(m00)+vradius*abs(m11))*2);
|
|
//int cAccuracy = (int)(4+Math.sqrt(hradius+vradius)*2);
|
|
|
|
// notched this up to *3 instead of *2 because things were
|
|
// looking a little rough, i.e. the calculate->arctangent example [fry]
|
|
|
|
// also removed the m00 and m11 because those were causing weirdness
|
|
// need an actual measure of magnitude in there [fry]
|
|
|
|
int cAccuracy = (int)(4+Math.sqrt(hradius+vradius)*3);
|
|
|
|
boolean plain =
|
|
!lights && !smooth && (strokeWeight == 1) &&
|
|
!fillAlpha && !strokeAlpha;
|
|
|
|
//boolean flat = (dimensions == 0) ||
|
|
//((dimensions == 2) && (m00 == m11) && (m00 == 1));
|
|
// FIXME
|
|
boolean flat = false;
|
|
|
|
if (plain && flat) {
|
|
if (hradius == vradius) {
|
|
circle0((int)x, (int)y, (int)hradius);
|
|
|
|
} else {
|
|
ellipse0((int)x, (int)y, (int)hradius, (int)vradius);
|
|
}
|
|
|
|
} else {
|
|
// [toxi031031] adapted to use new lookup tables
|
|
float inc = (float)SINCOS_LENGTH / cAccuracy;
|
|
|
|
float val = 0;
|
|
beginShape(POLYGON);
|
|
for (int i = 0; i < cAccuracy; i++) {
|
|
vertex(x + cosLUT[(int) val] * hradius,
|
|
y + sinLUT[(int) val] * vradius);
|
|
val += inc;
|
|
}
|
|
endShape();
|
|
}
|
|
}
|
|
*/
|
|
|
|
|
|
/*
|
|
private void flat_ellipse(int centerX, int centerY, int a, int b) {
|
|
//FIXME
|
|
//if (dimensions == 2) { // probably a translate but no scale
|
|
centerX = (int) screenX(centerX, centerY, 0);
|
|
centerY = (int) screenY(centerX, centerY, 0);
|
|
//}
|
|
if (fill) flat_ellipse_internal(centerX, centerY, a, b, true);
|
|
if (stroke) flat_ellipse_internal(centerX, centerY, a, b, false);
|
|
}
|
|
*/
|
|
|
|
|
|
private void ellipse0_stroke_rough(int cx, int cy, int a, int b) {
|
|
ellipse0_rough(cx, cy, a, b, false);
|
|
}
|
|
|
|
|
|
private void ellipse0_fill_rough(int cx, int cy, int a, int b) {
|
|
ellipse0_rough(cx, cy, a, b, true);
|
|
}
|
|
|
|
|
|
/**
|
|
* Bresenham-style ellipse drawing function, adapted from a posting to
|
|
* comp.graphics.algortihms.
|
|
*
|
|
* This function is included because the quality is so much better,
|
|
* and the drawing significantly faster than with adaptive ellipses
|
|
* drawn using the sine/cosine tables.
|
|
*
|
|
* @param centerX x coordinate of the center
|
|
* @param centerY y coordinate of the center
|
|
* @param a horizontal radius
|
|
* @param b vertical radius
|
|
*/
|
|
private void ellipse0_rough(int centerX, int centerY,
|
|
int a, int b, boolean filling) {
|
|
//int x, y, a2, b2, s, t;
|
|
|
|
int a2 = a*a;
|
|
int b2 = b*b;
|
|
int x = 0;
|
|
int y = b;
|
|
int s = a2*(1-2*b) + 2*b2;
|
|
int t = b2 - 2*a2*(2*b-1);
|
|
ellipse0_rough_internal(centerX, centerY, x, y, filling);
|
|
|
|
do {
|
|
if (s < 0) {
|
|
s += 2*b2*(2*x+3);
|
|
t += 4*b2*(x+1);
|
|
x++;
|
|
} else if (t < 0) {
|
|
s += 2*b2*(2*x+3) - 4*a2*(y-1);
|
|
t += 4*b2*(x+1) - 2*a2*(2*y-3);
|
|
x++;
|
|
y--;
|
|
} else {
|
|
s -= 4*a2*(y-1);
|
|
t -= 2*a2*(2*y-3);
|
|
y--;
|
|
}
|
|
ellipse0_rough_internal(centerX, centerY, x, y, filling);
|
|
|
|
} while (y > 0);
|
|
}
|
|
|
|
|
|
private final void ellipse0_rough_internal(int centerX, int centerY,
|
|
int ellipseX, int ellipseY,
|
|
boolean filling) {
|
|
// unfortunately this can't handle fill and stroke simultaneously,
|
|
// because the fill will later replace some of the stroke points
|
|
|
|
if (filling) {
|
|
for (int i = centerX - ellipseX + 1; i < centerX + ellipseX; i++) {
|
|
point0(i, centerY - ellipseY, 0, fillColor);
|
|
point0(i, centerY + ellipseY, 0, fillColor);
|
|
}
|
|
} else {
|
|
point0(centerX - ellipseX, centerY + ellipseY, 0, strokeColor);
|
|
point0(centerX + ellipseX, centerY + ellipseY, 0, strokeColor);
|
|
point0(centerX - ellipseX, centerY - ellipseY, 0, strokeColor);
|
|
point0(centerX + ellipseX, centerY - ellipseY, 0, strokeColor);
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
public void circle(float x, float y, float radius) {
|
|
switch (ellipseMode) {
|
|
case CENTER_RADIUS:
|
|
break;
|
|
case CENTER:
|
|
radius /= 2f; radius /= 2f;
|
|
break;
|
|
case CORNER:
|
|
radius /= 2f; radius /= 2f;
|
|
x += radius; y += radius;
|
|
break;
|
|
case CORNERS:
|
|
radius = (radius - x) / 2f;
|
|
radius = (radius - y) / 2f;
|
|
x += radius;
|
|
y += radius;
|
|
break;
|
|
}
|
|
|
|
if (depth || !optimize2) {
|
|
ellipse3(x, y, radius, radius);
|
|
|
|
} else {
|
|
circle2(x, y, radius);
|
|
}
|
|
}
|
|
|
|
|
|
public void circle2(float x, float y, float radius) {
|
|
}
|
|
|
|
|
|
/*
|
|
private void flat_circle(int centerX, int centerY, int radius) {
|
|
// FIXME
|
|
//if (dimensions == 2) { // translate but no scale
|
|
centerX = (int) screenX(centerX, centerY, 0);
|
|
centerY = (int) screenY(centerX, centerY, 0);
|
|
//}
|
|
if (fill) circle0_fill(centerX, centerY, radius);
|
|
if (stroke) circle0_stroke(centerX, centerY, radius);
|
|
}
|
|
*/
|
|
|
|
|
|
private void circle0(float x, float y, float r) {
|
|
if (fill) circle0_fill(x, y, 0, r, fillColor);
|
|
if (stroke) circle0_stroke(x, y, 0, r, strokeColor);
|
|
}
|
|
|
|
|
|
private void circle0_stroke(float x, float y, float z, float r, int color) {
|
|
if (smooth) {
|
|
circle0_stroke_smooth(x, y, z, r, color);
|
|
} else {
|
|
circle0_stroke_rough(x, y, z, r, color);
|
|
}
|
|
}
|
|
|
|
|
|
private void circle0_stroke_smooth(float x, float y, float z,
|
|
float r, int color) {
|
|
// TODO draw a circle that's smoothed in screen space coords
|
|
}
|
|
|
|
|
|
/**
|
|
* Draw the outline around a flat circle using a bresenham-style
|
|
* algorithm. Adapted from drawCircle function in "Computer Graphics
|
|
* for Java Programmers" by Leen Ammeraal, p. 110
|
|
*
|
|
* This function is included because the quality is so much better,
|
|
* and the drawing significantly faster than with adaptive ellipses
|
|
* drawn using the sine/cosine tables.
|
|
*
|
|
* Circle quadrants break down like so:
|
|
* |
|
|
* \ NNW | NNE /
|
|
* \ | /
|
|
* WNW \ | / ENE
|
|
* -------------------
|
|
* WSW / | \ ESE
|
|
* / | \
|
|
* / SSW | SSE \
|
|
* |
|
|
*
|
|
* @param xc x center
|
|
* @param yc y center
|
|
* @param r radius
|
|
*/
|
|
private void circle0_stroke_rough(float xcf, float ycf, float z,
|
|
float rf, int color) {
|
|
int xc = (int) (xcf + 0.5f);
|
|
int yc = (int) (ycf + 0.5f);
|
|
int r = (int) (rf + 0.5f);
|
|
|
|
int x = 0, y = r, u = 1, v = 2 * r - 1, E = 0;
|
|
while (x < y) {
|
|
point0(xc + x, yc + y, z, color); // NNE
|
|
point0(xc + y, yc - x, z, color); // ESE
|
|
point0(xc - x, yc - y, z, color); // SSW
|
|
point0(xc - y, yc + x, z, color); // WNW
|
|
|
|
x++; E += u; u += 2;
|
|
if (v < 2 * E) {
|
|
y--; E -= v; v -= 2;
|
|
}
|
|
if (x > y) break;
|
|
|
|
point0(xc + y, yc + x, z, color); // ENE
|
|
point0(xc + x, yc - y, z, color); // SSE
|
|
point0(xc - y, yc - x, z, color); // WSW
|
|
point0(xc - x, yc + y, z, color); // NNW
|
|
}
|
|
}
|
|
|
|
|
|
private void circle0_fill(float x, float y, float z, float r, int color) {
|
|
if (smooth) {
|
|
circle0_smooth_fill(x, y, z, r, color);
|
|
} else {
|
|
circle0_rough_fill(x, y, z, r, color);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Heavily adapted version of the above algorithm that handles
|
|
* filling the ellipse. Works by drawing from the center and
|
|
* outwards to the points themselves. Has to be done this way
|
|
* because the values for the points are changed halfway through
|
|
* the function, making it impossible to just store a series of
|
|
* left and right edges to be drawn more quickly.
|
|
*
|
|
* @param xc x center
|
|
* @param yc y center
|
|
* @param r radius
|
|
*/
|
|
private void circle0_rough_fill(float xcf, float ycf, float z,
|
|
float rf, int color) {
|
|
int xc = (int) (xcf + 0.5f);
|
|
int yc = (int) (ycf + 0.5f);
|
|
int r = (int) (rf + 0.5f);
|
|
|
|
int x = 0, y = r, u = 1, v = 2 * r - 1, E = 0;
|
|
while (x < y) {
|
|
for (int xx = xc; xx < xc + x; xx++) { // NNE
|
|
point0(xx, yc + y, z, color);
|
|
}
|
|
for (int xx = xc; xx < xc + y; xx++) { // ESE
|
|
point0(xx, yc - x, z, color);
|
|
}
|
|
for (int xx = xc - x; xx < xc; xx++) { // SSW
|
|
point0(xx, yc - y, z, color);
|
|
}
|
|
for (int xx = xc - y; xx < xc; xx++) { // WNW
|
|
point0(xx, yc + x, z, color);
|
|
}
|
|
|
|
x++; E += u; u += 2;
|
|
if (v < 2 * E) {
|
|
y--; E -= v; v -= 2;
|
|
}
|
|
if (x > y) break;
|
|
|
|
for (int xx = xc; xx < xc + y; xx++) { // ENE
|
|
point0(xx, yc + x, z, color);
|
|
}
|
|
for (int xx = xc; xx < xc + x; xx++) { // SSE
|
|
point0(xx, yc - y, z, color);
|
|
}
|
|
for (int xx = xc - y; xx < xc; xx++) { // WSW
|
|
point0(xx, yc - x, z, color);
|
|
}
|
|
for (int xx = xc - x; xx < xc; xx++) { // NNW
|
|
point0(xx, yc + y, z, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private void circle0_smooth_fill(float x, float y, float z,
|
|
float r, int color) {
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// CURVES
|
|
|
|
|
|
/**
|
|
* Evalutes quadratic bezier at point t for points a, b, c, d.
|
|
* t varies between 0 and 1, and a and d are the on curve points,
|
|
* b and c are the control points. this can be done once with the
|
|
* x coordinates and a second time with the y coordinates to get
|
|
* the location of a bezier curve at t.
|
|
*
|
|
* for instance, to convert the following example:<code>
|
|
* stroke(255, 102, 0);
|
|
* line(85, 20, 10, 10);
|
|
* line(90, 90, 15, 80);
|
|
* stroke(0, 0, 0);
|
|
* bezier(85, 20, 10, 10, 90, 90, 15, 80);
|
|
*
|
|
* // draw it in gray, using 10 steps instead of the default 20
|
|
* // this is a slower way to do it, but useful if you need
|
|
* // to do things with the coordinates at each step
|
|
* stroke(128);
|
|
* beginShape(LINE_STRIP);
|
|
* for (int i = 0; i <= 10; i++) {
|
|
* float t = i / 10.0f;
|
|
* float x = bezier(85, 10, 90, 15, t);
|
|
* float y = bezier(20, 10, 90, 80, t);
|
|
* vertex(x, y);
|
|
* }
|
|
* endShape();</code>
|
|
*/
|
|
public float bezierPoint(float a, float b, float c, float d, float t) {
|
|
float t1 = 1.0f - t;
|
|
|
|
// quadratic bezier
|
|
//return a*t1*t1 + 2*b*t*t1 + c*t*t;
|
|
|
|
// cubic bezier
|
|
//return a*t*t*t + 3*b*t*t*t1 + 3*c*t*t1*t1 + d*t1*t1*t1;
|
|
return a*t1*t1*t1 + 3*b*t*t1*t1 + 3*c*t*t*t1 + d*t*t*t;
|
|
}
|
|
|
|
|
|
/**
|
|
* Provide the tangent at the given point on the bezier curve.
|
|
* Based on code from v3ga's wordstree sketch.
|
|
*/
|
|
public float bezierTangent(float a, float b, float c, float d, float t) {
|
|
float t1 = 1.0f - t;
|
|
|
|
return (a * 3 * t*t +
|
|
b * 3 * t * (2 - 3*t) +
|
|
c * 3 * (3*t*t - 4*t + 1) +
|
|
d * -3 * t1*t1);
|
|
}
|
|
|
|
|
|
/**
|
|
* Draw a 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();
|
|
* bezierVertex(x1, y1);
|
|
* bezierVertex(x2, y2);
|
|
* bezierVertex(x3, y3);
|
|
* bezierVertex(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 bagel by adding these statements:
|
|
* curveVertex(x4, y4);
|
|
* curveVertex(x5, y5);
|
|
* curveVertex(x6, y6);
|
|
* curveVertex(x7, y7);
|
|
* Note that x4/y4 are being pulled from the previous
|
|
* curveto and used again.
|
|
*
|
|
* The solution here may be a bit more verbose than Postscript,
|
|
* but in general, decisions opted for maximum flexibility,
|
|
* since these beginShape() commands are intended as a bit lower-level.
|
|
* Rather than having many types of curveto (curve to corner,
|
|
* and several others described in the Postscript and Illustrator specs)
|
|
* let someone else implement a nice moveto/lineto/curveto library on top.
|
|
* In fact, it's tempting that we may put one in there ourselves.
|
|
*
|
|
* Another method for bezier (though not implemented this way)
|
|
* 1. first start with a call to vertex()
|
|
* 2. every three calls to bezierVertex produce a new segment
|
|
* This option seemed no good because of the confusion of mixing
|
|
* vertex and bezierVertex calls.
|
|
*/
|
|
public void bezier(float x1, float y1,
|
|
float x2, float y2,
|
|
float x3, float y3,
|
|
float x4, float y4) {
|
|
beginShape(LINE_STRIP);
|
|
bezierVertex(x1, y1);
|
|
bezierVertex(x2, y2);
|
|
bezierVertex(x3, y3);
|
|
bezierVertex(x4, y4);
|
|
endShape();
|
|
}
|
|
|
|
|
|
protected boolean bezier_inited = false;
|
|
protected int bezier_detail = 20; //BEZIER_DETAIL;
|
|
// msjvm complained when bezier_basis was final
|
|
protected float bezier_basis[][] = {
|
|
{ -1, 3, -3, 1},
|
|
{ 3, -6, 3, 0},
|
|
{ -3, 3, 0, 0},
|
|
{ 1, 0, 0, 0}
|
|
};
|
|
protected float bezier_forward[][]; // = new float[4][4];
|
|
protected float bezier_draw[][]; // = new float[4][4];
|
|
|
|
|
|
protected void bezier_init() {
|
|
bezierDetail(bezier_detail); //BEZIER_DETAIL);
|
|
//bezier_inited = true;
|
|
}
|
|
|
|
|
|
public void bezierDetail(int detail) {
|
|
if (bezier_forward == null) {
|
|
bezier_forward = new float[4][4];
|
|
bezier_draw = new float[4][4];
|
|
}
|
|
bezier_detail = detail;
|
|
bezier_inited = true;
|
|
|
|
// setup matrix for forward differencing to speed up drawing
|
|
setup_spline_forward(detail, bezier_forward);
|
|
|
|
// multiply the basis and forward diff matrices together
|
|
// saves much time since this needn't be done for each curve
|
|
mult_spline_matrix(bezier_forward, bezier_basis, bezier_draw, 4);
|
|
}
|
|
|
|
|
|
protected boolean curve_inited = false;
|
|
protected int curve_detail = 20;
|
|
// catmull-rom basis matrix, perhaps with optional s parameter
|
|
protected float curve_tightness = 0;
|
|
protected float curve_basis[][]; // = new float[4][4];
|
|
protected float curve_forward[][]; // = new float[4][4];
|
|
protected float curve_draw[][];
|
|
|
|
|
|
protected void curve_init() {
|
|
curve_mode(curve_detail, curve_tightness);
|
|
}
|
|
|
|
|
|
public void curveDetail(int detail) {
|
|
curve_mode(detail, curve_tightness);
|
|
}
|
|
|
|
|
|
public void curveTightness(float tightness) {
|
|
curve_mode(curve_detail, tightness);
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the number of segments to use when drawing a Catmull-Rom
|
|
* curve, and setting the s parameter, which defines how tightly
|
|
* the curve fits to each vertex. Catmull-Rom curves are actually
|
|
* a subset of this curve type where the s is set to zero.
|
|
*
|
|
* (This function is not optimized, since it's not expected to
|
|
* be called all that often. there are many juicy and obvious
|
|
* opimizations in here, but it's probably better to keep the
|
|
* code more readable)
|
|
*/
|
|
protected void curve_mode(int segments, float s) {
|
|
curve_detail = segments;
|
|
//curve_mode = ((curve_tightness != 0) ||
|
|
// (curve_segments != CURVE_DETAIL));
|
|
|
|
if (curve_basis == null) {
|
|
// allocate these when used, to save startup time
|
|
curve_basis = new float[4][4];
|
|
curve_forward = new float[4][4];
|
|
curve_draw = new float[4][4];
|
|
curve_inited = true;
|
|
}
|
|
|
|
float c[][] = curve_basis;
|
|
|
|
c[0][0] = s-1; c[0][1] = s+3; c[0][2] = -3-s; c[0][3] = 1-s;
|
|
c[1][0] = 2*(1-s); c[1][1] = -5-s; c[1][2] = 2*(s+2); c[1][3] = s-1;
|
|
c[2][0] = s-1; c[2][1] = 0; c[2][2] = 1-s; c[2][3] = 0;
|
|
c[3][0] = 0; c[3][1] = 2; c[3][2] = 0; c[3][3] = 0;
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
for (int j = 0; j < 4; j++) {
|
|
c[i][j] /= 2f;
|
|
}
|
|
}
|
|
setup_spline_forward(segments, curve_forward);
|
|
|
|
// multiply the basis and forward diff matrices together
|
|
// saves much time since this needn't be done for each curve
|
|
mult_spline_matrix(curve_forward, curve_basis, curve_draw, 4);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get a location along a catmull-rom curve segment.
|
|
*
|
|
* @param t Value between zero and one for how far along the segment
|
|
*/
|
|
public float curvePoint(float a, float b, float c, float d, float t) {
|
|
if (!curve_inited) curve_init();
|
|
|
|
float tt = t * t;
|
|
float ttt = t * tt;
|
|
float m[][] = curve_basis;
|
|
|
|
// not optimized (and probably need not be)
|
|
return (a * (ttt*m[0][0] + tt*m[1][0] + t*m[2][0] + m[3][0]) +
|
|
b * (ttt*m[0][1] + tt*m[1][1] + t*m[2][1] + m[3][1]) +
|
|
c * (ttt*m[0][2] + tt*m[1][2] + t*m[2][2] + m[3][2]) +
|
|
d * (ttt*m[0][3] + tt*m[1][3] + t*m[2][3] + m[3][3]));
|
|
}
|
|
|
|
|
|
public float curveTangent(float a, float b, float c, float d,
|
|
float t) {
|
|
System.err.println("curveTangent not yet implemented");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Draws a segment of Catmull-Rom curve.
|
|
*
|
|
* Identical to typing out:
|
|
* beginShape();
|
|
* curveVertex(x1, y1);
|
|
* curveVertex(x2, y2);
|
|
* curveVertex(x3, y3);
|
|
* curveVertex(x4, y4);
|
|
* endShape();
|
|
*
|
|
* As of 0070, this function no longer doubles the first and
|
|
* last points. The curves are a bit more boring, but it's more
|
|
* mathematically correct, and properly mirrored in curvePoint().
|
|
*/
|
|
public void curve(float x1, float y1,
|
|
float x2, float y2,
|
|
float x3, float y3,
|
|
float x4, float y4) {
|
|
beginShape(LINE_STRIP);
|
|
curveVertex(x1, y1);
|
|
curveVertex(x2, y2);
|
|
curveVertex(x3, y3);
|
|
curveVertex(x4, y4);
|
|
endShape();
|
|
}
|
|
|
|
|
|
public void curve(float x1, float y1, float z1,
|
|
float x2, float y2, float z2,
|
|
float x3, float y3, float z3,
|
|
float x4, float y4, float z4) {
|
|
beginShape(LINE_STRIP);
|
|
curveVertex(x1, y1, z1);
|
|
curveVertex(x2, y2, z2);
|
|
curveVertex(x3, y3, z3);
|
|
curveVertex(x4, y4, z4);
|
|
endShape();
|
|
}
|
|
|
|
|
|
/**
|
|
* Setup forward-differencing matrix to be used for speedy
|
|
* curve rendering. It's based on using a specific number
|
|
* of curve segments and just doing incremental adds for each
|
|
* vertex of the segment, rather than running the mathematically
|
|
* expensive cubic equation.
|
|
* @param segments number of curve segments to use when drawing
|
|
*/
|
|
protected void setup_spline_forward(int segments, float fwd[][]) {
|
|
float f = 1.0f / segments;
|
|
float ff = f * f;
|
|
float fff = ff * f;
|
|
|
|
fwd[0][0] = 0; fwd[0][1] = 0; fwd[0][2] = 0; fwd[0][3] = 1;
|
|
fwd[1][0] = fff; fwd[1][1] = ff; fwd[1][2] = f; fwd[1][3] = 0;
|
|
fwd[2][0] = 6*fff; fwd[2][1] = 2*ff; fwd[2][2] = 0; fwd[2][3] = 0;
|
|
fwd[3][0] = 6*fff; fwd[3][1] = 0; fwd[3][2] = 0; fwd[3][3] = 0;
|
|
}
|
|
|
|
|
|
// internal matrix multiplication routine used by the spline code
|
|
// should these go to 4 instead of 3?
|
|
//void mult_curve_matrix(float m[4][4], float g[4][3], float mg[4][3]);
|
|
protected void mult_spline_matrix(float m[][], float g[][],
|
|
float mg[][], int dimensions) {
|
|
for (int i = 0; i < 4; i++) {
|
|
for (int j = 0; j < dimensions; j++) {
|
|
mg[i][j] = 0;
|
|
}
|
|
}
|
|
for (int i = 0; i < 4; i++) {
|
|
for (int j = 0; j < dimensions; j++) {
|
|
for (int k = 0; k < 4; k++) {
|
|
mg[i][j] = mg[i][j] + (m[i][k] * g[k][j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Draw a segment of spline (bezier or catmull-rom curve)
|
|
* using the matrix m, which is the basis matrix already
|
|
* multiplied with the forward differencing matrix.
|
|
*
|
|
* the x0, y0, z0 points are the point that's being used as
|
|
* the start, and also as the accumulator. for bezier curves,
|
|
* the x1, y1, z1 are the first point drawn, and added to.
|
|
* for catmull-rom curves, the first control point (x2, y2, z2)
|
|
* is the first drawn point, and is accumulated to.
|
|
*/
|
|
protected void spline2_segment(int offset, int start,
|
|
float m[][], int segments) {
|
|
float x1 = splineVertices[offset][MX];
|
|
float y1 = splineVertices[offset][MY];
|
|
|
|
float x2 = splineVertices[offset+1][MX];
|
|
float y2 = splineVertices[offset+1][MY];
|
|
|
|
float x3 = splineVertices[offset+2][MX];
|
|
float y3 = splineVertices[offset+2][MY];
|
|
|
|
float x4 = splineVertices[offset+3][MX];
|
|
float y4 = splineVertices[offset+3][MY];
|
|
|
|
float x0 = splineVertices[start][MX];
|
|
float y0 = splineVertices[start][MY];
|
|
|
|
float xplot1 = m[1][0]*x1 + m[1][1]*x2 + m[1][2]*x3 + m[1][3]*x4;
|
|
float xplot2 = m[2][0]*x1 + m[2][1]*x2 + m[2][2]*x3 + m[2][3]*x4;
|
|
float xplot3 = m[3][0]*x1 + m[3][1]*x2 + m[3][2]*x3 + m[3][3]*x4;
|
|
|
|
float yplot1 = m[1][0]*y1 + m[1][1]*y2 + m[1][2]*y3 + m[1][3]*y4;
|
|
float yplot2 = m[2][0]*y1 + m[2][1]*y2 + m[2][2]*y3 + m[2][3]*y4;
|
|
float yplot3 = m[3][0]*y1 + m[3][1]*y2 + m[3][2]*y3 + m[3][3]*y4;
|
|
|
|
// vertex() will reset splineVertexCount, so save it
|
|
int splineVertexSaved = splineVertexCount;
|
|
vertex(x0, y0);
|
|
for (int j = 0; j < segments; j++) {
|
|
x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
|
|
y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
|
|
vertex(x0, y0);
|
|
}
|
|
splineVertexCount = splineVertexSaved;
|
|
}
|
|
|
|
|
|
protected void spline3_segment(int offset, int start,
|
|
float m[][], int segments) {
|
|
float x1 = splineVertices[offset+0][MX];
|
|
float y1 = splineVertices[offset+0][MY];
|
|
float z1 = splineVertices[offset+0][MZ];
|
|
|
|
float x2 = splineVertices[offset+1][MX];
|
|
float y2 = splineVertices[offset+1][MY];
|
|
float z2 = splineVertices[offset+1][MZ];
|
|
|
|
float x3 = splineVertices[offset+2][MX];
|
|
float y3 = splineVertices[offset+2][MY];
|
|
float z3 = splineVertices[offset+2][MZ];
|
|
|
|
float x4 = splineVertices[offset+3][MX];
|
|
float y4 = splineVertices[offset+3][MY];
|
|
float z4 = splineVertices[offset+3][MZ];
|
|
|
|
float x0 = splineVertices[start][MX];
|
|
float y0 = splineVertices[start][MY];
|
|
float z0 = splineVertices[start][MZ];
|
|
|
|
float xplot1 = m[1][0]*x1 + m[1][1]*x2 + m[1][2]*x3 + m[1][3]*x4;
|
|
float xplot2 = m[2][0]*x1 + m[2][1]*x2 + m[2][2]*x3 + m[2][3]*x4;
|
|
float xplot3 = m[3][0]*x1 + m[3][1]*x2 + m[3][2]*x3 + m[3][3]*x4;
|
|
|
|
float yplot1 = m[1][0]*y1 + m[1][1]*y2 + m[1][2]*y3 + m[1][3]*y4;
|
|
float yplot2 = m[2][0]*y1 + m[2][1]*y2 + m[2][2]*y3 + m[2][3]*y4;
|
|
float yplot3 = m[3][0]*y1 + m[3][1]*y2 + m[3][2]*y3 + m[3][3]*y4;
|
|
|
|
float zplot1 = m[1][0]*z1 + m[1][1]*z2 + m[1][2]*z3 + m[1][3]*z4;
|
|
float zplot2 = m[2][0]*z1 + m[2][1]*z2 + m[2][2]*z3 + m[2][3]*z4;
|
|
float zplot3 = m[3][0]*z1 + m[3][1]*z2 + m[3][2]*z3 + m[3][3]*z4;
|
|
|
|
//unchangedZ = false;
|
|
//dimensions = 3;
|
|
|
|
// vertex() will reset splineVertexCount, so save it
|
|
int cvertexSaved = splineVertexCount;
|
|
vertex(x0, y0, z0);
|
|
for (int j = 0; j < segments; j++) {
|
|
x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
|
|
y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
|
|
z0 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
|
|
vertex(x0, y0, z0);
|
|
}
|
|
splineVertexCount = cvertexSaved;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// TEXT/FONTS
|
|
|
|
|
|
public void textFont(PFont which) {
|
|
if (which != null) {
|
|
textFont = which;
|
|
textFont.resetSize();
|
|
textFont.resetLeading();
|
|
|
|
} else {
|
|
System.err.println("Ignoring improperly loaded font in textFont()");
|
|
}
|
|
}
|
|
|
|
|
|
public void textSize(float size) {
|
|
if (textFont != null) {
|
|
textFont.size(size);
|
|
|
|
} else {
|
|
System.err.println("First set a font before setting its size.");
|
|
}
|
|
}
|
|
|
|
|
|
public void textFont(PFont which, float size) {
|
|
textFont(which);
|
|
textSize(size);
|
|
}
|
|
|
|
|
|
public void textLeading(float leading) {
|
|
if (textFont != null) {
|
|
textFont.leading(leading);
|
|
|
|
} else {
|
|
System.err.println("First set a font before setting its leading.");
|
|
}
|
|
}
|
|
|
|
|
|
public void textMode(int mode) {
|
|
if (textFont != null) {
|
|
textFont.align(mode);
|
|
|
|
} else {
|
|
System.err.println("First set a font before setting its mode.");
|
|
}
|
|
}
|
|
|
|
|
|
public void textSpace(int space) {
|
|
if (textFont != null) {
|
|
textFont.space(space);
|
|
|
|
} else {
|
|
System.err.println("First set a font before setting the space.");
|
|
}
|
|
}
|
|
|
|
|
|
public void text(char c, float x, float y) {
|
|
text(c, x, y, 0);
|
|
}
|
|
|
|
public void text(char c, float x, float y, float z) {
|
|
if (textFont != null) {
|
|
textFont.text(c, x, y, z, this);
|
|
|
|
} else {
|
|
System.err.println("text(): first set a font before drawing text");
|
|
}
|
|
}
|
|
|
|
|
|
public void text(String s, float x, float y) {
|
|
text(s, x, y, 0);
|
|
}
|
|
|
|
public void text(String s, float x, float y, float z) {
|
|
if (textFont != null) {
|
|
textFont.text(s, x, y, z, this);
|
|
|
|
} else {
|
|
System.err.println("text(): first set a font before drawing text");
|
|
}
|
|
}
|
|
|
|
|
|
public void text(String s, float x, float y, float w, float h) {
|
|
text(s, x, y, 0, w, h);
|
|
}
|
|
|
|
public void text(String s, float x1, float y1, float z, float x2, float y2) {
|
|
if (textFont != null) {
|
|
float hradius, vradius;
|
|
switch (rectMode) {
|
|
case CORNER:
|
|
x2 += x1; y2 += y1;
|
|
break;
|
|
case CENTER_RADIUS:
|
|
hradius = x2;
|
|
vradius = y2;
|
|
x2 = x1 + hradius;
|
|
y2 = y1 + vradius;
|
|
x1 -= hradius;
|
|
y1 -= vradius;
|
|
break;
|
|
case CENTER:
|
|
hradius = x2 / 2.0f;
|
|
vradius = y2 / 2.0f;
|
|
x2 = x1 + hradius;
|
|
y2 = y1 + vradius;
|
|
x1 -= hradius;
|
|
y1 -= vradius;
|
|
}
|
|
if (x2 < x1) {
|
|
float temp = x1; x1 = x2; x2 = temp;
|
|
}
|
|
if (y2 < y1) {
|
|
float temp = y1; y1 = y2; y2 = temp;
|
|
}
|
|
textFont.text(s, x1, y1, z, x2, y2, this);
|
|
|
|
} else {
|
|
System.err.println("text(): first set a font before drawing text");
|
|
}
|
|
}
|
|
|
|
|
|
public void text(int num, float x, float y) {
|
|
text(String.valueOf(num), x, y, 0);
|
|
}
|
|
|
|
public void text(int num, float x, float y, float z) {
|
|
text(String.valueOf(num), x, y, z);
|
|
}
|
|
|
|
|
|
/**
|
|
* See three-dimensional version of the same function, below.
|
|
*/
|
|
public void text(float num, float x, float y) {
|
|
text(PApplet.nfs(num, 0, 3), x, y, 0);
|
|
}
|
|
|
|
/**
|
|
* This does a basic number formatting, to avoid the
|
|
* generally ugly appearance of printing floats.
|
|
* Users who want more control should use their own nfs() cmmand,
|
|
* or if they want the long, ugly version of float,
|
|
* use String.valueOf() to convert the float to a String first.
|
|
*/
|
|
public void text(float num, float x, float y, float z) {
|
|
text(PApplet.nf(num, 0, 3), x, y, z);
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// MATRIX TRANSFORMATIONS
|
|
|
|
|
|
public void translate(float tx, float ty) {
|
|
m02 += tx*m00 + ty*m01 + m02;
|
|
m12 += tx*m10 + ty*m11 + m12;
|
|
}
|
|
|
|
|
|
//public void translate(float tx, float ty, float tz) {
|
|
//}
|
|
|
|
|
|
//public void rotateX(float angle) {
|
|
//}
|
|
|
|
//public void rotateY(float angle) {
|
|
//}
|
|
|
|
//public void rotateZ(float angle) {
|
|
//}
|
|
|
|
|
|
/**
|
|
* Two dimensional rotation. Same as rotateZ (this is identical
|
|
* to a 3D rotation along the z-axis) but included for clarity --
|
|
* it'd be weird for people drawing 2D graphics to be using rotateZ.
|
|
* And they might kick our a-- for the confusion.
|
|
*/
|
|
public void rotate(float angle) {
|
|
float c = cos(angle);
|
|
float s = sin(angle);
|
|
applyMatrix(c, -s, 0, s, c, 0);
|
|
}
|
|
|
|
|
|
//public void rotate(float angle, float vx, float vy, float vz) {
|
|
//}
|
|
|
|
|
|
/**
|
|
* This will scale in all three dimensions, but not set the
|
|
* dimensions higher than two, in case this is still 2D mode.
|
|
*/
|
|
public void scale(float s) {
|
|
applyMatrix(s, 0, 0,
|
|
0, s, 0);
|
|
}
|
|
|
|
|
|
public void scale(float sx, float sy) {
|
|
applyMatrix(sx, 0, 0,
|
|
0, sy, 0);
|
|
}
|
|
|
|
|
|
//public void scale(float x, float y, float z) {
|
|
//}
|
|
|
|
|
|
/*
|
|
public void transform(float n00, float n01, float n02, float n03,
|
|
float n10, float n11, float n12, float n13,
|
|
float n20, float n21, float n22, float n23,
|
|
float n30, float n31, float n32, float n33) {
|
|
//dimensions = 3;
|
|
applyMatrix(n00, n01, n02, n03, n10, n11, n12, n13,
|
|
n20, n21, n22, n23, n30, n31, n32, n33);
|
|
}
|
|
*/
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// TRANSFORMATION MATRIX
|
|
|
|
|
|
public void push() {
|
|
if (matrixStackDepth+1 == MATRIX_STACK_DEPTH) {
|
|
message(COMPLAINT, "matrix stack overflow, to much pushmatrix");
|
|
return;
|
|
}
|
|
float mat[] = matrixStack[matrixStackDepth];
|
|
mat[0] = m00; mat[1] = m01; mat[2] = m02;
|
|
mat[3] = m10; mat[4] = m11; mat[5] = m12;
|
|
matrixStackDepth++;
|
|
}
|
|
|
|
|
|
public void pop() {
|
|
if (matrixStackDepth == 0) {
|
|
message(COMPLAINT, "matrix stack underflow, to many popmatrix");
|
|
return;
|
|
}
|
|
matrixStackDepth--;
|
|
float mat[] = matrixStack[matrixStackDepth];
|
|
m00 = mat[0]; m01 = mat[1]; m02 = mat[2];
|
|
m10 = mat[3]; m11 = mat[4]; m12 = mat[5];
|
|
}
|
|
|
|
|
|
/**
|
|
* Load identity as the transform/model matrix.
|
|
* Same as glLoadIdentity().
|
|
*/
|
|
public void resetMatrix() {
|
|
m00 = 1; m01 = 0; m02 = 0;
|
|
m10 = 0; m11 = 1; m12 = 0;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Apply a 3x2 affine transformation matrix.
|
|
*/
|
|
public void applyMatrix(float n00, float n01, float n02,
|
|
float n10, float n11, float n12) {
|
|
|
|
float r00 = m00*n00 + m01*n10;
|
|
float r01 = m00*n01 + m01*n11;
|
|
float r02 = m00*n02 + m01*n12 + m02;
|
|
|
|
float r10 = m10*n00 + m11*n10;
|
|
float r11 = m10*n01 + m11*n11;
|
|
float r12 = m10*n02 + m11*n12 + m12;
|
|
|
|
m00 = r00; m01 = r01; m02 = r02;
|
|
m10 = r10; m11 = r11; m12 = r12;
|
|
}
|
|
|
|
|
|
/**
|
|
* Print the current model (or "transformation") matrix.
|
|
*/
|
|
public void printMatrix() {
|
|
int big = (int) abs(max(max(abs(m00), abs(m01), abs(m02)),
|
|
max(abs(m10), abs(m11), abs(m12))));
|
|
|
|
int d = 1;
|
|
while ((big /= 10) != 0) d++; // cheap log()
|
|
|
|
System.out.println(PApplet.nfs(m00, d, 4) + " " +
|
|
PApplet.nfs(m01, d, 4) + " " +
|
|
PApplet.nfs(m02, d, 4));
|
|
|
|
System.out.println(PApplet.nfs(m10, d, 4) + " " +
|
|
PApplet.nfs(m11, d, 4) + " " +
|
|
PApplet.nfs(m12, d, 4));
|
|
|
|
System.out.println();
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// CAMERA
|
|
|
|
|
|
public float screenX(float x, float y) {
|
|
return m00*x + m01*y + m02;
|
|
}
|
|
|
|
|
|
public float screenY(float x, float y) {
|
|
return m10*x + m11*y + m12;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// COLOR
|
|
|
|
|
|
public void colorMode(int mode) {
|
|
colorMode = mode;
|
|
}
|
|
|
|
|
|
public void colorMode(int mode, float max) {
|
|
colorMode(mode, max, max, max, max);
|
|
}
|
|
|
|
|
|
// note that this doesn't set the alpha color max..
|
|
// so colorMode(RGB, 255, 255, 255) would retain the previous max alpha
|
|
// could be a problem when colorMode(HSB, 360, 100, 100);
|
|
|
|
public void colorMode(int mode,
|
|
float maxX, float maxY, float maxZ) {
|
|
colorMode(mode, maxX, maxY, maxZ, colorModeA); //maxX); //ONE);
|
|
}
|
|
|
|
|
|
public void colorMode(int mode,
|
|
float maxX, float maxY, float maxZ, float maxA) {
|
|
colorMode = mode;
|
|
|
|
colorModeX = maxX; // still needs to be set for hsb
|
|
colorModeY = maxY;
|
|
colorModeZ = maxZ;
|
|
colorModeA = maxA;
|
|
|
|
// if color max values are all 1, then no need to scale
|
|
colorScale = ((maxA != ONE) || (maxX != maxY) ||
|
|
(maxY != maxZ) || (maxZ != maxA));
|
|
|
|
// if color is rgb/0..255 this will make it easier for the
|
|
// red() green() etc functions
|
|
colorRgb255 = (colorMode == RGB) &&
|
|
(colorModeA == 255) && (colorModeX == 255) &&
|
|
(colorModeY == 255) && (colorModeZ == 255);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
protected void calc_color(float gray) {
|
|
calc_color(gray, colorModeA);
|
|
}
|
|
|
|
|
|
protected void calc_color(float gray, float alpha) {
|
|
if (gray > colorModeX) gray = colorModeX;
|
|
if (alpha > colorModeA) alpha = colorModeA;
|
|
|
|
if (gray < 0) gray = 0;
|
|
if (alpha < 0) alpha = 0;
|
|
|
|
calcR = colorScale ? (gray / colorModeX) : gray;
|
|
calcG = calcR;
|
|
calcB = calcR;
|
|
calcA = colorScale ? (alpha / colorModeA) : alpha;
|
|
|
|
calcRi = (int)(calcR*255); calcGi = (int)(calcG*255);
|
|
calcBi = (int)(calcB*255); calcAi = (int)(calcA*255);
|
|
calcColor = (calcAi << 24) | (calcRi << 16) | (calcGi << 8) | calcBi;
|
|
calcAlpha = (calcAi != 255);
|
|
}
|
|
|
|
|
|
protected void calc_color(float x, float y, float z) {
|
|
calc_color(x, y, z, colorModeA);
|
|
}
|
|
|
|
|
|
protected void calc_color(float x, float y, float z, float a) {
|
|
if (x > colorModeX) x = colorModeX;
|
|
if (y > colorModeY) y = colorModeY;
|
|
if (z > colorModeZ) z = colorModeZ;
|
|
if (a > colorModeA) a = colorModeA;
|
|
|
|
if (x < 0) x = 0;
|
|
if (y < 0) y = 0;
|
|
if (z < 0) z = 0;
|
|
if (a < 0) a = 0;
|
|
|
|
switch (colorMode) {
|
|
case RGB:
|
|
if (colorScale) {
|
|
calcR = x / colorModeX;
|
|
calcG = y / colorModeY;
|
|
calcB = z / colorModeZ;
|
|
calcA = a / colorModeA;
|
|
} else {
|
|
calcR = x; calcG = y; calcB = z; calcA = a;
|
|
}
|
|
break;
|
|
|
|
case HSB:
|
|
x /= colorModeX; // h
|
|
y /= colorModeY; // s
|
|
z /= colorModeZ; // b
|
|
|
|
calcA = colorScale ? (a/colorModeA) : a;
|
|
|
|
if (y == 0) { // saturation == 0
|
|
calcR = calcG = calcB = z;
|
|
|
|
} else {
|
|
float which = (x - (int)x) * 6.0f;
|
|
float f = which - (int)which;
|
|
float p = z * (1.0f - y);
|
|
float q = z * (1.0f - y * f);
|
|
float t = z * (1.0f - (y * (1.0f - f)));
|
|
|
|
switch ((int)which) {
|
|
case 0: calcR = z; calcG = t; calcB = p; break;
|
|
case 1: calcR = q; calcG = z; calcB = p; break;
|
|
case 2: calcR = p; calcG = z; calcB = t; break;
|
|
case 3: calcR = p; calcG = q; calcB = z; break;
|
|
case 4: calcR = t; calcG = p; calcB = z; break;
|
|
case 5: calcR = z; calcG = p; calcB = q; break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
calcRi = (int)(255*calcR); calcGi = (int)(255*calcG);
|
|
calcBi = (int)(255*calcB); calcAi = (int)(255*calcA);
|
|
calcColor = (calcAi << 24) | (calcRi << 16) | (calcGi << 8) | calcBi;
|
|
calcAlpha = (calcAi != 255);
|
|
}
|
|
|
|
|
|
/**
|
|
* unpacks AARRGGBB color for direct use with calc_color.
|
|
* 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 bounds check since it's a 32 bit number)
|
|
*/
|
|
protected void calc_color_from(int argb) {
|
|
calcColor = argb;
|
|
calcAi = (argb >> 24) & 0xff;
|
|
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);
|
|
}
|
|
|
|
|
|
protected void calc_tint() {
|
|
tint = true;
|
|
tintR = calcR;
|
|
tintG = calcG;
|
|
tintB = calcB;
|
|
tintA = calcA;
|
|
tintRi = calcRi;
|
|
tintGi = calcGi;
|
|
tintBi = calcBi;
|
|
tintAi = calcAi;
|
|
tintColor = calcColor;
|
|
tintAlpha = calcAlpha;
|
|
}
|
|
|
|
|
|
protected void calc_fill() {
|
|
fill = true;
|
|
//fillChanged = true;
|
|
fillR = calcR;
|
|
fillG = calcG;
|
|
fillB = calcB;
|
|
fillA = calcA;
|
|
fillRi = calcRi;
|
|
fillGi = calcGi;
|
|
fillBi = calcBi;
|
|
fillAi = calcAi;
|
|
fillColor = calcColor;
|
|
fillAlpha = calcAlpha;
|
|
}
|
|
|
|
|
|
protected void calc_stroke() {
|
|
stroke = true;
|
|
//strokeChanged = true;
|
|
strokeR = calcR;
|
|
strokeG = calcG;
|
|
strokeB = calcB;
|
|
strokeA = calcA;
|
|
strokeRi = calcRi;
|
|
strokeGi = calcGi;
|
|
strokeBi = calcBi;
|
|
strokeAi = calcAi;
|
|
strokeColor = calcColor;
|
|
strokeAlpha = calcAlpha;
|
|
}
|
|
|
|
|
|
protected void calc_background() {
|
|
backgroundR = calcR;
|
|
backgroundG = calcG;
|
|
backgroundB = calcB;
|
|
backgroundRi = calcRi;
|
|
backgroundGi = calcGi;
|
|
backgroundBi = calcBi;
|
|
backgroundColor = calcColor;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
public void noTint() {
|
|
tint = false;
|
|
}
|
|
|
|
|
|
// if high bit isn't set, then it's not a #ffcc00 style web color
|
|
// so redirect to the float version, b/c they want a gray.
|
|
// only danger is that someone would try to set the color to a
|
|
// zero alpha.. which would be kooky but not unlikely
|
|
// (i.e. if it were in a loop) so in addition to checking the high
|
|
// bit, check to see if the value is at least just below the
|
|
// colorModeX (i.e. 0..255). can't just check the latter since
|
|
// if the high bit is > 0x80 then the int value for rgb will be
|
|
// negative. yay for no unsigned types in java!
|
|
|
|
public void tint(int rgb) {
|
|
if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) {
|
|
tint((float) rgb);
|
|
|
|
} else {
|
|
calc_color_from(rgb);
|
|
calc_tint();
|
|
}
|
|
}
|
|
|
|
public void tint(float gray) {
|
|
calc_color(gray);
|
|
calc_tint();
|
|
}
|
|
|
|
|
|
public void tint(float gray, float alpha) {
|
|
calc_color(gray, alpha);
|
|
calc_tint();
|
|
}
|
|
|
|
|
|
public void tint(float x, float y, float z) {
|
|
calc_color(x, y, z);
|
|
calc_tint();
|
|
}
|
|
|
|
|
|
public void tint(float x, float y, float z, float a) {
|
|
calc_color(x, y, z, a);
|
|
calc_tint();
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
public void noFill() {
|
|
fill = false;
|
|
}
|
|
|
|
|
|
public void fill(int rgb) {
|
|
if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
|
|
fill((float) rgb);
|
|
|
|
} else {
|
|
calc_color_from(rgb);
|
|
calc_fill();
|
|
}
|
|
}
|
|
|
|
public void fill(float gray) {
|
|
calc_color(gray);
|
|
calc_fill();
|
|
}
|
|
|
|
|
|
public void fill(float gray, float alpha) {
|
|
calc_color(gray, alpha);
|
|
calc_fill();
|
|
}
|
|
|
|
|
|
public void fill(float x, float y, float z) {
|
|
calc_color(x, y, z);
|
|
calc_fill();
|
|
}
|
|
|
|
|
|
public void fill(float x, float y, float z, float a) {
|
|
calc_color(x, y, z, a);
|
|
calc_fill();
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
public void strokeWeight(float weight) {
|
|
strokeWeight = weight;
|
|
}
|
|
|
|
|
|
public void strokeJoin(int join) {
|
|
strokeJoin = join;
|
|
}
|
|
|
|
|
|
public void strokeCap(int cap) {
|
|
strokeCap = cap;
|
|
}
|
|
|
|
|
|
public void noStroke() {
|
|
stroke = false;
|
|
}
|
|
|
|
|
|
public void stroke(int rgb) {
|
|
if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
|
|
stroke((float) rgb);
|
|
|
|
} else {
|
|
calc_color_from(rgb);
|
|
calc_stroke();
|
|
}
|
|
}
|
|
|
|
|
|
public void stroke(float gray) {
|
|
calc_color(gray);
|
|
calc_stroke();
|
|
}
|
|
|
|
|
|
public void stroke(float gray, float alpha) {
|
|
calc_color(gray, alpha);
|
|
calc_stroke();
|
|
}
|
|
|
|
|
|
public void stroke(float x, float y, float z) {
|
|
calc_color(x, y, z);
|
|
calc_stroke();
|
|
}
|
|
|
|
|
|
public void stroke(float x, float y, float z, float a) {
|
|
calc_color(x, y, z, a);
|
|
calc_stroke();
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
public void background(int rgb) {
|
|
if (((rgb & 0xff000000) == 0) && (rgb <= colorModeX)) { // see above
|
|
background((float) rgb);
|
|
|
|
} else {
|
|
calc_color_from(rgb);
|
|
calc_background();
|
|
}
|
|
clear();
|
|
}
|
|
|
|
|
|
public void background(float gray) {
|
|
calc_color(gray);
|
|
calc_background();
|
|
clear();
|
|
}
|
|
|
|
|
|
public void background(float x, float y, float z) {
|
|
calc_color(x, y, z);
|
|
calc_background();
|
|
clear();
|
|
}
|
|
|
|
|
|
/**
|
|
* Takes an RGB or RGBA image and sets it as the background.
|
|
*
|
|
* Note that even if the image is set as RGB, the high 8 bits of
|
|
* each pixel must be set (0xFF000000), because the image data will
|
|
* be copied directly to the screen.
|
|
*
|
|
* Also clears out the zbuffer and stencil buffer if they exist.
|
|
*/
|
|
public void background(PImage image) {
|
|
if ((image.width != width) || (image.height != height)) {
|
|
die("background image must be the same size " +
|
|
"as your application");
|
|
}
|
|
if ((image.format != RGB) && (image.format != ARGB)) {
|
|
die("background images should be RGB or ARGB");
|
|
}
|
|
|
|
// blit image to the screen
|
|
System.arraycopy(image.pixels, 0, pixels, 0, pixels.length);
|
|
}
|
|
|
|
|
|
/**
|
|
* Clears pixel buffer. Also clears the stencil and zbuffer
|
|
* if they exist. Their existence is more accurate than using 'depth'
|
|
* to test whether to clear them, because if they're non-null,
|
|
* it means that depth() has been called somewhere in the program,
|
|
* even if noDepth() was called before draw() exited.
|
|
*/
|
|
public void clear() {
|
|
for (int i = 0; i < pixelCount; i++) {
|
|
pixels[i] = backgroundColor;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// HINTS
|
|
|
|
// for the most part, hints are temporary api quirks,
|
|
// for which a proper api hasn't been properly worked out.
|
|
// for instance SMOOTH_IMAGES existed because smooth()
|
|
// wasn't yet implemented, but it will soon go away.
|
|
|
|
// they also exist for obscure features in the graphics
|
|
// engine, like enabling/disabling single pixel lines
|
|
// that ignore the zbuffer, the way they do in alphabot.
|
|
|
|
public void hint(int which) {
|
|
hints[which] = true;
|
|
}
|
|
|
|
public void unhint(int which) {
|
|
hints[which] = false;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// MESSAGES / ERRORS / LOGGING
|
|
|
|
|
|
public void message(int level, String message) { // ignore
|
|
switch (level) {
|
|
case CHATTER:
|
|
//System.err.println("bagel chatter: " + message);
|
|
break;
|
|
case COMPLAINT:
|
|
System.err.println("bagel complaint: " + message);
|
|
break;
|
|
case PROBLEM:
|
|
System.err.println("bagel problem: " + message);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void message(int level, String message, Exception e) { // ignore
|
|
message(level, message);
|
|
e.printStackTrace();
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// PIXELS
|
|
|
|
// these functions are really slow, but easy to use
|
|
// if folks are advanced enough to want something faster,
|
|
// they can write it themselves (not difficult)
|
|
|
|
|
|
public final int color(int gray) { // ignore
|
|
if (colorRgb255) {
|
|
// bounds checking to make sure the numbers aren't to high or low
|
|
if (gray > 255) gray = 255; else if (gray < 0) gray = 0;
|
|
return 0xff000000 | (gray << 16) | (gray << 8) | gray;
|
|
}
|
|
calc_color(gray);
|
|
return calcColor;
|
|
}
|
|
|
|
public final int color(float gray) { // ignore
|
|
calc_color(gray);
|
|
return calcColor;
|
|
}
|
|
|
|
|
|
public final int color(int gray, int alpha) { // ignore
|
|
if (colorRgb255) {
|
|
// bounds checking to make sure the numbers aren't to high or low
|
|
if (gray > 255) gray = 255; else if (gray < 0) gray = 0;
|
|
if (alpha > 255) alpha = 255; else if (alpha < 0) alpha = 0;
|
|
|
|
return ((alpha & 0xff) << 24) | (gray << 16) | (gray << 8) | gray;
|
|
}
|
|
calc_color(gray, alpha);
|
|
return calcColor;
|
|
}
|
|
|
|
public final int color(float gray, float alpha) { // ignore
|
|
calc_color(gray, alpha);
|
|
return calcColor;
|
|
}
|
|
|
|
|
|
public final int color(int x, int y, int z) { // ignore
|
|
if (colorRgb255) {
|
|
// bounds checking to make sure the numbers aren't to high or low
|
|
if (x > 255) x = 255; else if (x < 0) x = 0;
|
|
if (y > 255) y = 255; else if (y < 0) y = 0;
|
|
if (z > 255) z = 255; else if (z < 0) z = 0;
|
|
|
|
return 0xff000000 | (x << 16) | (y << 8) | z;
|
|
}
|
|
calc_color(x, y, z);
|
|
return calcColor;
|
|
}
|
|
|
|
public final int color(float x, float y, float z) { // ignore
|
|
calc_color(x, y, z);
|
|
return calcColor;
|
|
}
|
|
|
|
|
|
public final int color(int x, int y, int z, int a) { // ignore
|
|
if (colorRgb255) {
|
|
// bounds checking to make sure the numbers aren't to high or low
|
|
if (a > 255) a = 255; else if (a < 0) a = 0;
|
|
if (x > 255) x = 255; else if (x < 0) x = 0;
|
|
if (y > 255) y = 255; else if (y < 0) y = 0;
|
|
if (z > 255) z = 255; else if (z < 0) z = 0;
|
|
|
|
return (a << 24) | (x << 16) | (y << 8) | z;
|
|
}
|
|
calc_color(x, y, z, a);
|
|
return calcColor;
|
|
}
|
|
|
|
public final int color(float x, float y, float z, float a) { // ignore
|
|
calc_color(x, y, z, a);
|
|
return calcColor;
|
|
}
|
|
|
|
|
|
public final float alpha(int what) {
|
|
float c = (what >> 24) & 0xff;
|
|
if (colorModeA == 255) return c;
|
|
return (c / 255.0f) * colorModeA;
|
|
}
|
|
|
|
public final float red(int what) {
|
|
float c = (what >> 16) & 0xff;
|
|
if (colorRgb255) return c;
|
|
return (c / 255.0f) * colorModeX;
|
|
}
|
|
|
|
public final float green(int what) {
|
|
float c = (what >> 8) & 0xff;
|
|
if (colorRgb255) return c;
|
|
return (c / 255.0f) * colorModeY;
|
|
}
|
|
|
|
public final float blue(int what) {
|
|
float c = (what) & 0xff;
|
|
if (colorRgb255) return c;
|
|
return (c / 255.0f) * colorModeZ;
|
|
}
|
|
|
|
|
|
public final float hue(int what) {
|
|
if (what != cacheHsbKey) {
|
|
Color.RGBtoHSB((what >> 16) & 0xff, (what >> 8) & 0xff,
|
|
what & 0xff, cacheHsbValue);
|
|
cacheHsbKey = what;
|
|
}
|
|
return cacheHsbValue[0] * colorModeX;
|
|
}
|
|
|
|
public final float saturation(int what) {
|
|
if (what != cacheHsbKey) {
|
|
Color.RGBtoHSB((what >> 16) & 0xff, (what >> 8) & 0xff,
|
|
what & 0xff, cacheHsbValue);
|
|
cacheHsbKey = what;
|
|
}
|
|
return cacheHsbValue[1] * colorModeY;
|
|
}
|
|
|
|
public final float brightness(int what) {
|
|
if (what != cacheHsbKey) {
|
|
Color.RGBtoHSB((what >> 16) & 0xff, (what >> 8) & 0xff,
|
|
what & 0xff, cacheHsbValue);
|
|
cacheHsbKey = what;
|
|
}
|
|
return cacheHsbValue[2] * colorModeZ;
|
|
}
|
|
|
|
|
|
public final static int _blend(int p1, int p2, int a2) {
|
|
// scale alpha by alpha of incoming pixel
|
|
a2 = (a2 * (p2 >>> 24)) >> 8;
|
|
|
|
int a1 = a2 ^ 0xff;
|
|
int r = (a1 * ((p1 >> 16) & 0xff) + a2 * ((p2 >> 16) & 0xff)) & 0xff00;
|
|
int g = (a1 * ((p1 >> 8) & 0xff) + a2 * ((p2 >> 8) & 0xff)) & 0xff00;
|
|
int b = (a1 * ( p1 & 0xff) + a2 * ( p2 & 0xff)) >> 8;
|
|
|
|
return 0xff000000 | (r << 8) | g | b;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
// MATH
|
|
|
|
// these are *only* the functions used internally
|
|
// the real math functions are inside PApplet
|
|
|
|
// these have been made private so as not to conflict
|
|
// with the versions found in PApplet when fxn importing happens
|
|
// also might be faster that way. hmm.
|
|
|
|
|
|
public void angleMode(int mode) {
|
|
angleMode = mode;
|
|
}
|
|
|
|
private final float mag(float a, float b) {
|
|
return (float)Math.sqrt(a*a + b*b);
|
|
}
|
|
|
|
private final float mag(float a, float b, float c) {
|
|
return (float)Math.sqrt(a*a + b*b + c*c);
|
|
}
|
|
|
|
private final float max(float a, float b) {
|
|
return (a > b) ? a : b;
|
|
}
|
|
|
|
private final float max(float a, float b, float c) {
|
|
return Math.max(a, Math.max(b, c));
|
|
}
|
|
|
|
private final float sq(float a) {
|
|
return a*a;
|
|
}
|
|
|
|
private final float sqrt(float a) {
|
|
return (float)Math.sqrt(a);
|
|
}
|
|
|
|
private final float abs(float a) {
|
|
return (a < 0) ? -a : a;
|
|
}
|
|
|
|
private final float sin(float angle) {
|
|
if (angleMode == DEGREES) angle *= DEG_TO_RAD;
|
|
return (float)Math.sin(angle);
|
|
}
|
|
|
|
private final float cos(float angle) {
|
|
if (angleMode == DEGREES) angle *= DEG_TO_RAD;
|
|
return (float)Math.cos(angle);
|
|
}
|
|
|
|
private final float tan(float angle) {
|
|
if (angleMode == DEGREES) angle *= DEG_TO_RAD;
|
|
return (float)Math.tan(angle);
|
|
}
|
|
}
|
|
|